1488 lines
58 KiB
Python
1488 lines
58 KiB
Python
#
|
|
# kickstart.py: kickstart install support
|
|
#
|
|
# Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007
|
|
# 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/>.
|
|
#
|
|
|
|
from storage.deviceaction import *
|
|
from storage.devices import LUKSDevice
|
|
from storage.devicelibs.lvm import getPossiblePhysicalExtents
|
|
from storage.formats import getFormat
|
|
from storage.partitioning import clearPartitions
|
|
from storage.partitioning import shouldClear
|
|
import storage.iscsi
|
|
import storage.fcoe
|
|
import storage.zfcp
|
|
|
|
from errors import *
|
|
import iutil
|
|
import isys
|
|
import os
|
|
import os.path
|
|
import tempfile
|
|
from flags import flags
|
|
from constants import *
|
|
import sys
|
|
import string
|
|
import urlgrabber
|
|
import warnings
|
|
import network
|
|
import upgrade
|
|
import pykickstart.commands as commands
|
|
from storage.devices import *
|
|
from scdate.core import zonetab
|
|
from pykickstart.base import KickstartCommand, BaseData
|
|
from pykickstart.constants import *
|
|
from pykickstart.errors import *
|
|
from pykickstart.parser import *
|
|
from pykickstart.version import *
|
|
|
|
import gettext
|
|
_ = lambda x: gettext.ldgettext("anaconda", x)
|
|
|
|
import logging
|
|
log = logging.getLogger("anaconda")
|
|
stderrLog = logging.getLogger("anaconda.stderr")
|
|
from anaconda_log import logger, logLevelMap, setHandlersLevel,\
|
|
DEFAULT_TTY_LEVEL
|
|
|
|
class AnacondaKSScript(Script):
|
|
def run(self, chroot, serial, intf = None):
|
|
if self.inChroot:
|
|
scriptRoot = chroot
|
|
else:
|
|
scriptRoot = "/"
|
|
|
|
(fd, path) = tempfile.mkstemp("", "ks-script-", scriptRoot + "/tmp")
|
|
|
|
os.write(fd, self.script)
|
|
os.close(fd)
|
|
os.chmod(path, 0700)
|
|
|
|
# Always log stdout/stderr from scripts. Using --logfile just lets you
|
|
# pick where it goes. The script will also be logged to program.log
|
|
# because of execWithRedirect, and to anaconda.log if the script fails.
|
|
if self.logfile:
|
|
if self.inChroot:
|
|
messages = "%s/%s" % (scriptRoot, self.logfile)
|
|
else:
|
|
messages = self.logfile
|
|
|
|
d = os.path.basename(messages)
|
|
if not os.path.exists(d):
|
|
os.makedirs(d)
|
|
else:
|
|
messages = "%s.log" % path
|
|
|
|
if intf:
|
|
intf.suspend()
|
|
rc = iutil.execWithRedirect(self.interp, ["/tmp/%s" % os.path.basename(path)],
|
|
stdin = messages, stdout = messages, stderr = messages,
|
|
root = scriptRoot)
|
|
if intf:
|
|
intf.resume()
|
|
|
|
# Always log an error. Only fail if we have a handle on the
|
|
# windowing system and the kickstart file included --erroronfail.
|
|
if rc != 0:
|
|
log.error("Error code %s running the kickstart script at line %s" % (rc, self.lineno))
|
|
|
|
try:
|
|
f = open(messages, "r")
|
|
err = f.readlines()
|
|
f.close()
|
|
for l in err:
|
|
log.error("\t%s" % l)
|
|
except:
|
|
err = None
|
|
|
|
if self.errorOnFail:
|
|
if intf != None:
|
|
msg = _("There was an error running the kickstart "
|
|
"script at line %(lineno)s. You may examine the "
|
|
"output in %(msgs)s. This is a fatal error and "
|
|
"installation will be aborted. Press the "
|
|
"OK button to exit the installer.") \
|
|
% {'lineno': self.lineno, 'msgs': messages}
|
|
|
|
if err:
|
|
intf.detailedMessageWindow(_("Scriptlet Failure"), msg, err)
|
|
else:
|
|
intf.messageWindow(_("Scriptlet Failure"), msg)
|
|
|
|
sys.exit(0)
|
|
|
|
if serial or self.logfile is not None:
|
|
os.chmod("%s" % messages, 0600)
|
|
|
|
class AnacondaKSPackages(Packages):
|
|
def __init__(self):
|
|
Packages.__init__(self)
|
|
|
|
# Has the %packages section been seen at all?
|
|
self.seen = False
|
|
|
|
|
|
def getEscrowCertificate(anaconda, url):
|
|
if not url:
|
|
return None
|
|
|
|
if url in anaconda.storage.escrowCertificates:
|
|
return anaconda.storage.escrowCertificates[url]
|
|
|
|
needs_net = not url.startswith("/") and not url.startswith("file:")
|
|
if needs_net and not network.hasActiveNetDev():
|
|
if not anaconda.intf.enableNetwork(anaconda):
|
|
anaconda.intf.messageWindow(_("No Network Available"),
|
|
_("Encryption key escrow requires "
|
|
"networking, but there was an error "
|
|
"enabling the network on your "
|
|
"system."), type="custom",
|
|
custom_icon="error",
|
|
custom_buttons=[_("_Exit installer")])
|
|
sys.exit(1)
|
|
|
|
log.info("escrow: downloading %s" % (url,))
|
|
f = urlgrabber.urlopen(url)
|
|
try:
|
|
anaconda.storage.escrowCertificates[url] = f.read()
|
|
finally:
|
|
f.close()
|
|
return anaconda.storage.escrowCertificates[url]
|
|
|
|
def deviceMatches(spec):
|
|
if not spec.startswith("/dev/"):
|
|
spec = os.path.normpath("/dev/" + spec)
|
|
|
|
matches = udev_resolve_glob(spec)
|
|
dev = udev_resolve_devspec(spec)
|
|
|
|
# udev_resolve_devspec returns None if there's no match, but we don't
|
|
# want that ending up in the list.
|
|
if dev and dev not in matches:
|
|
matches.append(dev)
|
|
|
|
return matches
|
|
|
|
# Remove any existing formatting on a device, but do not remove the partition
|
|
# itself. This sets up an existing device to be used in a --onpart option.
|
|
def removeExistingFormat(device, storage):
|
|
deps = storage.deviceDeps(device)
|
|
while deps:
|
|
leaves = [d for d in deps if d.isleaf]
|
|
for leaf in leaves:
|
|
storage.destroyDevice(leaf)
|
|
deps.remove(leaf)
|
|
|
|
storage.devicetree.registerAction(ActionDestroyFormat(device))
|
|
|
|
###
|
|
### SUBCLASSES OF PYKICKSTART COMMAND HANDLERS
|
|
###
|
|
|
|
class Authconfig(commands.authconfig.FC3_Authconfig):
|
|
def execute(self, anaconda):
|
|
anaconda.security.auth = self.authconfig
|
|
|
|
class AutoPart(commands.autopart.F12_AutoPart):
|
|
def execute(self, anaconda):
|
|
# sets up default autopartitioning. use clearpart separately
|
|
# if you want it
|
|
anaconda.instClass.setDefaultPartitioning(anaconda.storage, anaconda.platform)
|
|
anaconda.storage.doAutoPart = True
|
|
|
|
if self.encrypted:
|
|
anaconda.storage.encryptedAutoPart = True
|
|
anaconda.storage.encryptionPassphrase = self.passphrase
|
|
anaconda.storage.autoPartEscrowCert = \
|
|
getEscrowCertificate(anaconda, self.escrowcert)
|
|
anaconda.storage.autoPartAddBackupPassphrase = \
|
|
self.backuppassphrase
|
|
|
|
anaconda.ksdata.skipSteps.extend(["partition", "zfcpconfig", "parttype"])
|
|
|
|
class AutoStep(commands.autostep.FC3_AutoStep):
|
|
def execute(self, anaconda):
|
|
flags.autostep = 1
|
|
flags.autoscreenshot = self.autoscreenshot
|
|
|
|
class Bootloader(commands.bootloader.F12_Bootloader):
|
|
def execute(self, anaconda):
|
|
if self.location == "none":
|
|
location = None
|
|
elif self.location == "partition":
|
|
location = "boot"
|
|
else:
|
|
location = self.location
|
|
|
|
if self.upgrade and not anaconda.upgrade:
|
|
raise KickstartValueError, formatErrorMsg(self.lineno, msg="Selected upgrade mode for bootloader but not doing an upgrade")
|
|
|
|
if self.upgrade:
|
|
anaconda.bootloader.kickstart = 1
|
|
anaconda.bootloader.doUpgradeOnly = 1
|
|
|
|
if location is None:
|
|
anaconda.ksdata.permanentSkipSteps.extend(["bootloadersetup", "instbootloader"])
|
|
else:
|
|
anaconda.ksdata.showSteps.append("bootloader")
|
|
|
|
if self.appendLine:
|
|
anaconda.bootloader.args.append(self.appendLine)
|
|
|
|
if self.password:
|
|
anaconda.bootloader.setPassword(self.password, isCrypted = 0)
|
|
|
|
if self.md5pass:
|
|
anaconda.bootloader.setPassword(self.md5pass)
|
|
|
|
if location != None:
|
|
anaconda.bootloader.defaultDevice = location
|
|
else:
|
|
anaconda.bootloader.defaultDevice = -1
|
|
|
|
if self.timeout:
|
|
anaconda.bootloader.timeout = self.timeout
|
|
|
|
# add unpartitioned devices that will get partitioned into
|
|
# bootloader.drivelist
|
|
disks = anaconda.storage.disks
|
|
partitioned = anaconda.storage.partitioned
|
|
for disk in [d for d in disks if not d.partitioned]:
|
|
if shouldClear(disk, anaconda.storage.clearPartType,
|
|
anaconda.storage.clearPartDisks):
|
|
# add newly partitioned disks to the drivelist
|
|
anaconda.bootloader.drivelist.append(disk.name)
|
|
elif disk.name in anaconda.bootloader.drivelist:
|
|
# remove unpartitioned disks from the drivelist
|
|
anaconda.bootloader.drivelist.remove(disk.name)
|
|
anaconda.bootloader.drivelist.sort(
|
|
cmp=anaconda.storage.compareDisks)
|
|
|
|
# Throw out drives specified that don't exist.
|
|
if self.driveorder and len(self.driveorder) > 0:
|
|
new = []
|
|
for drive in self.driveorder:
|
|
if drive in anaconda.bootloader.drivelist:
|
|
new.append(drive)
|
|
else:
|
|
log.warning("requested drive %s in boot drive order "
|
|
"doesn't exist" %(drive,))
|
|
|
|
anaconda.bootloader.updateDriveList(new)
|
|
|
|
anaconda.ksdata.permanentSkipSteps.extend(["upgbootloader", "bootloader"])
|
|
|
|
class ClearPart(commands.clearpart.FC3_ClearPart):
|
|
def parse(self, args):
|
|
retval = commands.clearpart.FC3_ClearPart.parse(self, args)
|
|
|
|
if self.type is None:
|
|
self.type = CLEARPART_TYPE_NONE
|
|
|
|
# Do any glob expansion now, since we need to have the real list of
|
|
# disks available before the execute methods run.
|
|
drives = []
|
|
for spec in self.drives:
|
|
matched = deviceMatches(spec)
|
|
if matched:
|
|
drives.extend(matched)
|
|
else:
|
|
raise KickstartValueError, formatErrorMsg(self.lineno, msg="Specified nonexistent disk %s in clearpart command" % spec)
|
|
|
|
self.drives = drives
|
|
|
|
return retval
|
|
|
|
def execute(self, anaconda):
|
|
anaconda.storage.clearPartType = self.type
|
|
anaconda.storage.clearPartDisks = self.drives
|
|
if self.initAll:
|
|
anaconda.storage.reinitializeDisks = self.initAll
|
|
|
|
clearPartitions(anaconda.storage)
|
|
anaconda.ksdata.skipSteps.append("cleardiskssel")
|
|
|
|
class Fcoe(commands.fcoe.F13_Fcoe):
|
|
def parse(self, args):
|
|
fc = commands.fcoe.F13_Fcoe.parse(self, args)
|
|
|
|
if fc.nic not in isys.getDeviceProperties():
|
|
raise KickstartValueError, formatErrorMsg(self.lineno, msg="Specified nonexistent nic %s in fcoe command" % fc.nic)
|
|
|
|
storage.fcoe.fcoe().addSan(nic=fc.nic, dcb=fc.dcb)
|
|
|
|
return fc
|
|
|
|
class Firewall(commands.firewall.F10_Firewall):
|
|
def execute(self, anaconda):
|
|
anaconda.firewall.enabled = self.enabled
|
|
anaconda.firewall.trustdevs = self.trusts
|
|
|
|
for port in self.ports:
|
|
anaconda.firewall.portlist.append (port)
|
|
|
|
for svc in self.services:
|
|
anaconda.firewall.servicelist.append (svc)
|
|
|
|
class IgnoreDisk(commands.ignoredisk.F8_IgnoreDisk):
|
|
def parse(self, args):
|
|
retval = commands.ignoredisk.F8_IgnoreDisk.parse(self, args)
|
|
|
|
# See comment in ClearPart.parse
|
|
drives = []
|
|
for spec in self.ignoredisk:
|
|
matched = deviceMatches(spec)
|
|
if matched:
|
|
drives.extend(matched)
|
|
else:
|
|
raise KickstartValueError, formatErrorMsg(self.lineno, msg="Specified nonexistent disk %s in ignoredisk command" % spec)
|
|
|
|
self.ignoredisk = drives
|
|
|
|
drives = []
|
|
for spec in self.onlyuse:
|
|
matched = deviceMatches(spec)
|
|
if matched:
|
|
drives.extend(matched)
|
|
else:
|
|
raise KickstartValueError, formatErrorMsg(self.lineno, msg="Specified nonexistent disk %s in ignoredisk command" % spec)
|
|
|
|
self.onlyuse = drives
|
|
|
|
return retval
|
|
|
|
def execute(self, anaconda):
|
|
anaconda.storage.ignoredDisks = self.ignoredisk
|
|
anaconda.storage.exclusiveDisks = self.onlyuse
|
|
anaconda.ksdata.skipSteps.extend(["filter", "filtertype"])
|
|
|
|
class Iscsi(commands.iscsi.F10_Iscsi):
|
|
def parse(self, args):
|
|
tg = commands.iscsi.F10_Iscsi.parse(self, args)
|
|
|
|
try:
|
|
storage.iscsi.iscsi().addTarget(tg.ipaddr, tg.port,
|
|
tg.user, tg.password, tg.user_in, tg.password_in)
|
|
log.info("added iscsi target: %s" %(tg.ipaddr,))
|
|
except (IOError, ValueError), e:
|
|
raise KickstartValueError, formatErrorMsg(self.lineno,
|
|
msg=str(e))
|
|
return tg
|
|
|
|
class IscsiName(commands.iscsiname.FC6_IscsiName):
|
|
def parse(self, args):
|
|
retval = commands.iscsiname.FC6_IscsiName.parse(self, args)
|
|
|
|
storage.iscsi.iscsi().initiator = self.iscsiname
|
|
return retval
|
|
|
|
class Keyboard(commands.keyboard.FC3_Keyboard):
|
|
def execute(self, anaconda):
|
|
anaconda.keyboard.set(self.keyboard)
|
|
anaconda.keyboard.beenset = 1
|
|
anaconda.ksdata.skipSteps.append("keyboard")
|
|
|
|
class Lang(commands.lang.FC3_Lang):
|
|
def execute(self, anaconda):
|
|
anaconda.instLanguage.instLang = self.lang
|
|
anaconda.instLanguage.systemLang = self.lang
|
|
anaconda.ksdata.skipSteps.append("language")
|
|
|
|
class LogVolData(commands.logvol.F12_LogVolData):
|
|
def execute(self, anaconda):
|
|
storage = anaconda.storage
|
|
devicetree = storage.devicetree
|
|
|
|
storage.doAutoPart = False
|
|
|
|
if self.mountpoint == "swap":
|
|
type = "swap"
|
|
self.mountpoint = ""
|
|
if self.recommended:
|
|
(self.size, self.maxSizeMB) = iutil.swapSuggestion()
|
|
self.grow = True
|
|
else:
|
|
if self.fstype != "":
|
|
type = self.fstype
|
|
else:
|
|
type = storage.defaultFSType
|
|
|
|
# Sanity check mountpoint
|
|
if self.mountpoint != "" and self.mountpoint[0] != '/':
|
|
raise KickstartValueError, formatErrorMsg(self.lineno, msg="The mount point \"%s\" is not valid." % (self.mountpoint,))
|
|
|
|
# Check that the VG this LV is a member of has already been specified.
|
|
vg = devicetree.getDeviceByName(self.vgname)
|
|
if not vg:
|
|
raise KickstartValueError, formatErrorMsg(self.lineno, msg="No volume group exists with the name \"%s\". Specify volume groups before logical volumes." % self.vgname)
|
|
|
|
# If this specifies an existing request that we should not format,
|
|
# quit here after setting up enough information to mount it later.
|
|
if not self.format:
|
|
if not self.name:
|
|
raise KickstartValueError, formatErrorMsg(self.lineno, msg="--noformat used without --name")
|
|
|
|
dev = devicetree.getDeviceByName("%s-%s" % (vg.name, self.name))
|
|
if not dev:
|
|
raise KickstartValueError, formatErrorMsg(self.lineno, msg="No preexisting logical volume with the name \"%s\" was found." % self.name)
|
|
|
|
dev.format.mountpoint = self.mountpoint
|
|
dev.format.mountopts = self.fsopts
|
|
anaconda.ksdata.skipSteps.extend(["partition", "zfcpconfig", "parttype"])
|
|
return
|
|
|
|
# Make sure this LV name is not already used in the requested VG.
|
|
if not self.preexist:
|
|
tmp = devicetree.getDeviceByName("%s-%s" % (vg.name, self.name))
|
|
if tmp:
|
|
raise KickstartValueError, formatErrorMsg(self.lineno, msg="Logical volume name already used in volume group %s" % vg.name)
|
|
|
|
# Size specification checks
|
|
if not self.percent:
|
|
if not self.size:
|
|
raise KickstartValueError, formatErrorMsg(self.lineno, msg="Size required")
|
|
elif not self.grow and self.size*1024 < vg.peSize:
|
|
raise KickstartValueError, formatErrorMsg(self.lineno, msg="Logical volume size must be larger than the volume group physical extent size.")
|
|
elif self.percent <= 0 or self.percent > 100:
|
|
raise KickstartValueError, formatErrorMsg(self.lineno, msg="Percentage must be between 0 and 100")
|
|
|
|
# Now get a format to hold a lot of these extra values.
|
|
format = getFormat(type,
|
|
mountpoint=self.mountpoint,
|
|
mountopts=self.fsopts)
|
|
if not format:
|
|
raise KickstartValueError, formatErrorMsg(self.lineno, msg="The \"%s\" filesystem type is not supported." % type)
|
|
|
|
# If we were given a pre-existing LV to create a filesystem on, we need
|
|
# to verify it and its VG exists and then schedule a new format action
|
|
# to take place there. Also, we only support a subset of all the
|
|
# options on pre-existing LVs.
|
|
if self.preexist:
|
|
device = devicetree.getDeviceByName("%s-%s" % (vg.name, self.name))
|
|
if not device:
|
|
raise KickstartValueError, formatErrorMsg(self.lineno, msg="Specified nonexistent LV %s in logvol command" % self.name)
|
|
|
|
removeExistingFormat(device, storage)
|
|
devicetree.registerAction(ActionCreateFormat(device, format))
|
|
else:
|
|
# If a previous device has claimed this mount point, delete the
|
|
# old one.
|
|
try:
|
|
if self.mountpoint:
|
|
device = storage.mountpoints[self.mountpoint]
|
|
storage.destroyDevice(device)
|
|
except KeyError:
|
|
pass
|
|
|
|
request = storage.newLV(format=format,
|
|
name=self.name,
|
|
vg=vg,
|
|
size=self.size,
|
|
grow=self.grow,
|
|
maxsize=self.maxSizeMB,
|
|
percent=self.percent)
|
|
|
|
if self.fsprofile and hasattr(request.format, "fsprofile"):
|
|
request.format.fsprofile = self.fsprofile
|
|
|
|
storage.createDevice(request)
|
|
|
|
if self.encrypted:
|
|
if self.passphrase and not storage.encryptionPassphrase:
|
|
storage.encryptionPassphrase = self.passphrase
|
|
|
|
cert = getEscrowCertificate(anaconda, self.escrowcert)
|
|
if self.preexist:
|
|
luksformat = format
|
|
device.format = getFormat("luks", passphrase=self.passphrase, device=device.path,
|
|
escrow_cert=cert,
|
|
add_backup_passphrase=self.backuppassphrase)
|
|
luksdev = LUKSDevice("luks%d" % storage.nextID,
|
|
format=luksformat,
|
|
parents=device)
|
|
else:
|
|
luksformat = request.format
|
|
request.format = getFormat("luks", passphrase=self.passphrase,
|
|
escrow_cert=cert,
|
|
add_backup_passphrase=self.backuppassphrase)
|
|
luksdev = LUKSDevice("luks%d" % storage.nextID,
|
|
format=luksformat,
|
|
parents=request)
|
|
storage.createDevice(luksdev)
|
|
|
|
anaconda.ksdata.skipSteps.extend(["partition", "zfcpconfig", "parttype"])
|
|
|
|
class Logging(commands.logging.FC6_Logging):
|
|
def execute(self, anaconda):
|
|
if logger.tty_loglevel == DEFAULT_TTY_LEVEL:
|
|
# not set from the command line
|
|
level = logLevelMap[self.level]
|
|
logger.tty_loglevel = level
|
|
storage_log = logging.getLogger("storage")
|
|
setHandlersLevel(log, level)
|
|
setHandlersLevel(storage_log, level)
|
|
|
|
if logger.remote_syslog == None and len(self.host) > 0:
|
|
# not set from the command line, ok to use kickstart
|
|
remote_server = self.host
|
|
if self.port:
|
|
remote_server = "%s:%s" %(self.host, self.port)
|
|
logger.updateRemote(remote_server)
|
|
|
|
class NetworkData(commands.network.F8_NetworkData):
|
|
def execute(self, anaconda):
|
|
if self.bootProto:
|
|
devices = anaconda.network.netdevices
|
|
if (devices and self.bootProto):
|
|
if not self.device:
|
|
list = devices.keys ()
|
|
list.sort()
|
|
device = list[0]
|
|
else:
|
|
device = self.device
|
|
|
|
try:
|
|
dev = devices[device]
|
|
except KeyError:
|
|
raise KickstartValueError, formatErrorMsg(self.lineno, msg="The provided network interface %s does not exist" % device)
|
|
|
|
dev.set (("bootproto", self.bootProto))
|
|
dev.set (("dhcpclass", self.dhcpclass))
|
|
|
|
if self.onboot:
|
|
dev.set (("onboot", "yes"))
|
|
else:
|
|
dev.set (("onboot", "no"))
|
|
|
|
if self.bootProto == "static":
|
|
if (self.ip):
|
|
dev.set (("ipaddr", self.ip))
|
|
if (self.netmask):
|
|
dev.set (("netmask", self.netmask))
|
|
|
|
if self.ethtool:
|
|
dev.set (("ethtool_opts", self.ethtool))
|
|
|
|
if isys.isWireless(device):
|
|
if self.essid:
|
|
dev.set(("essid", self.essid))
|
|
if self.wepkey:
|
|
dev.set(("wepkey", self.wepkey))
|
|
|
|
if self.hostname != "":
|
|
anaconda.network.setHostname(self.hostname)
|
|
anaconda.network.overrideDHCPhostname = True
|
|
|
|
if self.nameserver != "":
|
|
anaconda.network.setDNS(self.nameserver, device)
|
|
|
|
if self.gateway != "":
|
|
anaconda.network.setGateway(self.gateway, device)
|
|
|
|
needs_net = (anaconda.methodstr and
|
|
(anaconda.methodstr.startswith("http:") or
|
|
anaconda.methodstr.startswith("ftp:") or
|
|
anaconda.methodstr.startswith("nfs:")))
|
|
if needs_net and not network.hasActiveNetDev():
|
|
log.info("Bringing up network in stage2 kickstart ...")
|
|
rc = anaconda.network.bringUp()
|
|
log.info("Network setup %s" % (rc and 'succeeded' or 'failed',))
|
|
|
|
class MultiPath(commands.multipath.FC6_MultiPath):
|
|
def parse(self, args):
|
|
raise NotImplementedError("The multipath kickstart command is not currently supported")
|
|
|
|
class DmRaid(commands.dmraid.FC6_DmRaid):
|
|
def parse(self, args):
|
|
raise NotImplementedError("The dmraid kickstart command is not currently supported")
|
|
|
|
class PartitionData(commands.partition.F12_PartData):
|
|
def execute(self, anaconda):
|
|
storage = anaconda.storage
|
|
devicetree = storage.devicetree
|
|
kwargs = {}
|
|
|
|
storage.doAutoPart = False
|
|
|
|
if self.onbiosdisk != "":
|
|
for (disk, biosdisk) in storage.eddDict.iteritems():
|
|
if str(biosdisk) == self.onbiosdisk:
|
|
self.disk = disk
|
|
break
|
|
|
|
if self.disk == "":
|
|
raise KickstartValueError, formatErrorMsg(self.lineno, msg="Specified BIOS disk %s cannot be determined" % self.onbiosdisk)
|
|
|
|
if self.mountpoint == "swap":
|
|
type = "swap"
|
|
self.mountpoint = ""
|
|
if self.recommended:
|
|
(self.size, self.maxSizeMB) = iutil.swapSuggestion()
|
|
self.grow = True
|
|
# if people want to specify no mountpoint for some reason, let them
|
|
# this is really needed for pSeries boot partitions :(
|
|
elif self.mountpoint == "None":
|
|
self.mountpoint = ""
|
|
if self.fstype:
|
|
type = self.fstype
|
|
else:
|
|
type = storage.defaultFSType
|
|
elif self.mountpoint == 'appleboot':
|
|
type = "Apple Bootstrap"
|
|
self.mountpoint = ""
|
|
kwargs["weight"] = anaconda.platform.weight(fstype="appleboot")
|
|
elif self.mountpoint == 'prepboot':
|
|
type = "PPC PReP Boot"
|
|
self.mountpoint = ""
|
|
kwargs["weight"] = anaconda.platform.weight(fstype="prepboot")
|
|
elif self.mountpoint.startswith("raid."):
|
|
type = "mdmember"
|
|
kwargs["name"] = self.mountpoint
|
|
|
|
if devicetree.getDeviceByName(kwargs["name"]):
|
|
raise KickstartValueError, formatErrorMsg(self.lineno, msg="RAID partition defined multiple times")
|
|
|
|
# store "raid." alias for other ks partitioning commands
|
|
if self.onPart:
|
|
anaconda.ksdata.onPart[kwargs["name"]] = self.onPart
|
|
self.mountpoint = ""
|
|
elif self.mountpoint.startswith("pv."):
|
|
type = "lvmpv"
|
|
kwargs["name"] = self.mountpoint
|
|
|
|
if devicetree.getDeviceByName(kwargs["name"]):
|
|
raise KickstartValueError, formatErrorMsg(self.lineno, msg="PV partition defined multiple times")
|
|
|
|
# store "pv." alias for other ks partitioning commands
|
|
if self.onPart:
|
|
anaconda.ksdata.onPart[kwargs["name"]] = self.onPart
|
|
self.mountpoint = ""
|
|
elif self.mountpoint == "/boot/efi":
|
|
type = "EFI System Partition"
|
|
self.fsopts = "defaults,uid=0,gid=0,umask=0077,shortname=winnt"
|
|
kwargs["weight"] = anaconda.platform.weight(fstype="efi")
|
|
else:
|
|
if self.fstype != "":
|
|
type = self.fstype
|
|
elif self.mountpoint == "/boot":
|
|
type = anaconda.platform.defaultBootFSType
|
|
else:
|
|
type = storage.defaultFSType
|
|
|
|
# If this specified an existing request that we should not format,
|
|
# quit here after setting up enough information to mount it later.
|
|
if not self.format:
|
|
if not self.onPart:
|
|
raise KickstartValueError, formatErrorMsg(self.lineno, msg="--noformat used without --onpart")
|
|
|
|
dev = devicetree.getDeviceByName(udev_resolve_devspec(self.onPart))
|
|
if not dev:
|
|
raise KickstartValueError, formatErrorMsg(self.lineno, msg="No preexisting partition with the name \"%s\" was found." % self.onPart)
|
|
|
|
dev.format.mountpoint = self.mountpoint
|
|
dev.format.mountopts = self.fsopts
|
|
anaconda.ksdata.skipSteps.extend(["partition", "zfcpconfig", "parttype"])
|
|
return
|
|
|
|
# Size specification checks.
|
|
if not self.size and not self.onPart:
|
|
raise KickstartValueError, formatErrorMsg(self.lineno, msg="Partition requires a size specification")
|
|
|
|
# Now get a format to hold a lot of these extra values.
|
|
kwargs["format"] = getFormat(type,
|
|
mountpoint=self.mountpoint,
|
|
label=self.label,
|
|
mountopts=self.fsopts)
|
|
if not kwargs["format"]:
|
|
raise KickstartValueError, formatErrorMsg(self.lineno, msg="The \"%s\" filesystem type is not supported." % type)
|
|
|
|
# If we were given a specific disk to create the partition on, verify
|
|
# that it exists first. If it doesn't exist, see if it exists with
|
|
# mapper/ on the front. If that doesn't exist either, it's an error.
|
|
if self.disk:
|
|
names = [self.disk, "mapper/" + self.disk]
|
|
for n in names:
|
|
disk = devicetree.getDeviceByName(udev_resolve_devspec(n))
|
|
if not disk:
|
|
raise KickstartValueError, formatErrorMsg(self.lineno, msg="Specified nonexistent disk %s in partition command" % n)
|
|
|
|
should_clear = shouldClear(disk,
|
|
storage.clearPartType,
|
|
storage.clearPartDisks)
|
|
if disk and (disk.partitioned or should_clear):
|
|
kwargs["disks"] = [disk]
|
|
break
|
|
elif disk:
|
|
raise KickstartValueError, formatErrorMsg(self.lineno, msg="Specified unpartitioned disk %s in partition command" % self.disk)
|
|
|
|
if not kwargs["disks"]:
|
|
raise KickstartValueError, formatErrorMsg(self.lineno, msg="Specified nonexistent disk %s in partition command" % self.disk)
|
|
|
|
kwargs["grow"] = self.grow
|
|
kwargs["size"] = self.size
|
|
kwargs["maxsize"] = self.maxSizeMB
|
|
kwargs["primary"] = self.primOnly
|
|
|
|
# If we were given a pre-existing partition to create a filesystem on,
|
|
# we need to verify it exists and then schedule a new format action to
|
|
# take place there. Also, we only support a subset of all the options
|
|
# on pre-existing partitions.
|
|
if self.onPart:
|
|
device = devicetree.getDeviceByName(udev_resolve_devspec(self.onPart))
|
|
if not device:
|
|
raise KickstartValueError, formatErrorMsg(self.lineno, msg="Specified nonexistent partition %s in partition command" % self.onPart)
|
|
|
|
removeExistingFormat(device, storage)
|
|
devicetree.registerAction(ActionCreateFormat(device, kwargs["format"]))
|
|
else:
|
|
# If a previous device has claimed this mount point, delete the
|
|
# old one.
|
|
try:
|
|
if self.mountpoint:
|
|
device = storage.mountpoints[self.mountpoint]
|
|
storage.destroyDevice(device)
|
|
except KeyError:
|
|
pass
|
|
|
|
request = storage.newPartition(**kwargs)
|
|
|
|
if self.fsprofile and hasattr(request.format, "fsprofile"):
|
|
request.format.fsprofile = self.fsprofile
|
|
|
|
storage.createDevice(request)
|
|
|
|
if self.encrypted:
|
|
if self.passphrase and not storage.encryptionPassphrase:
|
|
storage.encryptionPassphrase = self.passphrase
|
|
|
|
cert = getEscrowCertificate(anaconda, self.escrowcert)
|
|
if self.onPart:
|
|
luksformat = format
|
|
device.format = getFormat("luks", passphrase=self.passphrase, device=device.path,
|
|
escrow_cert=cert,
|
|
add_backup_passphrase=self.backuppassphrase)
|
|
luksdev = LUKSDevice("luks%d" % storage.nextID,
|
|
format=luksformat,
|
|
parents=device)
|
|
else:
|
|
luksformat = request.format
|
|
request.format = getFormat("luks", passphrase=self.passphrase,
|
|
escrow_cert=cert,
|
|
add_backup_passphrase=self.backuppassphrase)
|
|
luksdev = LUKSDevice("luks%d" % storage.nextID,
|
|
format=luksformat,
|
|
parents=request)
|
|
storage.createDevice(luksdev)
|
|
|
|
anaconda.ksdata.skipSteps.extend(["partition", "zfcpconfig", "parttype"])
|
|
|
|
class Reboot(commands.reboot.FC6_Reboot):
|
|
def execute(self, anaconda):
|
|
anaconda.ksdata.skipSteps.append("complete")
|
|
|
|
class RaidData(commands.raid.F12_RaidData):
|
|
def execute(self, anaconda):
|
|
raidmems = []
|
|
devicename = "md%d" % self.device
|
|
|
|
storage = anaconda.storage
|
|
devicetree = storage.devicetree
|
|
kwargs = {}
|
|
|
|
storage.doAutoPart = False
|
|
|
|
if self.mountpoint == "swap":
|
|
type = "swap"
|
|
self.mountpoint = ""
|
|
elif self.mountpoint.startswith("pv."):
|
|
type = "lvmpv"
|
|
kwargs["name"] = self.mountpoint
|
|
anaconda.ksdata.onPart[kwargs["name"]] = devicename
|
|
|
|
if devicetree.getDeviceByName(kwargs["name"]):
|
|
raise KickstartValueError, formatErrorMsg(self.lineno, msg="PV partition defined multiple times")
|
|
|
|
self.mountpoint = ""
|
|
else:
|
|
if self.fstype != "":
|
|
type = self.fstype
|
|
elif self.mountpoint == "/boot" and anaconda.platform.supportsMdRaidBoot:
|
|
type = anaconda.platform.defaultBootFSType
|
|
else:
|
|
type = storage.defaultFSType
|
|
|
|
# Sanity check mountpoint
|
|
if self.mountpoint != "" and self.mountpoint[0] != '/':
|
|
raise KickstartValueError, formatErrorMsg(self.lineno, msg="The mount point is not valid.")
|
|
|
|
# If this specifies an existing request that we should not format,
|
|
# quit here after setting up enough information to mount it later.
|
|
if not self.format:
|
|
if not devicename:
|
|
raise KickstartValueError, formatErrorMsg(self.lineno, msg="--noformat used without --device")
|
|
|
|
dev = devicetree.getDeviceByName(devicename)
|
|
if not dev:
|
|
raise KickstartValueError, formatErrorMsg(self.lineno, msg="No preexisting RAID device with the name \"%s\" was found." % devicename)
|
|
|
|
dev.format.mountpoint = self.mountpoint
|
|
dev.format.mountopts = self.fsopts
|
|
anaconda.ksdata.skipSteps.extend(["partition", "zfcpconfig", "parttype"])
|
|
return
|
|
|
|
# Get a list of all the RAID members.
|
|
for member in self.members:
|
|
# if member is using --onpart, use original device
|
|
member = anaconda.ksdata.onPart.get(member, member)
|
|
dev = devicetree.getDeviceByName(member)
|
|
if dev and dev.format.type == "luks":
|
|
try:
|
|
dev = devicetree.getChildren(dev)[0]
|
|
except IndexError:
|
|
dev = None
|
|
if not dev:
|
|
raise KickstartValueError, formatErrorMsg(self.lineno, msg="Tried to use undefined partition %s in RAID specification" % member)
|
|
|
|
raidmems.append(dev)
|
|
|
|
if not self.preexist:
|
|
if len(raidmems) == 0:
|
|
raise KickstartValueError, formatErrorMsg(self.lineno, msg="RAID Partition defined without any RAID members")
|
|
|
|
if self.level == "":
|
|
raise KickstartValueError, formatErrorMsg(self.lineno, msg="RAID Partition defined without RAID level")
|
|
|
|
# Now get a format to hold a lot of these extra values.
|
|
kwargs["format"] = getFormat(type,
|
|
mountpoint=self.mountpoint,
|
|
mountopts=self.fsopts)
|
|
if not kwargs["format"]:
|
|
raise KickstartValueError, formatErrorMsg(self.lineno, msg="The \"%s\" filesystem type is not supported." % type)
|
|
|
|
kwargs["name"] = devicename
|
|
kwargs["level"] = self.level
|
|
kwargs["parents"] = raidmems
|
|
kwargs["memberDevices"] = len(raidmems)
|
|
kwargs["totalDevices"] = kwargs["memberDevices"]+self.spares
|
|
|
|
# If we were given a pre-existing RAID to create a filesystem on,
|
|
# we need to verify it exists and then schedule a new format action
|
|
# to take place there. Also, we only support a subset of all the
|
|
# options on pre-existing RAIDs.
|
|
if self.preexist:
|
|
device = devicetree.getDeviceByName(devicename)
|
|
if not device:
|
|
raise KickstartValueError, formatErrorMsg(self.lineno, msg="Specifeid nonexistent RAID %s in raid command" % devicename)
|
|
|
|
removeExistingFormat(device, storage)
|
|
devicetree.registerAction(ActionCreateFormat(device, kwargs["format"]))
|
|
else:
|
|
# If a previous device has claimed this mount point, delete the
|
|
# old one.
|
|
try:
|
|
if self.mountpoint:
|
|
device = storage.mountpoints[self.mountpoint]
|
|
storage.destroyDevice(device)
|
|
except KeyError:
|
|
pass
|
|
|
|
try:
|
|
request = storage.newMDArray(**kwargs)
|
|
except ValueError, e:
|
|
raise KickstartValueError, formatErrorMsg(self.lineno, msg=str(e))
|
|
|
|
if self.fsprofile and hasattr(request.format, "fsprofile"):
|
|
request.format.fsprofile = self.fsprofile
|
|
|
|
storage.createDevice(request)
|
|
|
|
if self.encrypted:
|
|
if self.passphrase and not storage.encryptionPassphrase:
|
|
storage.encryptionPassphrase = self.passphrase
|
|
|
|
cert = getEscrowCertificate(anaconda, self.escrowcert)
|
|
if self.preexist:
|
|
luksformat = format
|
|
device.format = getFormat("luks", passphrase=self.passphrase, device=device.path,
|
|
escrow_cert=cert,
|
|
add_backup_passphrase=self.backuppassphrase)
|
|
luksdev = LUKSDevice("luks%d" % storage.nextID,
|
|
format=luksformat,
|
|
parents=device)
|
|
else:
|
|
luksformat = request.format
|
|
request.format = getFormat("luks", passphrase=self.passphrase,
|
|
escrow_cert=cert,
|
|
add_backup_passphrase=self.backuppassphrase)
|
|
luksdev = LUKSDevice("luks%d" % storage.nextID,
|
|
format=luksformat,
|
|
parents=request)
|
|
storage.createDevice(luksdev)
|
|
|
|
anaconda.ksdata.skipSteps.extend(["partition", "zfcpconfig", "parttype"])
|
|
|
|
class RootPw(commands.rootpw.F8_RootPw):
|
|
def execute(self, anaconda):
|
|
anaconda.users.rootPassword["password"] = self.password
|
|
anaconda.users.rootPassword["isCrypted"] = self.isCrypted
|
|
anaconda.users.rootPassword["lock"] = self.lock
|
|
anaconda.ksdata.skipSteps.append("accounts")
|
|
|
|
class SELinux(commands.selinux.FC3_SELinux):
|
|
def execute(self, anaconda):
|
|
anaconda.security.setSELinux(self.selinux)
|
|
|
|
class SkipX(commands.skipx.FC3_SkipX):
|
|
def execute(self, anaconda):
|
|
anaconda.ksdata.skipSteps.extend(["setsanex", "videocard", "xcustom"])
|
|
|
|
if anaconda.desktop is not None:
|
|
anaconda.desktop.setDefaultRunLevel(3)
|
|
|
|
class Timezone(commands.timezone.FC6_Timezone):
|
|
def execute(self, anaconda):
|
|
# check validity
|
|
tab = zonetab.ZoneTab()
|
|
if self.timezone not in (entry.tz.replace(' ','_') for entry in
|
|
tab.getEntries()):
|
|
log.warning("Timezone %s set in kickstart is not valid." % (self.timezone,))
|
|
|
|
anaconda.timezone.setTimezoneInfo(self.timezone, self.isUtc)
|
|
anaconda.ksdata.skipSteps.append("timezone")
|
|
|
|
class Upgrade(commands.upgrade.F11_Upgrade):
|
|
def execute(self, anaconda):
|
|
anaconda.upgrade = self.upgrade
|
|
|
|
class VolGroupData(commands.volgroup.FC3_VolGroupData):
|
|
def execute(self, anaconda):
|
|
pvs = []
|
|
|
|
storage = anaconda.storage
|
|
devicetree = storage.devicetree
|
|
|
|
storage.doAutoPart = False
|
|
|
|
# Get a list of all the physical volume devices that make up this VG.
|
|
for pv in self.physvols:
|
|
# if pv is using --onpart, use original device
|
|
pv = anaconda.ksdata.onPart.get(pv, pv)
|
|
dev = devicetree.getDeviceByName(pv)
|
|
if dev and dev.format.type == "luks":
|
|
try:
|
|
dev = devicetree.getChildren(dev)[0]
|
|
except IndexError:
|
|
dev = None
|
|
if not dev:
|
|
raise KickstartValueError, formatErrorMsg(self.lineno, msg="Tried to use undefined partition %s in Volume Group specification" % pv)
|
|
|
|
pvs.append(dev)
|
|
|
|
if len(pvs) == 0 and not self.preexist:
|
|
raise KickstartValueError, formatErrorMsg(self.lineno, msg="Volume group defined without any physical volumes. Either specify physical volumes or use --useexisting.")
|
|
|
|
if self.pesize not in getPossiblePhysicalExtents(floor=1024):
|
|
raise KickstartValueError, formatErrorMsg(self.lineno, msg="Volume group specified invalid pesize")
|
|
|
|
# If --noformat or --useexisting was given, there's really nothing to do.
|
|
if not self.format or self.preexist:
|
|
if not self.vgname:
|
|
raise KickstartValueError, formatErrorMsg(self.lineno, msg="--noformat or --useexisting used without giving a name")
|
|
|
|
dev = devicetree.getDeviceByName(self.vgname)
|
|
if not dev:
|
|
raise KickstartValueError, formatErrorMsg(self.lineno, msg="No preexisting VG with the name \"%s\" was found." % self.vgname)
|
|
else:
|
|
request = storage.newVG(pvs=pvs,
|
|
name=self.vgname,
|
|
peSize=self.pesize/1024.0)
|
|
|
|
storage.createDevice(request)
|
|
|
|
class XConfig(commands.xconfig.F10_XConfig):
|
|
def execute(self, anaconda):
|
|
if self.startX:
|
|
anaconda.desktop.setDefaultRunLevel(5)
|
|
|
|
if self.defaultdesktop:
|
|
anaconda.desktop.setDefaultDesktop(self.defaultdesktop)
|
|
|
|
class ZeroMbr(commands.zerombr.FC3_ZeroMbr):
|
|
def execute(self, anaconda):
|
|
anaconda.storage.zeroMbr = 1
|
|
|
|
class ZFCP(commands.zfcp.FC3_ZFCP):
|
|
def parse(self, args):
|
|
fcp = commands.zfcp.FC3_ZFCP.parse(self, args)
|
|
try:
|
|
storage.zfcp.ZFCP().addFCP(fcp.devnum, fcp.wwpn, fcp.fcplun)
|
|
except ValueError, e:
|
|
log.warning(str(e))
|
|
|
|
return fcp
|
|
|
|
###
|
|
### HANDLERS
|
|
###
|
|
|
|
# This is just the latest entry from pykickstart.handlers.control with all the
|
|
# classes we're overriding in place of the defaults.
|
|
commandMap = {
|
|
"auth": Authconfig,
|
|
"authconfig": Authconfig,
|
|
"autopart": AutoPart,
|
|
"autostep": AutoStep,
|
|
"bootloader": Bootloader,
|
|
"clearpart": ClearPart,
|
|
"dmraid": DmRaid,
|
|
"fcoe": Fcoe,
|
|
"firewall": Firewall,
|
|
"halt": Reboot,
|
|
"ignoredisk": IgnoreDisk,
|
|
"install": Upgrade,
|
|
"iscsi": Iscsi,
|
|
"iscsiname": IscsiName,
|
|
"keyboard": Keyboard,
|
|
"lang": Lang,
|
|
"logging": Logging,
|
|
"multipath": MultiPath,
|
|
"poweroff": Reboot,
|
|
"reboot": Reboot,
|
|
"rootpw": RootPw,
|
|
"selinux": SELinux,
|
|
"shutdown": Reboot,
|
|
"skipx": SkipX,
|
|
"timezone": Timezone,
|
|
"upgrade": Upgrade,
|
|
"xconfig": XConfig,
|
|
"zerombr": ZeroMbr,
|
|
"zfcp": ZFCP,
|
|
}
|
|
|
|
dataMap = {
|
|
"LogVolData": LogVolData,
|
|
"NetworkData": NetworkData,
|
|
"PartData": PartitionData,
|
|
"RaidData": RaidData,
|
|
"VolGroupData": VolGroupData,
|
|
}
|
|
|
|
superclass = returnClassForVersion()
|
|
|
|
class AnacondaKSHandler(superclass):
|
|
def __init__ (self, anaconda):
|
|
superclass.__init__(self, commandUpdates=commandMap, dataUpdates=dataMap)
|
|
self.packages = AnacondaKSPackages()
|
|
|
|
self.permanentSkipSteps = []
|
|
self.skipSteps = []
|
|
self.showSteps = []
|
|
self.anaconda = anaconda
|
|
self.id = self.anaconda.id
|
|
self.onPart = {}
|
|
|
|
# All the KickstartCommand and KickstartData objects that
|
|
# handleCommand returns, so we can later iterate over them and run
|
|
# the execute methods. These really should be stored in the order
|
|
# they're seen in the kickstart file.
|
|
self._dataObjs = []
|
|
|
|
def add(self, obj):
|
|
if isinstance(obj, KickstartCommand):
|
|
# Commands can only be run once, and the latest one seen takes
|
|
# precedence over any earlier ones.
|
|
i = 0
|
|
while i < len(self._dataObjs):
|
|
if self._dataObjs[i].__class__ == obj.__class__:
|
|
self._dataObjs.pop(i)
|
|
break
|
|
|
|
i += 1
|
|
|
|
self._dataObjs.append(obj)
|
|
else:
|
|
# Data objects can be seen over and over again.
|
|
self._dataObjs.append(obj)
|
|
|
|
def execute(self):
|
|
for obj in filter(lambda o: hasattr(o, "execute"), self._dataObjs):
|
|
obj.execute(self.anaconda)
|
|
|
|
class AnacondaPreParser(KickstartParser):
|
|
# A subclass of KickstartParser that only looks for %pre scripts and
|
|
# sets them up to be run. All other scripts and commands are ignored.
|
|
def __init__ (self, handler, followIncludes=True, errorsAreFatal=True,
|
|
missingIncludeIsFatal=True):
|
|
KickstartParser.__init__(self, handler, missingIncludeIsFatal=False)
|
|
|
|
def addScript (self):
|
|
if self._script["type"] != KS_SCRIPT_PRE:
|
|
return
|
|
|
|
s = AnacondaKSScript (self._script["body"], type=self._script["type"],
|
|
interp=self._script["interp"],
|
|
lineno=self._script["lineno"],
|
|
inChroot=self._script["chroot"],
|
|
logfile=self._script["log"],
|
|
errorOnFail=self._script["errorOnFail"])
|
|
self.handler.scripts.append(s)
|
|
|
|
def addPackages (self, line):
|
|
pass
|
|
|
|
def handleCommand (self, lineno, args):
|
|
pass
|
|
|
|
def handlePackageHdr (self, lineno, args):
|
|
pass
|
|
|
|
def handleScriptHdr (self, lineno, args):
|
|
if not args[0] == "%pre":
|
|
return
|
|
|
|
KickstartParser.handleScriptHdr(self, lineno, args)
|
|
|
|
class AnacondaKSParser(KickstartParser):
|
|
def __init__ (self, handler, followIncludes=True, errorsAreFatal=True,
|
|
missingIncludeIsFatal=True):
|
|
KickstartParser.__init__(self, handler)
|
|
|
|
def addScript (self):
|
|
if string.join(self._script["body"]).strip() == "":
|
|
return
|
|
|
|
s = AnacondaKSScript (self._script["body"], type=self._script["type"],
|
|
interp=self._script["interp"],
|
|
lineno=self._script["lineno"],
|
|
inChroot=self._script["chroot"],
|
|
logfile=self._script["log"],
|
|
errorOnFail=self._script["errorOnFail"])
|
|
self.handler.scripts.append(s)
|
|
|
|
def handlePackageHdr (self, lineno, args):
|
|
KickstartParser.handlePackageHdr (self, lineno, args)
|
|
self.handler.packages.seen = True
|
|
|
|
def handleCommand (self, lineno, args):
|
|
if not self.handler:
|
|
return
|
|
|
|
retval = KickstartParser.handleCommand(self, lineno, args)
|
|
self.handler.add(retval)
|
|
return retval
|
|
|
|
def preScriptPass(anaconda, file):
|
|
# The first pass through kickstart file processing - look for %pre scripts
|
|
# and run them. This must come in a separate pass in case a script
|
|
# generates an included file that has commands for later.
|
|
ksparser = AnacondaPreParser(AnacondaKSHandler(anaconda))
|
|
|
|
try:
|
|
ksparser.readKickstart(file)
|
|
except IOError, e:
|
|
if anaconda.intf:
|
|
anaconda.intf.kickstartErrorWindow("Could not open kickstart file or included file named %s" % e.filename)
|
|
sys.exit(1)
|
|
else:
|
|
stderrLog.critical(_("The following error was found while parsing the kickstart "
|
|
"configuration file:\n\n%s") % e)
|
|
sys.exit(1)
|
|
except KickstartError, e:
|
|
if anaconda.intf:
|
|
anaconda.intf.kickstartErrorWindow(e.__str__())
|
|
sys.exit(1)
|
|
else:
|
|
stderrLog.critical(_("The following error was found while parsing the kickstart "
|
|
"configuration file:\n\n%s") % e)
|
|
sys.exit(1)
|
|
|
|
# run %pre scripts
|
|
runPreScripts(anaconda, ksparser.handler.scripts)
|
|
|
|
def parseKickstart(anaconda, file):
|
|
try:
|
|
file = preprocessKickstart(file)
|
|
except KickstartError, msg:
|
|
stderrLog.critical(_("Error processing %%ksappend lines: %s") % msg)
|
|
sys.exit(1)
|
|
except Exception, e:
|
|
stderrLog.critical(_("Unknown error processing %%ksappend lines: %s") % e)
|
|
sys.exit(1)
|
|
|
|
handler = AnacondaKSHandler(anaconda)
|
|
ksparser = AnacondaKSParser(handler)
|
|
|
|
# We need this so all the /dev/disk/* stuff is set up before parsing.
|
|
udev_trigger(subsystem="block", action="change")
|
|
# So that drives onlined by these can be used in the ks file
|
|
storage.iscsi.iscsi().startup()
|
|
storage.fcoe.fcoe().startup()
|
|
storage.zfcp.ZFCP().startup()
|
|
# Note we do NOT call dasd.startup() here, that does not online drives, but
|
|
# only checks if they need formatting, which requires zerombr to be known
|
|
|
|
try:
|
|
ksparser.readKickstart(file)
|
|
except IOError, e:
|
|
# We may not have an intf now, but we can do better than just raising
|
|
# the exception.
|
|
if anaconda.intf:
|
|
anaconda.intf.kickstartErrorWindow("Could not open kickstart file or included file named %s" % e.filename)
|
|
sys.exit(1)
|
|
else:
|
|
stderrLog.critical(_("The following error was found while parsing the kickstart "
|
|
"configuration file:\n\n%s") % e)
|
|
sys.exit(1)
|
|
except KickstartError, e:
|
|
if anaconda.intf:
|
|
anaconda.intf.kickstartErrorWindow(e.__str__())
|
|
sys.exit(1)
|
|
else:
|
|
stderrLog.critical(_("The following error was found while parsing the kickstart "
|
|
"configuration file:\n\n%s") % e)
|
|
sys.exit(1)
|
|
|
|
return handler
|
|
|
|
def runPostScripts(anaconda):
|
|
if not anaconda.ksdata:
|
|
return
|
|
|
|
postScripts = filter (lambda s: s.type == KS_SCRIPT_POST,
|
|
anaconda.ksdata.scripts)
|
|
|
|
if len(postScripts) == 0:
|
|
return
|
|
|
|
# Remove environment variables that cause problems for %post scripts.
|
|
for var in ["LIBUSER_CONF"]:
|
|
if os.environ.has_key(var):
|
|
del(os.environ[var])
|
|
|
|
log.info("Running kickstart %%post script(s)")
|
|
if anaconda.intf is not None:
|
|
w = anaconda.intf.waitWindow(_("Post-Installation"),
|
|
_("Running post-installation scripts"))
|
|
|
|
map (lambda s: s.run(anaconda.rootPath, flags.serial, anaconda.intf), postScripts)
|
|
|
|
log.info("All kickstart %%post script(s) have been run")
|
|
if anaconda.intf is not None:
|
|
w.pop()
|
|
|
|
def runPreScripts(anaconda, scripts):
|
|
preScripts = filter (lambda s: s.type == KS_SCRIPT_PRE, scripts)
|
|
|
|
if len(preScripts) == 0:
|
|
return
|
|
|
|
log.info("Running kickstart %%pre script(s)")
|
|
if anaconda.intf is not None:
|
|
w = anaconda.intf.waitWindow(_("Pre-Installation"),
|
|
_("Running pre-installation scripts"))
|
|
|
|
map (lambda s: s.run("/", flags.serial, anaconda.intf), preScripts)
|
|
|
|
log.info("All kickstart %%pre script(s) have been run")
|
|
if anaconda.intf is not None:
|
|
w.pop()
|
|
|
|
def runTracebackScripts(anaconda):
|
|
log.info("Running kickstart %%traceback script(s)")
|
|
for script in filter (lambda s: s.type == KS_SCRIPT_TRACEBACK,
|
|
anaconda.ksdata.scripts):
|
|
script.run("/", flags.serial)
|
|
log.info("All kickstart %%traceback script(s) have been run")
|
|
|
|
def selectPackages(anaconda):
|
|
ksdata = anaconda.ksdata
|
|
ignoreAll = False
|
|
|
|
# If no %packages header was seen, use the installclass's default group
|
|
# selections. This can also be explicitly specified with %packages
|
|
# --default. Otherwise, select whatever was given (even if it's nothing).
|
|
if not ksdata.packages.seen or ksdata.packages.default:
|
|
anaconda.instClass.setGroupSelection(anaconda)
|
|
return
|
|
|
|
for pkg in ksdata.packages.packageList:
|
|
num = anaconda.backend.selectPackage(pkg)
|
|
if ksdata.packages.handleMissing == KS_MISSING_IGNORE or ignoreAll:
|
|
continue
|
|
if num > 0:
|
|
continue
|
|
rc = anaconda.intf.messageWindow(_("Missing Package"),
|
|
_("You have specified that the "
|
|
"package '%s' should be installed. "
|
|
"This package does not exist. "
|
|
"Would you like to continue or "
|
|
"abort this installation?") %(pkg,),
|
|
type="custom",
|
|
custom_buttons=[_("_Abort"),
|
|
_("_Ignore All"),
|
|
_("_Continue")])
|
|
if rc == 0:
|
|
sys.exit(1)
|
|
elif rc == 1:
|
|
ignoreAll = True
|
|
|
|
ksdata.packages.groupList.insert(0, Group("Core"))
|
|
|
|
if ksdata.packages.addBase:
|
|
ksdata.packages.groupList.insert(1, Group("Base"))
|
|
else:
|
|
log.warning("not adding Base group")
|
|
|
|
for grp in ksdata.packages.groupList:
|
|
default = False
|
|
optional = False
|
|
|
|
if grp.include == GROUP_DEFAULT:
|
|
default = True
|
|
elif grp.include == GROUP_ALL:
|
|
default = True
|
|
optional = True
|
|
|
|
try:
|
|
anaconda.backend.selectGroup(grp.name, (default, optional))
|
|
except NoSuchGroup, e:
|
|
if ksdata.packages.handleMissing == KS_MISSING_IGNORE or ignoreAll:
|
|
pass
|
|
else:
|
|
rc = anaconda.intf.messageWindow(_("Missing Group"),
|
|
_("You have specified that the "
|
|
"group '%s' should be installed. "
|
|
"This group does not exist. "
|
|
"Would you like to continue or "
|
|
"abort this installation?")
|
|
%(grp.name,),
|
|
type="custom",
|
|
custom_buttons=[_("_Abort"),
|
|
_("_Ignore All"),
|
|
_("_Continue")])
|
|
if rc == 0:
|
|
sys.exit(1)
|
|
elif rc == 1:
|
|
ignoreAll = True
|
|
|
|
map(anaconda.backend.deselectPackage, ksdata.packages.excludedList)
|
|
map(lambda g: anaconda.backend.deselectGroup(g.name),
|
|
ksdata.packages.excludedGroupList)
|
|
|
|
def setSteps(anaconda):
|
|
def havePackages(packages):
|
|
return len(packages.groupList) > 0 or len(packages.packageList) > 0 or \
|
|
len(packages.excludedList) > 0 or len(packages.excludedGroupList) > 0
|
|
|
|
dispatch = anaconda.dispatch
|
|
ksdata = anaconda.ksdata
|
|
interactive = ksdata.interactive.interactive
|
|
|
|
if ksdata.upgrade.upgrade:
|
|
upgrade.setSteps(anaconda)
|
|
|
|
# we have no way to specify migrating yet
|
|
dispatch.skipStep("upgrademigfind")
|
|
dispatch.skipStep("upgrademigratefs")
|
|
dispatch.skipStep("upgradecontinue")
|
|
dispatch.skipStep("findinstall", permanent = 1)
|
|
dispatch.skipStep("language")
|
|
dispatch.skipStep("keyboard")
|
|
dispatch.skipStep("betanag")
|
|
dispatch.skipStep("installtype")
|
|
else:
|
|
anaconda.instClass.setSteps(anaconda)
|
|
dispatch.skipStep("findrootparts")
|
|
|
|
if interactive or flags.autostep:
|
|
dispatch.skipStep("installtype")
|
|
dispatch.skipStep("bootdisk")
|
|
|
|
dispatch.skipStep("bootdisk")
|
|
dispatch.skipStep("betanag")
|
|
dispatch.skipStep("installtype")
|
|
dispatch.skipStep("network")
|
|
|
|
# Storage is initialized for us right when kickstart processing starts.
|
|
dispatch.skipStep("storageinit")
|
|
|
|
if not interactive:
|
|
# Don't show confirmation screens on non-interactive installs.
|
|
dispatch.skipStep("confirminstall")
|
|
dispatch.skipStep("confirmupgrade")
|
|
dispatch.skipStep("welcome")
|
|
|
|
# Since ignoredisk is optional and not specifying it means you want to
|
|
# consider all possible disks, we should not stop on the filter steps
|
|
# unless it's an interactive install.
|
|
dispatch.skipStep("filter")
|
|
dispatch.skipStep("filtertype")
|
|
|
|
# Make sure to automatically reboot even in interactive if told to.
|
|
if interactive and ksdata.reboot.action in [KS_REBOOT, KS_SHUTDOWN]:
|
|
dispatch.skipStep("complete")
|
|
|
|
# If the package section included anything, skip group selection unless
|
|
# they're in interactive.
|
|
if ksdata.upgrade.upgrade:
|
|
ksdata.skipSteps.extend(["tasksel", "group-selection"])
|
|
|
|
# Special check for this, since it doesn't make any sense.
|
|
if ksdata.packages.seen:
|
|
warnings.warn("Ignoring contents of %packages section due to upgrade.")
|
|
elif havePackages(ksdata.packages):
|
|
if interactive:
|
|
ksdata.showSteps.extend(["tasksel", "group-selection"])
|
|
else:
|
|
ksdata.skipSteps.extend(["tasksel", "group-selection"])
|
|
else:
|
|
if ksdata.packages.seen:
|
|
ksdata.skipSteps.extend(["tasksel", "group-selection"])
|
|
else:
|
|
ksdata.showSteps.extend(["tasksel", "group-selection"])
|
|
|
|
if not interactive:
|
|
for n in ksdata.skipSteps:
|
|
dispatch.skipStep(n)
|
|
for n in ksdata.permanentSkipSteps:
|
|
dispatch.skipStep(n, permanent=1)
|
|
for n in ksdata.showSteps:
|
|
dispatch.skipStep(n, skip = 0)
|
|
|
|
# Text mode doesn't have all the steps that graphical mode does, so we
|
|
# can't stop and prompt for missing information. Make sure we've got
|
|
# everything that would be provided by a missing section now and error
|
|
# out if we don't.
|
|
if anaconda.displayMode == "t":
|
|
missingSteps = [("bootloader", "Bootloader configuration"),
|
|
("filter", "Disks to use in installation"),
|
|
("cleardiskssel", "Disks to clear"),
|
|
("group-selection", "Package selection")]
|
|
errors = []
|
|
|
|
for (step, msg) in missingSteps:
|
|
if not dispatch.stepInSkipList(step):
|
|
errors.append(msg)
|
|
|
|
if len(errors) > 0:
|
|
anaconda.intf.kickstartErrorWindow(_("The kickstart configuration "
|
|
"file is missing required information that anaconda cannot "
|
|
"prompt for. Please add the following sections and try "
|
|
"again:\n%s") % ", ".join(errors))
|
|
sys.exit(0)
|