summaryrefslogtreecommitdiffstats
path: root/pyanaconda
diff options
context:
space:
mode:
Diffstat (limited to 'pyanaconda')
-rw-r--r--pyanaconda/__init__.py12
-rw-r--r--pyanaconda/anaconda_log.py10
-rw-r--r--pyanaconda/backend.py3
-rw-r--r--pyanaconda/backend_log.py95
-rw-r--r--pyanaconda/bootloader.py46
-rw-r--r--pyanaconda/errors.py2
-rw-r--r--pyanaconda/exception.py54
-rw-r--r--pyanaconda/install.py3
-rw-r--r--pyanaconda/installclass.py9
-rw-r--r--pyanaconda/installclasses/fedora.py13
-rw-r--r--pyanaconda/installclasses/rhel.py3
-rw-r--r--pyanaconda/iutil.py59
-rw-r--r--pyanaconda/kickstart.py142
-rw-r--r--pyanaconda/network.py837
-rw-r--r--pyanaconda/packaging/__init__.py43
-rw-r--r--pyanaconda/packaging/yumpayload.py61
-rw-r--r--pyanaconda/rescue.py163
-rw-r--r--pyanaconda/security.py25
-rw-r--r--pyanaconda/storage/__init__.py111
-rw-r--r--pyanaconda/storage/deviceaction.py5
-rw-r--r--pyanaconda/storage/devicelibs/crypto.py2
-rw-r--r--pyanaconda/storage/devicelibs/swap.py41
-rw-r--r--pyanaconda/storage/devices.py86
-rw-r--r--pyanaconda/storage/devicetree.py68
-rw-r--r--pyanaconda/storage/fcoe.py6
-rw-r--r--pyanaconda/storage/formats/fs.py5
-rw-r--r--pyanaconda/storage/formats/swap.py3
-rw-r--r--pyanaconda/storage/partitioning.py41
-rw-r--r--pyanaconda/storage/udev.py35
-rw-r--r--pyanaconda/text.py6
-rw-r--r--pyanaconda/textw/add_drive_text.py8
-rw-r--r--pyanaconda/textw/netconfig_text.py37
-rw-r--r--pyanaconda/textw/network_text.py3
-rw-r--r--pyanaconda/ui/__init__.py19
-rw-r--r--pyanaconda/ui/common.py1
-rw-r--r--pyanaconda/ui/gui/Makefile.am2
-rw-r--r--pyanaconda/ui/gui/TODO4
-rw-r--r--pyanaconda/ui/gui/__init__.py18
-rw-r--r--pyanaconda/ui/gui/hubs/Makefile.am2
-rw-r--r--pyanaconda/ui/gui/hubs/__init__.py6
-rw-r--r--pyanaconda/ui/gui/hubs/progress.glade (renamed from pyanaconda/ui/gui/hubs/progress.ui)0
-rw-r--r--pyanaconda/ui/gui/hubs/progress.py11
-rw-r--r--pyanaconda/ui/gui/hubs/summary.glade (renamed from pyanaconda/ui/gui/hubs/summary.ui)0
-rw-r--r--pyanaconda/ui/gui/hubs/summary.py2
-rw-r--r--pyanaconda/ui/gui/main.glade (renamed from pyanaconda/ui/gui/main.ui)0
-rw-r--r--pyanaconda/ui/gui/spokes/Makefile.am2
-rw-r--r--pyanaconda/ui/gui/spokes/__init__.py27
-rw-r--r--pyanaconda/ui/gui/spokes/custom.glade (renamed from pyanaconda/ui/gui/spokes/custom.ui)0
-rw-r--r--pyanaconda/ui/gui/spokes/custom.py6
-rw-r--r--pyanaconda/ui/gui/spokes/datetime_spoke.glade (renamed from pyanaconda/ui/gui/spokes/datetime_spoke.ui)0
-rw-r--r--pyanaconda/ui/gui/spokes/datetime_spoke.py4
-rw-r--r--pyanaconda/ui/gui/spokes/keyboard.glade (renamed from pyanaconda/ui/gui/spokes/keyboard.ui)0
-rw-r--r--pyanaconda/ui/gui/spokes/keyboard.py4
-rw-r--r--pyanaconda/ui/gui/spokes/lib/Makefile.am2
-rw-r--r--pyanaconda/ui/gui/spokes/lib/cart.glade (renamed from pyanaconda/ui/gui/spokes/lib/cart.ui)0
-rw-r--r--pyanaconda/ui/gui/spokes/lib/cart.py2
-rw-r--r--pyanaconda/ui/gui/spokes/lib/detailederror.glade90
-rw-r--r--pyanaconda/ui/gui/spokes/lib/detailederror.py58
-rw-r--r--pyanaconda/ui/gui/spokes/network.glade (renamed from pyanaconda/ui/gui/spokes/network.ui)0
-rw-r--r--pyanaconda/ui/gui/spokes/network.py6
-rw-r--r--pyanaconda/ui/gui/spokes/software.glade (renamed from pyanaconda/ui/gui/spokes/software.ui)4
-rw-r--r--pyanaconda/ui/gui/spokes/software.py50
-rw-r--r--pyanaconda/ui/gui/spokes/source.glade (renamed from pyanaconda/ui/gui/spokes/source.ui)0
-rw-r--r--pyanaconda/ui/gui/spokes/source.py10
-rw-r--r--pyanaconda/ui/gui/spokes/storage.glade (renamed from pyanaconda/ui/gui/spokes/storage.ui)0
-rw-r--r--pyanaconda/ui/gui/spokes/storage.py24
-rw-r--r--pyanaconda/ui/gui/spokes/welcome.glade (renamed from pyanaconda/ui/gui/spokes/welcome.ui)0
-rw-r--r--pyanaconda/ui/gui/spokes/welcome.py4
-rwxr-xr-xpyanaconda/ui/gui/tools/run-spoke.py1
-rw-r--r--pyanaconda/upgrade.py341
-rw-r--r--pyanaconda/vnc.py11
-rw-r--r--pyanaconda/yuminstall.py7
72 files changed, 1308 insertions, 1447 deletions
diff --git a/pyanaconda/__init__.py b/pyanaconda/__init__.py
index eb44ca90e..cf40ccd04 100644
--- a/pyanaconda/__init__.py
+++ b/pyanaconda/__init__.py
@@ -261,16 +261,18 @@ class Anaconda(object):
self.methodstr = methodstr
def write(self):
+ import network
self.writeXdriver()
self.instLanguage.write()
self.timezone.write()
+ network.write_sysconfig_network()
+ network.disableIPV6()
+ network.copyConfigToPath(ROOT_PATH)
if not self.ksdata:
- self.instClass.setNetworkOnbootDefault(self.network)
- self.network.write()
- self.network.copyConfigToPath()
- self.network.disableNMForStorageDevices(self)
- self.network.autostartFCoEDevices(self)
+ self.instClass.setNetworkOnbootDefault()
+ network.disableNMForStorageDevices(self.storage)
+ network.autostartFCoEDevices(self.storage)
self.desktop.write()
self.security.write()
self.firewall.write()
diff --git a/pyanaconda/anaconda_log.py b/pyanaconda/anaconda_log.py
index f9d884e73..40c1938f2 100644
--- a/pyanaconda/anaconda_log.py
+++ b/pyanaconda/anaconda_log.py
@@ -44,6 +44,7 @@ MAIN_LOG_FILE = "/tmp/anaconda.log"
MAIN_LOG_TTY = "/dev/tty3"
PROGRAM_LOG_FILE = "/tmp/program.log"
STORAGE_LOG_FILE = "/tmp/storage.log"
+PACKAGING_LOG_FILE = "/tmp/packaging.log"
ANACONDA_SYSLOG_FACILITY = SysLogHandler.LOG_LOCAL1
logLevelMap = {"debug": logging.DEBUG, "info": logging.INFO,
@@ -151,6 +152,13 @@ class AnacondaLog:
minLevel=logging.DEBUG)
self.forwardToSyslog(program_logger)
+ # Create the packaging logger.
+ packaging_logger = logging.getLogger("packaging")
+ packaging_logger.setLevel(logging.DEBUG)
+ self.addFileHandler(PACKAGING_LOG_FILE, packaging_logger,
+ minLevel=logging.DEBUG)
+ self.forwardToSyslog(packaging_logger)
+
# Create a second logger for just the stuff we want to dup on
# stdout. Anything written here will also get passed up to the
# parent loggers for processing and possibly be written to the
@@ -224,7 +232,7 @@ class AnacondaLog:
def setupVirtio(self):
"""Setup virtio rsyslog logging.
"""
- TEMPLATE = "*.* %s\n"
+ TEMPLATE = "*.* %s;anaconda_syslog\n"
if not os.path.exists(self.VIRTIO_PORT) \
or not os.access(self.VIRTIO_PORT, os.W_OK):
diff --git a/pyanaconda/backend.py b/pyanaconda/backend.py
index 53e8a9f05..4a3e6c1c5 100644
--- a/pyanaconda/backend.py
+++ b/pyanaconda/backend.py
@@ -25,7 +25,6 @@ import shutil
import iutil
import os, sys
import logging
-import backend_log
from constants import *
import isys
@@ -98,7 +97,6 @@ class AnacondaBackend:
storage.writeEscrowPackets(anaconda)
sys.stdout.flush()
- backend_log.log.stop()
def doInstall(self, anaconda):
log.warning("doInstall not implemented for backend!")
@@ -126,7 +124,6 @@ class AnacondaBackend:
shutil.rmtree (syslogname)
except OSError:
pass
- backend_log.log.start(instPath, syslogname)
if self.anaconda.upgrade:
self.modeText = _("%s Upgrading %s\n")
diff --git a/pyanaconda/backend_log.py b/pyanaconda/backend_log.py
deleted file mode 100644
index 9125d7303..000000000
--- a/pyanaconda/backend_log.py
+++ /dev/null
@@ -1,95 +0,0 @@
-# backend_log.py
-# Logging infrastructure for Anaconda's backend.
-#
-# Copyright (C) 2009 Red Hat, Inc.
-#
-# This copyrighted material is made available to anyone wishing to use,
-# modify, copy, or redistribute it subject to the terms and conditions of
-# the GNU General Public License v.2, or (at your option) any later version.
-# This program is distributed in the hope that it will be useful, but WITHOUT
-# ANY WARRANTY expressed or implied, including the implied warranties 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, write to the
-# Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
-# 02110-1301, USA. Any Red Hat trademarks that are incorporated in the
-# source code or documentation are not subject to the GNU General Public
-# License and may only be used or replicated with the express permission of
-# Red Hat, Inc.
-#
-# Red Hat Author(s): Ales Kozumplik <akozumpl@redhat.com>
-#
-
-import logging
-import os
-import signal
-
-import anaconda_log
-import iutil
-
-SYSLOG_PATH = '/sbin/rsyslogd'
-SYSLOG_PIDFILE = '/var/run/rsyslog_backend.pid'
-SYSLOG_CFGFILE = '/tmp/rsyslog_backend.conf'
-
-CFG_TEMPLATE = """
-$ModLoad imuxsock
-$InputUnixListenSocketHostName sysimage
-$AddUnixListenSocket %(socket)s
-+sysimage
-*.* %(logfile)s;RSYSLOG_TraditionalFileFormat
-%(remote_syslog)s
-"""
-
-global_log = logging.getLogger("anaconda")
-class BackendSyslog:
- def __init__(self):
- pass
-
- def build_cfg(self, root, log):
- socket = "%s/dev/log" % (root, )
- remote_syslog = ''
- if anaconda_log.logger.remote_syslog:
- remote_syslog = "*.* @@%s" % (anaconda_log.logger.remote_syslog, )
-
- cfg = CFG_TEMPLATE % {
- 'socket' : socket,
- 'logfile' : log,
- 'remote_syslog' : remote_syslog
- }
- with open(SYSLOG_CFGFILE, 'w') as cfg_file:
- cfg_file.write(cfg)
-
- def start(self, root, log):
- """ Start an rsyslogd instance dedicated for the sysimage.
-
- Other possibility would be to change configuration and SIGHUP the
- existing instance, but it could lose some of its internal queues and
- give us problems with remote logging.
- """
- self.build_cfg(root, log)
- args = ['-c', '4',
- '-f', SYSLOG_CFGFILE,
- '-i', str(SYSLOG_PIDFILE)]
- status = iutil.execWithRedirect(SYSLOG_PATH, args)
- if status == 0:
- global_log.info("Backend logger started.")
- else:
- global_log.error("Unable to start backend logger")
-
- def stop(self):
- try:
- with open(SYSLOG_PIDFILE, 'r') as pidfile:
- pid = int(pidfile.read())
- os.kill(pid, signal.SIGKILL)
- except Exception:
- pass
- else:
- global_log.info("Backend logger stopped.")
-
- try:
- os.unlink(SYSLOG_CFGFILE)
- except OSError as e:
- global_log.error("Failed to unlink backend logger config file: %s"
- % e)
-
-log = BackendSyslog()
diff --git a/pyanaconda/bootloader.py b/pyanaconda/bootloader.py
index 991c8de80..ed8040ff5 100644
--- a/pyanaconda/bootloader.py
+++ b/pyanaconda/bootloader.py
@@ -28,12 +28,13 @@ import struct
from pyanaconda import iutil
from pyanaconda.storage.devicelibs import mdraid
-from pyanaconda.isys import sync
+from pyanaconda.isys import sync, getMacAddress
from pyanaconda.product import productName
from pyanaconda.flags import flags
from pyanaconda.constants import *
from pyanaconda.storage.errors import StorageError
from pyanaconda.storage.fcoe import fcoe
+import pyanaconda.network
import gettext
_ = lambda x: gettext.ldgettext("anaconda", x)
@@ -750,14 +751,11 @@ class BootLoader(object):
Keyword Arguments:
storage - a pyanaconda.storage.Storage instance
- network - a pyanaconda.network.Network instance (for network
- storage devices' boot arguments)
All other arguments are expected to have a dracutSetupArgs()
method.
"""
storage = kwargs.pop("storage", None)
- network = kwargs.pop("network", None)
#
# FIPS
@@ -811,15 +809,7 @@ class BootLoader(object):
# network storage
# XXX this is nothing to be proud of
if isinstance(dep, NetworkStorageDevice):
- if network is None:
- log.error("missing network instance for setup of boot "
- "command line for network storage device %s"
- % dep.name)
- raise BootLoaderError("missing network instance when "
- "setting boot args for network "
- "storage device")
-
- setup_args = network.dracutSetupArgs(dep)
+ setup_args = pyanaconda.network.dracutSetupArgs(dep)
self.boot_args.update(setup_args)
self.dracut_args.update(setup_args)
@@ -845,7 +835,7 @@ class BootLoader(object):
# Dracut needs the explicit ifname= because biosdevname
# fails to rename the iface (because of BFS booting from it).
for nic, dcb, auto_vlan in fcoe().nics:
- hwaddr = network.netdevices[nic].get("HWADDR")
+ hwaddr = getMacAddress(nic)
self.boot_args.add("ifname=%s:%s" % (nic, hwaddr.lower()))
#
@@ -1485,6 +1475,10 @@ class GRUB2(GRUB):
def write_config(self):
self.write_config_console(None)
+ # See if we have a password and if so update the boot args before we
+ # write out the defaults file.
+ if self.password or self.encrypted_password:
+ self.boot_args.add("rd.shell=0")
self.write_defaults()
# if we fail to setup password auth we should complete the
@@ -1522,13 +1516,13 @@ class GRUB2(GRUB):
# XXX will installing to multiple drives work as expected with GRUBv2?
for (stage1dev, stage2dev) in self.install_targets:
- args += ["--no-floppy", stage1dev.path]
+ grub_args = args + ["--no-floppy", stage1dev.path]
if stage1dev == stage2dev:
# This is hopefully a temporary hack. GRUB2 currently refuses
# to install to a partition's boot block without --force.
- args.insert(0, '--force')
+ grub_args.insert(0, '--force')
- rc = iutil.execWithRedirect("grub2-install", args,
+ rc = iutil.execWithRedirect("grub2-install", grub_args,
stdout="/dev/tty5", stderr="/dev/tty5",
root=ROOT_PATH,
env_prune=['MALLOC_PERTURB_'])
@@ -1668,6 +1662,8 @@ class YabootSILOBase(BootLoader):
continue
args = Arguments()
+ if self.password or self.encrypted_password:
+ args.add("rd.shell=0")
if image.initrd:
initrd_line = "\tinitrd=%s/%s\n" % (self.boot_prefix,
image.initrd)
@@ -1996,7 +1992,21 @@ class ZIPL(BootLoader):
# DWL FIXME: resolve the boot device to a StorageDevice from storage
buf = iutil.execWithCapture("zipl", [],
stderr="/dev/tty5",
- root=ROOT_PATH)
+ root=ROOT_PATH,
+ fatal=True)
+ for line in buf.splitlines():
+ if line.startswith("Preparing boot device: "):
+ # Output here may look like:
+ # Preparing boot device: dasdb (0200).
+ # Preparing boot device: dasdl.
+ # We want to extract the device name and pass that.
+ name = re.sub(".+?: ", "", line)
+ name = re.sub("(\s\(.+\))?\.$", "", name)
+ device = self.storage.devicetree.getDeviceByName(name)
+ if not device:
+ raise BootLoaderError("could not find IPL device")
+
+ self.stage1_device = device
class SILO(YabootSILOBase):
name = "SILO"
diff --git a/pyanaconda/errors.py b/pyanaconda/errors.py
index 5d823de23..37ee4d01a 100644
--- a/pyanaconda/errors.py
+++ b/pyanaconda/errors.py
@@ -241,7 +241,7 @@ class ErrorHandler(object):
rc = ERROR_RAISE
if not self.ui:
- raise exn
+ raise
_map = {KickstartError: self._kickstartErrorHandler,
StorageError.PartitioningError: self._partitionErrorHandler,
diff --git a/pyanaconda/exception.py b/pyanaconda/exception.py
index 47ccd1e4b..70deed5d4 100644
--- a/pyanaconda/exception.py
+++ b/pyanaconda/exception.py
@@ -31,22 +31,49 @@ from flags import flags
import kickstart
import storage.errors
from pyanaconda.constants import ROOT_PATH
+from gi.repository import GLib
import logging
log = logging.getLogger("anaconda")
+import gettext
+_ = lambda x: gettext.ldgettext("anaconda", x)
+
+
class AnacondaExceptionHandler(ExceptionHandler):
def handleException(self, (ty, value, tb), obj):
- import traceback
- # Save the exception to the filesystem first.
- self.exn = self.exnClass((ty, value, tb), self.conf)
- (fd, self.exnFile) = self.openFile()
- text = self.exn.write(obj, fd)
- fd.close()
-
- traceback.print_exception(ty, value, tb)
- os._exit(10)
+ def run_handleException_on_idle(args_tuple):
+ """
+ Helper function with one argument only so that it can be registered
+ with GLib.idle_add() to run on idle.
+
+ @param args_tuple: ((ty, value, tb), obj)
+
+ """
+
+ trace, obj = args_tuple
+ ty, value, tb = trace
+
+ super(AnacondaExceptionHandler, self).handleException((ty, value, tb),
+ obj)
+
+ if issubclass(ty, storage.errors.StorageError) and value.hardware_fault:
+ hw_error_msg = _("The installation was stopped due to what "
+ "seems to be a problem with your hardware. "
+ "The exact error message is:\n\n%s.\n\n "
+ "The installer will now terminate.") % str(value)
+ self.intf.showError(hw_error_msg)
+ sys.exit(0)
+ else:
+ if GLib.main_depth() > 0:
+ # main loop is running, don't crash it by running another one
+ # potentially from a different thread
+ GLib.idle_add(run_handleException_on_idle,
+ ((ty, value, tb), obj))
+ else:
+ super(AnacondaExceptionHandler, self).handleException(
+ (ty, value, tb), obj)
def postWriteHook(self, (ty, value, tb), anaconda):
# See if /mnt/sysimage is present and put exception there as well
@@ -72,8 +99,6 @@ class AnacondaExceptionHandler(ExceptionHandler):
except SystemError:
pass
- self.intf.__del__ ()
-
pidfl = "/tmp/vncshell.pid"
if os.path.exists(pidfl) and os.path.isfile(pidfl):
pf = open(pidfl, "r")
@@ -97,9 +122,14 @@ class AnacondaExceptionHandler(ExceptionHandler):
termios.tcsetattr(si, termios.TCSADRAIN, attr)
print("\nEntering debugger...")
+ print("Use 'continue' command to quit the debugger and get back to "\
+ "the main window")
import pdb
pdb.post_mortem (tb)
- os.kill(os.getpid(), signal.SIGKILL)
+ try:
+ isys.vtActivate(6)
+ except SystemError:
+ pass
def initExceptionHandling(anaconda):
fileList = [ "/tmp/anaconda.log",
diff --git a/pyanaconda/install.py b/pyanaconda/install.py
index be31e75b5..3ea3bd5ee 100644
--- a/pyanaconda/install.py
+++ b/pyanaconda/install.py
@@ -62,7 +62,7 @@ def doInstall(storage, payload, ksdata, instClass):
turnOnFilesystems(storage)
# Do packaging.
- payload.preInstall(packages=storage.packages)
+ payload.preInstall(packages=storage.packages, groups=payload.languageGroups(ksdata.lang.lang))
payload.install()
with progress_report(_("Performing post-install setup tasks")):
@@ -74,6 +74,7 @@ def doInstall(storage, payload, ksdata, instClass):
# Now run the execute methods of ksdata that require an installed system
# to be present first.
+ ksdata.authconfig.execute(storage, ksdata, instClass)
ksdata.firstboot.execute(storage, ksdata, instClass)
ksdata.services.execute(storage, ksdata, instClass)
ksdata.keyboard.execute(storage, ksdata, instClass)
diff --git a/pyanaconda/installclass.py b/pyanaconda/installclass.py
index 3f291059a..cdef54215 100644
--- a/pyanaconda/installclass.py
+++ b/pyanaconda/installclass.py
@@ -32,6 +32,7 @@ import types
from constants import *
from product import *
from storage.partspec import *
+from storage.devicelibs import swap
import gettext
_ = lambda x: gettext.ldgettext("anaconda", x)
@@ -116,9 +117,9 @@ class BaseInstallClass(object):
if bootreq:
autorequests.extend(bootreq)
- (minswap, maxswap) = iutil.swapSuggestion()
- autorequests.append(PartSpec(fstype="swap", size=minswap, maxSize=maxswap,
- grow=True, lv=True, encrypted=True))
+ swp = swap.swapSuggestion()
+ autorequests.append(PartSpec(fstype="swap", size=swp, grow=False,
+ lv=True, encrypted=True))
storage.autoPartitionRequests = autorequests
@@ -153,7 +154,7 @@ class BaseInstallClass(object):
return (all(result.values()), result)
- def setNetworkOnbootDefault(self, network):
+ def setNetworkOnbootDefault(self):
pass
def __init__(self):
diff --git a/pyanaconda/installclasses/fedora.py b/pyanaconda/installclasses/fedora.py
index bc575ea8d..e2c15433a 100644
--- a/pyanaconda/installclasses/fedora.py
+++ b/pyanaconda/installclasses/fedora.py
@@ -21,7 +21,7 @@ from pyanaconda.installclass import BaseInstallClass
from pyanaconda.constants import *
from pyanaconda.product import *
from pyanaconda import iutil
-from pyanaconda.network import hasActiveNetDev
+from pyanaconda import network
from pyanaconda import isys
import os, types
@@ -122,17 +122,20 @@ class InstallClass(BaseInstallClass):
# than two versions ago!
return newVer >= oldVer and newVer - oldVer <= 2
- def setNetworkOnbootDefault(self, network):
+ def setNetworkOnbootDefault(self):
# if something's already enabled, we can just leave the config alone
- for devName, dev in network.netdevices.items():
- if dev.get('ONBOOT') == 'yes':
+ for devName in network.getDevices():
+ if network.get_ifcfg_value(devName, "ONBOOT", ROOT_PATH) == "yes":
return
# the default otherwise: bring up the first wired netdev with link
- for devName, dev in network.netdevices.items():
+ for devName in network.getDevices():
if (not isys.isWirelessDevice(devName) and
isys.getLinkStatus(devName)):
+ dev = network.NetworkDevice(ROOT_PATH + network.netscriptsDir, devName)
+ dev.loadIfcfgFile()
dev.set(('ONBOOT', 'yes'))
+ dev.writeIfcfgFile()
break
def __init__(self):
diff --git a/pyanaconda/installclasses/rhel.py b/pyanaconda/installclasses/rhel.py
index 339b5b833..25edbae41 100644
--- a/pyanaconda/installclasses/rhel.py
+++ b/pyanaconda/installclasses/rhel.py
@@ -96,6 +96,9 @@ class InstallClass(BaseInstallClass):
return False
def versionMatches(self, oldver):
+ if oldver is None:
+ return False
+
oldMajor = oldver.split(".")[0]
newMajor = productVersion.split(".")[0]
diff --git a/pyanaconda/iutil.py b/pyanaconda/iutil.py
index 122894695..00fcb6ad7 100644
--- a/pyanaconda/iutil.py
+++ b/pyanaconda/iutil.py
@@ -200,8 +200,10 @@ def execWithRedirect(command, argv, stdin = None, stdout = None,
# @param stdin The file descriptor to read stdin from.
# @param stderr The file descriptor to redirect stderr to.
# @param root The directory to chroot to before running command.
+# @param fatal Boolean to determine if non-zero exit is fatal.
# @return The output of command from stdout.
-def execWithCapture(command, argv, stdin = None, stderr = None, root='/'):
+def execWithCapture(command, argv, stdin = None, stderr = None, root='/',
+ fatal = False):
if flags.testing:
log.info("not running command because we're testing: %s %s"
% (command, " ".join(argv)))
@@ -257,6 +259,10 @@ def execWithCapture(command, argv, stdin = None, stderr = None, root='/'):
if proc.returncode is not None:
break
+ # if we have anything other than a clean exit, and we get the fatal
+ # option, raise the OSError.
+ if proc.returncode and fatal:
+ raise OSError('Non-zero return code: %s' % proc.returncode)
except OSError as e:
log.error ("Error running " + command + ": " + e.strerror)
closefds()
@@ -459,34 +465,6 @@ def memInstalled():
return long(mem)
-## Suggest the size of the swap partition that will be created.
-# @param quiet Should size information be logged?
-# @return A tuple of the minimum and maximum swap size, in megabytes.
-def swapSuggestion(quiet=0):
- mem = memInstalled()/1024
- mem = ((mem/16)+1)*16
- if not quiet:
- log.info("Detected %sM of memory", mem)
-
- if mem <= 256:
- minswap = 256
- maxswap = 512
- else:
- if mem > 2048:
- minswap = 1024
- maxswap = 2048 + mem
- else:
- minswap = mem
- maxswap = 2*mem
-
- if isS390():
- minswap = 1
-
- if not quiet:
- log.info("Swap attempt of %sM to %sM", minswap, maxswap)
-
- return (minswap, maxswap)
-
## Create a directory path. Don't fail if the directory already exists.
# @param dir The directory path to create.
def mkdirChain(dir):
@@ -501,19 +479,6 @@ def mkdirChain(dir):
log.error("could not create directory %s: %s" % (dir, e.strerror))
-## Get the total amount of swap memory.
-# @return The total amount of swap memory in kilobytes, or 0 if unknown.
-def swapAmount():
- f = open("/proc/meminfo", "r")
- lines = f.readlines()
- f.close()
-
- for l in lines:
- if l.startswith("SwapTotal:"):
- fields = string.split(l)
- return int(fields[1])
- return 0
-
## Copy a device node.
# Copies a device node by looking at the device type, major and minor device
# numbers, and doing a mknod on the new device name.
@@ -1082,6 +1047,16 @@ def dracut_eject(device):
except Exception, e:
log.error("Error writing dracut shutdown eject hook for %s: %s" % (device, e))
+def get_option_value(opt_name, options):
+ """ Return the value of a named option in the specified options string. """
+ for opt in options.split(","):
+ if "=" not in opt:
+ continue
+
+ name, val = opt.split("=")
+ if name == opt_name:
+ return val.strip()
+
class ProxyStringError(Exception):
pass
diff --git a/pyanaconda/kickstart.py b/pyanaconda/kickstart.py
index 585911937..7c3baa2c1 100644
--- a/pyanaconda/kickstart.py
+++ b/pyanaconda/kickstart.py
@@ -23,7 +23,9 @@ from storage.deviceaction import *
from storage.devices import LUKSDevice
from storage.devicelibs.lvm import getPossiblePhysicalExtents
from storage.devicelibs.mpath import MultipathConfigWriter, MultipathTopology
+from storage.devicelibs import swap
from storage.formats import getFormat
+from storage.partitioning import doPartitioning
import storage.iscsi
import storage.fcoe
import storage.zfcp
@@ -37,6 +39,7 @@ import os.path
import tempfile
from flags import flags
from constants import *
+import shlex
import sys
import urlgrabber
import network
@@ -49,7 +52,9 @@ from pyanaconda import ntp
from pykickstart.base import KickstartCommand
from pykickstart.constants import *
from pykickstart.errors import formatErrorMsg, KickstartError, KickstartValueError
-from pykickstart.parser import Group, KickstartParser, Script
+from pykickstart.parser import KickstartParser
+from pykickstart.parser import Group as PackageGroup
+from pykickstart.parser import Script as KSScript
from pykickstart.sections import *
from pykickstart.version import returnClassForVersion
@@ -70,7 +75,7 @@ packagesSeen = False
# so it needs to know about them in some additional way: have the topology ready.
topology = None
-class AnacondaKSScript(Script):
+class AnacondaKSScript(KSScript):
def run(self, chroot, serial):
if self.inChroot:
scriptRoot = chroot
@@ -212,6 +217,22 @@ def removeExistingFormat(device, storage):
### SUBCLASSES OF PYKICKSTART COMMAND HANDLERS
###
+class Authconfig(commands.authconfig.FC3_Authconfig):
+ def execute(self, *args):
+ args = ["--update", "--nostart"] + shlex.split(self.authconfig)
+
+ if not flags.automatedInstall and \
+ (os.path.exists(ROOT_PATH + "/lib64/security/pam_fprintd.so") or \
+ os.path.exists(ROOT_PATH + "/lib/security/pam_fprintd.so")):
+ args += ["--enablefingerprint"]
+
+ try:
+ iutil.execWithRedirect("/usr/sbin/authconfig", args,
+ stdout="/dev/tty5", stderr="/dev/tty5",
+ root=ROOT_PATH)
+ except RuntimeError as msg:
+ log.error("Error running /usr/sbin/authconfig %s: %s", args, msg)
+
class AutoPart(commands.autopart.F17_AutoPart):
def execute(self, storage, ksdata, instClass):
from pyanaconda.platform import getPlatform
@@ -289,6 +310,11 @@ class Bootloader(commands.bootloader.F18_Bootloader):
if self.leavebootorder:
flags.leavebootorder = True
+class BTRFS(commands.btrfs.F17_BTRFS):
+ def execute(self, storage, ksdata, instClass):
+ for b in self.btrfsList:
+ b.execute(storage, ksdata, instClass)
+
class BTRFSData(commands.btrfs.F17_BTRFSData):
def execute(self, storage, ksdata, instClass):
devicetree = storage.devicetree
@@ -416,7 +442,7 @@ class Fcoe(commands.fcoe.F13_Fcoe):
class Firstboot(commands.firstboot.FC3_Firstboot):
def execute(self, *args):
- if not os.path.exists("/lib/systemd/system/firstboot-graphical.service"):
+ if not os.path.exists(ROOT_PATH + "/lib/systemd/system/firstboot-graphical.service"):
return
action = "enable"
@@ -507,6 +533,11 @@ class IscsiName(commands.iscsiname.FC6_IscsiName):
storage.iscsi.iscsi().initiator = self.iscsiname
return retval
+class LogVol(commands.logvol.F17_LogVol):
+ def execute(self, storage, ksdata, instClass):
+ for l in self.lvList:
+ l.execute(storage, ksdata, instClass)
+
class LogVolData(commands.logvol.F17_LogVolData):
def execute(self, storage, ksdata, instClass):
devicetree = storage.devicetree
@@ -516,9 +547,9 @@ class LogVolData(commands.logvol.F17_LogVolData):
if self.mountpoint == "swap":
type = "swap"
self.mountpoint = ""
- if self.recommended:
- (self.size, self.maxSizeMB) = iutil.swapSuggestion()
- self.grow = True
+ if self.recommended or self.hibernation:
+ self.size = swap.swapSuggestion(hibernation=self.hibernation)
+ self.grow = False
else:
if self.fstype != "":
type = self.fstype
@@ -621,7 +652,7 @@ class LogVolData(commands.logvol.F17_LogVolData):
request = storage.newLV(format=format,
name=self.name,
- vg=vg,
+ parents=[vg],
size=self.size,
grow=self.grow,
maxsize=self.maxSizeMB,
@@ -672,7 +703,7 @@ class NetworkData(commands.network.F16_NetworkData):
def execute(self):
if flags.imageInstall:
if self.hostname != "":
- self.anaconda.network.setHostname(self.hostname)
+ network.setHostname(self.hostname)
# Only set hostname
return
@@ -681,22 +712,22 @@ class NetworkData(commands.network.F16_NetworkData):
# only set hostname
if self.essid:
if self.hostname != "":
- self.anaconda.network.setHostname(self.hostname)
+ network.setHostname(self.hostname)
return
- devices = self.anaconda.network.netdevices
+ devices = network.getDevices()
if not self.device:
- if self.anaconda.network.ksdevice:
+ if "ksdevice" in flags.cmdline:
msg = "ksdevice boot parameter"
- device = self.anaconda.network.ksdevice
+ device = network.get_ksdevice_name(flags.cmdline["ksdevice"])
elif network.hasActiveNetDev():
# device activated in stage 1 by network kickstart command
msg = "first active device"
device = network.getActiveNetDevs()[0]
else:
msg = "first device found"
- device = min(devices.keys())
+ device = min(devices)
log.info("unspecified network --device in kickstart, using %s (%s)" %
(device, msg))
else:
@@ -722,18 +753,22 @@ class NetworkData(commands.network.F16_NetworkData):
# If we were given a network device name, grab the device object.
# If we were given a MAC address, resolve that to a device name
# and then grab the device object. Otherwise, errors.
- dev = None
- if devices.has_key(device):
- dev = devices[device]
- else:
- for (key, val) in devices.iteritems():
- if val.get("HWADDR").lower() == device.lower():
- dev = val
+ if device not in devices:
+ for d in devices:
+ if isys.getMacAddress(d).lower() == device.lower():
+ device = d
break
+ dev = network.NetworkDevice(ROOT_PATH, device)
+ try:
+ dev.loadIfcfgFile()
+ except IOError as e:
+ log.info("Can't load ifcfg file %s" % dev.path)
+ dev = None
+
if self.hostname != "":
- self.anaconda.network.setHostname(self.hostname)
+ network.setHostname(self.hostname)
if not dev:
# Only set hostname
return
@@ -784,14 +819,18 @@ class NetworkData(commands.network.F16_NetworkData):
dev.set(("ETHTOOL_OPTS", self.ethtool))
if self.nameserver != "":
- self.anaconda.network.setDNS(self.nameserver, dev.iface)
+ dev.setDNS(self.nameserver)
if self.gateway != "":
- self.anaconda.network.setGateway(self.gateway, dev.iface)
+ dev.setGateway(self.gateway)
if self.nodefroute:
dev.set (("DEFROUTE", "no"))
+ #TODO
+ # write ifcfg file, carefuly handle ONBOOT value,
+ # problems - might activate the device!!!
+
class MultiPath(commands.multipath.FC6_MultiPath):
def parse(self, args):
raise NotImplementedError("The multipath kickstart command is not currently supported")
@@ -800,6 +839,11 @@ class DmRaid(commands.dmraid.FC6_DmRaid):
def parse(self, args):
raise NotImplementedError("The dmraid kickstart command is not currently supported")
+class Partition(commands.partition.F17_Partition):
+ def execute(self, storage, ksdata, instClass):
+ for p in self.partitions:
+ p.execute(storage, ksdata, instClass)
+
class PartitionData(commands.partition.F17_PartData):
def execute(self, storage, ksdata, instClass):
devicetree = storage.devicetree
@@ -813,15 +857,15 @@ class PartitionData(commands.partition.F17_PartData):
self.disk = disk
break
- if self.disk == "":
+ if not 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 self.recommended or self.hibernation:
+ self.size = swap.swapSuggestion(hibernation=self.hibernation)
+ self.grow = False
# if people want to specify no mountpoint for some reason, let them
# this is really needed for pSeries boot partitions :(
elif self.mountpoint == "None":
@@ -947,12 +991,12 @@ class PartitionData(commands.partition.F17_PartData):
should_clear = storage.shouldClear(disk)
if disk and (disk.partitioned or should_clear):
- kwargs["disks"] = [disk]
+ kwargs["parents"] = [disk]
break
elif disk:
raise KickstartValueError, formatErrorMsg(self.lineno, msg="Specified unpartitioned disk %s in partition command" % self.disk)
- if not kwargs["disks"]:
+ if not kwargs["parents"]:
raise KickstartValueError, formatErrorMsg(self.lineno, msg="Specified nonexistent disk %s in partition command" % self.disk)
kwargs["grow"] = self.grow
@@ -1021,6 +1065,11 @@ class PartitionData(commands.partition.F17_PartData):
parents=request)
storage.createDevice(luksdev)
+class Raid(commands.raid.F15_Raid):
+ def execute(self, storage, ksdata, instClass):
+ for r in self.raidList:
+ r.execute(storage, ksdata, instClass)
+
class RaidData(commands.raid.F15_RaidData):
def execute(self, storage, ksdata, instClass):
raidmems = []
@@ -1207,6 +1256,11 @@ class User(commands.user.F12_User):
if not users.createUser(usr.name, **kwargs):
log.error("User %s already exists, not creating." % usr.name)
+class VolGroup(commands.volgroup.FC16_VolGroup):
+ def execute(self, storage, ksdata, instClass):
+ for v in self.vgList:
+ v.execute(storage, ksdata, instClass)
+
class VolGroupData(commands.volgroup.FC16_VolGroupData):
def execute(self, storage, ksdata, instClass):
pvs = []
@@ -1247,7 +1301,7 @@ class VolGroupData(commands.volgroup.FC16_VolGroupData):
elif self.vgname in [vg.name for vg in storage.vgs]:
raise KickstartValueError(formatErrorMsg(self.lineno, msg="The volume group name \"%s\" is already in use." % self.vgname))
else:
- request = storage.newVG(pvs=pvs,
+ request = storage.newVG(parents=pvs,
name=self.vgname,
peSize=self.pesize/1024.0)
@@ -1287,7 +1341,10 @@ class Keyboard(commands.keyboard.F18_Keyboard):
# 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,
+ "btrfs": BTRFS,
"bootloader": Bootloader,
"clearpart": ClearPart,
"dmraid": DmRaid,
@@ -1299,11 +1356,16 @@ commandMap = {
"iscsiname": IscsiName,
"keyboard": Keyboard,
"logging": Logging,
+ "logvol": LogVol,
"multipath": MultiPath,
+ "part": Partition,
+ "partition": Partition,
+ "raid": Raid,
"rootpw": RootPw,
"services": Services,
"timezone": Timezone,
"user": User,
+ "volgroup": VolGroup,
"xconfig": XConfig,
"zfcp": ZFCP,
}
@@ -1468,15 +1530,15 @@ def selectPackages(ksdata, payload):
if errorHandler.cb(e) == ERROR_RAISE:
sys.exit(1)
- ksdata.packages.groupList.insert(0, Group("Core"))
+ ksdata.packages.groupList.insert(0, PackageGroup("Core"))
if ksdata.packages.addBase:
# Only add @base if it's not already in the group list. If the
# %packages section contains something like "@base --optional",
# addBase will take effect first and yum will think the group is
# already selected.
- if not Group("Base") in ksdata.packages.groupList:
- ksdata.packages.groupList.insert(1, Group("Base"))
+ if not PackageGroup("Base") in ksdata.packages.groupList:
+ ksdata.packages.groupList.insert(1, PackageGroup("Base"))
else:
log.warning("not adding Base group")
@@ -1506,3 +1568,17 @@ def selectPackages(ksdata, payload):
payload.deselectGroup(grp.name)
except NoSuchGroup:
continue
+
+def doKickstartStorage(storage, ksdata, instClass):
+ """ Setup storage state from the kickstart data """
+ ksdata.clearpart.execute(storage, ksdata, instClass)
+ ksdata.bootloader.execute(storage, ksdata, instClass)
+ ksdata.autopart.execute(storage, ksdata, instClass)
+ ksdata.partition.execute(storage, ksdata, instClass)
+ ksdata.raid.execute(storage, ksdata, instClass)
+ ksdata.volgroup.execute(storage, ksdata, instClass)
+ ksdata.logvol.execute(storage, ksdata, instClass)
+ ksdata.btrfs.execute(storage, ksdata, instClass)
+ storage.setUpBootLoader()
+ doPartitioning(storage)
+
diff --git a/pyanaconda/network.py b/pyanaconda/network.py
index 859c36818..22025ff51 100644
--- a/pyanaconda/network.py
+++ b/pyanaconda/network.py
@@ -39,6 +39,7 @@ from flags import flags
from simpleconfig import IfcfgFile
from pyanaconda.constants import ROOT_PATH
import urlgrabber.grabber
+from pyanaconda.storage.devices import FcoeDiskDevice, iScsiDiskDevice
import gettext
_ = lambda x: gettext.ldgettext("anaconda", x)
@@ -94,7 +95,7 @@ def sanityCheckHostname(hostname):
return None
# Try to determine what the hostname should be for this system
-def getDefaultHostname(anaconda):
+def getHostname():
resetResolver()
hn = None
@@ -114,14 +115,6 @@ def getDefaultHostname(anaconda):
hn = hinfo[0]
break
- if hn and hn not in ('(none)', 'localhost', 'localhost.localdomain'):
- return hn
-
- try:
- hn = anaconda.network.hostname
- except:
- hn = None
-
if not hn or hn in ('(none)', 'localhost', 'localhost.localdomain'):
hn = socket.gethostname()
@@ -213,7 +206,6 @@ class NetworkDevice(IfcfgFile):
def __init__(self, dir, iface):
IfcfgFile.__init__(self, dir, iface)
- self.description = ""
if iface.startswith('ctc'):
self.info["TYPE"] = "CTC"
self.wepkey = ""
@@ -340,27 +332,30 @@ class NetworkDevice(IfcfgFile):
f.close()
return content
- def usedByFCoE(self, anaconda):
- import storage
- for d in anaconda.storage.devices:
- if (isinstance(d, storage.devices.FcoeDiskDevice) and
- d.nic == self.iface):
- return True
- return False
+ def setGateway(self, gw):
+ if ':' in gw:
+ self.set(('IPV6_DEFAULTGW', gw))
+ else:
+ self.set(('GATEWAY', gw))
- def usedByRootOnISCSI(self, anaconda):
- import storage
- rootdev = anaconda.storage.rootDevice
- for d in anaconda.storage.devices:
- if (isinstance(d, storage.devices.iScsiDiskDevice) and
- rootdev.dependsOn(d)):
- if d.nic == "default":
- if self.iface == ifaceForHostIP(d.host_address):
- return True
- elif d.nic == self.iface:
- return True
+ def unsetDNS(self):
+ """Unset all DNS* ifcfg parameters."""
+ i = 1
+ while True:
+ if self.get("DNS%d" % i):
+ self.unset("DNS%d" %i)
+ else:
+ break
+ i += 1
- return False
+ def setDNS(self, ns):
+ dns = ns.split(',')
+ i = 1
+ for addr in dns:
+ addr = addr.strip()
+ dnslabel = "DNS%d" % (i,)
+ self.set((dnslabel, addr))
+ i += 1
class WirelessNetworkDevice(NetworkDevice):
@@ -374,7 +369,6 @@ class WirelessNetworkDevice(NetworkDevice):
self.info = dict()
self.iface = iface
self.dir = ""
- self.description = ""
def clear(self):
self.info = dict()
@@ -411,495 +405,153 @@ class Network:
def __init__(self):
- self.hostname = socket.gethostname()
-
- self.update()
-
- def update(self):
- ifcfglog.debug("Network.update() called")
-
- self.netdevices = {}
- self.ksdevice = None
+ ifcfglog.debug("Network object created called")
+ # TODO this may need to be handled in getDevices()
if flags.imageInstall:
return
+ # TODO this should go away (patch pending),
+ # default ifcfg files should be created in dracut
+
# populate self.netdevices
devhash = isys.getDeviceProperties(dev=None)
for iface in devhash.keys():
- if isys.isWirelessDevice(iface):
- device = WirelessNetworkDevice(iface)
- else:
+ if not isys.isWirelessDevice(iface):
device = NetworkDevice(netscriptsDir, iface)
- if os.access(device.path, os.R_OK):
- device.loadIfcfgFile()
- else:
+ if not os.access(device.path, os.R_OK):
device.setDefaultConfig()
- # TODORV - the last iface in loop wins, might be ok,
- # not worthy of special juggling
- if device.get('HOSTNAME'):
- self.hostname = device.get('HOSTNAME')
-
- device.description = isys.getNetDevDesc(iface)
-
- self.netdevices[iface] = device
-
-
- ksdevice = flags.cmdline.get('ksdevice', None)
- if ksdevice:
- bootif_mac = None
- if ksdevice == 'bootif' and "BOOTIF" in flags.cmdline:
- bootif_mac = flags.cmdline["BOOTIF"][3:].replace("-", ":").upper()
- # sort for ksdevice=link (to select the same device as in initrd))
- for dev in sorted(self.netdevices):
- mac = self.netdevices[dev].get('HWADDR').upper()
- if ksdevice == 'link' and isys.getLinkStatus(dev):
- self.ksdevice = dev
- break
- elif ksdevice == 'bootif':
- if bootif_mac == mac:
- self.ksdevice = dev
- break
- elif ksdevice == dev:
- self.ksdevice = dev
- break
- elif ':' in ksdevice:
- if ksdevice.upper() == mac:
- self.ksdevice = dev
- break
-
-
-
- def getDevice(self, device):
- return self.netdevices[device]
-
- def getKSDevice(self):
- if self.ksdevice is None:
- return None
-
- try:
- return self.netdevices[self.ksdevice]
- except:
- return None
-
- def setHostname(self, hn):
- self.hostname = hn
- if flags.imageInstall:
- log.info("image install -- not setting hostname")
- return
-
- log.info("setting installation environment hostname to %s" % hn)
- iutil.execWithRedirect("hostname", ["-v", hn ],
- stdout="/dev/tty5", stderr="/dev/tty5")
-
- def unsetDNS(self, devname):
- """Unset all DNS* ifcfg parameters."""
- i = 1
- dev = self.netdevices[devname]
- while True:
- if dev.get("DNS%d" % i):
- dev.unset("DNS%d" %i)
- else:
- break
- i += 1
-
- def setDNS(self, ns, device):
- dns = ns.split(',')
- i = 1
- for addr in dns:
- addr = addr.strip()
- dnslabel = "DNS%d" % (i,)
- self.netdevices[device].set((dnslabel, addr))
- i += 1
-
- def setGateway(self, gw, device):
- if ':' in gw:
- self.netdevices[device].set(('IPV6_DEFAULTGW', gw))
- else:
- self.netdevices[device].set(('GATEWAY', gw))
-
- @property
- def gateway(self):
- """GATEWAY - last device in list wins"""
- for dev in reversed(self.netdevices.values()):
- if (dev.get('GATEWAY') and
- dev.get('DEFROUTE') != "no"):
- return dev.get('GATEWAY')
- return ""
-
- @property
- def ipv6_defaultgw(self):
- """IPV6_DEFAULTGW - last device in list wins"""
- for dev in reversed(self.netdevices.values()):
- if (dev.get('IPV6_DEFAULTGW') and
- dev.get('DEFROUTE') != "no"):
- return dev.get('IPV6_DEFAULTGW')
- return ""
-
- def lookupHostname(self):
- # can't look things up if they don't exist!
- if not self.hostname or self.hostname == "localhost.localdomain":
- return None
-
- if not hasActiveNetDev():
- log.warning("no network devices were available to look up host name")
- return None
+def getDevices():
+ # TODO: filter with existence of ifcfg file?
+ return isys.getDeviceProperties().keys()
- try:
- (family, socktype, proto, canonname, sockaddr) = \
- socket.getaddrinfo(self.hostname, None, socket.AF_INET)[0]
- (ip, port) = sockaddr
- except:
- try:
- (family, socktype, proto, canonname, sockaddr) = \
- socket.getaddrinfo(self.hostname, None, socket.AF_INET6)[0]
- (ip, port, flowinfo, scopeid) = sockaddr
- except:
- return None
-
- return ip
-
- # Note that the file is written-out only if there is a value
- # that has changed.
- def writeIfcfgFiles(self):
- for device in self.netdevices.values():
- device.writeIfcfgFile()
-
- # devices == None => set for all
- def updateActiveDevices(self, devices=None):
- for devname, device in self.netdevices.items():
- if devices and devname not in devices:
- device.set(('ONBOOT', 'no'))
- else:
- device.set(('ONBOOT', 'yes'))
-
- def getOnbootControlledIfaces(self):
- ifaces = []
- for iface, device in self.netdevices.items():
- if (device.get('ONBOOT') == "yes" and
- device.get('NM_CONTROLLED') == "yes"):
- ifaces.append(iface)
- return ifaces
-
- def writeSSIDifcfgs(self, devssids):
- ssids = []
- for ssidlist in devssids.values():
- ssids.extend(ssidlist)
- for ssid in ssids:
- path = "{0}/ifcfg-{1}".format(netscriptsDir, ssid)
- ifcfgfile = open(path, "w")
- ifcfgfile.write("NAME={0}\n".format(ssid)+
- "TYPE=Wireless\n"+
- "ESSID={0}\n".format(ssid)+
- "NM_CONTROLLED=yes\n")
- ifcfgfile.close()
-
-
- def getSSIDs(self):
- return getSSIDs()
-
- def writeKS(self, f):
- devNames = self.netdevices.keys()
- devNames.sort()
-
- if len(devNames) == 0:
- return
-
- for devName in devNames:
- dev = self.netdevices[devName]
- line = "%s" % kickstartNetworkData(dev, self.hostname)
- f.write(line)
-
- def hasNameServers(self, hash):
- if hash.keys() == []:
- return False
-
- for key in hash.keys():
- if key.upper().startswith('DNS'):
- return True
+def waitForDevicesActivation(devices):
+ waited_devs_props = {}
- return False
+ bus = dbus.SystemBus()
+ nm = bus.get_object(isys.NM_SERVICE, isys.NM_MANAGER_PATH)
+ device_paths = nm.get_dbus_method("GetDevices")()
+ for device_path in device_paths:
+ device = bus.get_object(isys.NM_SERVICE, device_path)
+ device_props_iface = dbus.Interface(device, isys.DBUS_PROPS_IFACE)
+ iface = str(device_props_iface.Get(isys.NM_DEVICE_IFACE, "Interface"))
+ if iface in devices:
+ waited_devs_props[iface] = device_props_iface
+
+ i = 0
+ while True:
+ for dev, device_props_iface in waited_devs_props.items():
+ state = device_props_iface.Get(isys.NM_DEVICE_IFACE, "State")
+ if state == isys.NM_DEVICE_STATE_ACTIVATED:
+ waited_devs_props.pop(dev)
+ if len(waited_devs_props) == 0 or i >= CONNECTION_TIMEOUT:
+ break
+ i += 1
+ time.sleep(1)
+
+ return waited_devs_props.keys()
+
+# write out current configuration state and wait for NetworkManager
+# to bring the device up, watch NM state and return to the caller
+# once we have a state
+def waitForConnection():
+ bus = dbus.SystemBus()
+ nm = bus.get_object(isys.NM_SERVICE, isys.NM_MANAGER_PATH)
+ props = dbus.Interface(nm, isys.DBUS_PROPS_IFACE)
- def hasWirelessDev(self):
- for dev in self.netdevices:
- if isys.isWirelessDevice(dev):
- return True
- return False
+ i = 0
+ while i < CONNECTION_TIMEOUT:
+ state = props.Get(isys.NM_SERVICE, "State")
+ if nmIsConnected(state):
+ return True
+ i += 1
+ time.sleep(1)
- def _copyFileToPath(self, file, instPath='', overwrite=False):
- if not os.path.isfile(file):
- return False
- destfile = os.path.join(instPath, file.lstrip('/'))
- if (os.path.isfile(destfile) and not overwrite):
- return False
- if not os.path.isdir(os.path.dirname(destfile)):
- iutil.mkdirChain(os.path.dirname(destfile))
- shutil.copy(file, destfile)
+ state = props.Get(isys.NM_SERVICE, "State")
+ if nmIsConnected(state):
return True
- def _copyIfcfgFiles(self):
- files = os.listdir(netscriptsDir)
- for cfgFile in files:
- if cfgFile.startswith(("ifcfg-","keys-")):
- srcfile = os.path.join(netscriptsDir, cfgFile)
- self._copyFileToPath(srcfile, ROOT_PATH)
+ return False
- def copyConfigToPath(self):
- if flags.imageInstall:
- # for image installs we only want to write out
- # /etc/sysconfig/network
- destfile = os.path.normpath(ROOT_PATH + networkConfFile)
- if not os.path.isdir(os.path.dirname(destfile)):
- iutil.mkdirChain(os.path.dirname(destfile))
- shutil.move("/tmp/sysconfig-network", destfile)
- return
+# get a kernel cmdline string for dracut needed for access to storage host
+def dracutSetupArgs(networkStorageDevice):
- # /etc/sysconfig/network-scripts/ifcfg-*
- # /etc/sysconfig/network-scripts/keys-*
- # we can copy all of them
- self._copyIfcfgFiles()
+ if networkStorageDevice.nic == "default":
+ nic = ifaceForHostIP(networkStorageDevice.host_address)
+ if not nic:
+ return ""
+ else:
+ nic = networkStorageDevice.nic
- # /etc/dhcp/dhclient-DEVICE.conf
- # TODORV: do we really don't want overwrite on live cd?
- for devName, device in self.netdevices.items():
- dhclientfile = os.path.join("/etc/dhcp/dhclient-%s.conf" % devName)
- self._copyFileToPath(dhclientfile, ROOT_PATH)
+ if nic not in getDevices():
+ log.error('Unknown network interface: %s' % nic)
+ return ""
- # /etc/sysconfig/network
- self._copyFileToPath(networkConfFile, ROOT_PATH,
- overwrite=flags.livecdInstall)
-
- # /etc/resolv.conf
- self._copyFileToPath("/etc/resolv.conf", ROOT_PATH,
- overwrite=flags.livecdInstall)
-
- # /etc/udev/rules.d/70-persistent-net.rules
- self._copyFileToPath("/etc/udev/rules.d/70-persistent-net.rules",
- ROOT_PATH, overwrite=flags.livecdInstall)
-
- self._copyFileToPath(ipv6ConfFile, ROOT_PATH,
- overwrite=flags.livecdInstall)
-
- def disableNMForStorageDevices(self, anaconda):
- for devName, device in self.netdevices.items():
- if (device.usedByFCoE(anaconda) or
- device.usedByRootOnISCSI(anaconda)):
- dev = NetworkDevice(ROOT_PATH + netscriptsDir, devName)
- if os.access(dev.path, os.R_OK):
- dev.loadIfcfgFile()
- dev.set(('NM_CONTROLLED', 'no'))
- dev.writeIfcfgFile()
- log.info("network device %s used by storage will not be "
- "controlled by NM" % device.path)
- else:
- log.warning("disableNMForStorageDevices: %s file not found" %
- device.path)
-
- def autostartFCoEDevices(self, anaconda):
- for devName, device in self.netdevices.items():
- if device.usedByFCoE(anaconda):
- dev = NetworkDevice(ROOT_PATH + netscriptsDir, devName)
- if os.access(dev.path, os.R_OK):
- dev.loadIfcfgFile()
- dev.set(('ONBOOT', 'yes'))
- dev.writeIfcfgFile()
- log.debug("setting ONBOOT=yes for network device %s used by fcoe"
- % device.path)
+ ifcfg = NetworkDevice(netscriptsDir, nic)
+ ifcfg.loadIfcfgFile()
+ return dracutBootArguments(ifcfg,
+ networkStorageDevice.host_address,
+ getHostname())
+
+def dracutBootArguments(ifcfg, storage_ipaddr, hostname=None):
+
+ netargs = set()
+ devname = ifcfg.iface
+
+ if ifcfg.get('BOOTPROTO') == 'ibft':
+ netargs.add("ip=ibft")
+ elif storage_ipaddr:
+ if hostname is None:
+ hostname = ""
+ # if using ipv6
+ if ':' in storage_ipaddr:
+ if ifcfg.get('DHCPV6C') == "yes":
+ # XXX combination with autoconf not yet clear,
+ # support for dhcpv6 is not yet implemented in NM/ifcfg-rh
+ netargs.add("ip=%s:dhcp6" % devname)
+ elif ifcfg.get('IPV6_AUTOCONF') == "yes":
+ netargs.add("ip=%s:auto6" % devname)
+ elif ifcfg.get('IPV6ADDR'):
+ ipaddr = "[%s]" % ifcfg.get('IPV6ADDR')
+ if ifcfg.get('IPV6_DEFAULTGW'):
+ gateway = "[%s]" % ifcfg.get('IPV6_DEFAULTGW')
else:
- log.warning("autoconnectFCoEDevices: %s file not found" %
- device.path)
-
- def write(self):
- ifcfglog.debug("Network.write() called")
-
- # /etc/sysconfig/network
- if flags.imageInstall:
- # don't write files into host's /etc/sysconfig on image installs
- newnetwork = "/tmp/sysconfig-network"
- else:
- newnetwork = "%s.new" % (networkConfFile)
-
- f = open(newnetwork, "w")
- f.write("NETWORKING=yes\n")
- f.write("HOSTNAME=")
-
- # use instclass hostname if set(kickstart) to override
- if self.hostname:
- f.write(self.hostname + "\n")
- else:
- f.write("localhost.localdomain\n")
-
- if self.gateway:
- f.write("GATEWAY=%s\n" % self.gateway)
-
- if self.ipv6_defaultgw:
- f.write("IPV6_DEFAULTGW=%s\n" % self.ipv6_defaultgw)
-
- f.close()
- if flags.imageInstall:
- # for image installs, all we want to write out is the contents of
- # /etc/sysconfig/network
- ifcfglog.debug("not writing per-device configs for image install")
- return
+ gateway = ""
+ netargs.add("ip=%s::%s:%s:%s:%s:none" % (ipaddr, gateway,
+ ifcfg.get('PREFIX'), hostname, devname))
else:
- shutil.move(newnetwork, networkConfFile)
-
- devices = self.netdevices.values()
-
- # /etc/sysconfig/network-scripts/ifcfg-*
- # /etc/sysconfig/network-scripts/keys-*
- for dev in devices:
-
- dev.writeIfcfgFile()
-
- # /etc/resolv.conf is managed by NM
-
- # disable ipv6
- if ('noipv6' in flags.cmdline
- and not [dev for dev in devices
- if dev.get('IPV6INIT') == "yes"]):
- if os.path.exists(ipv6ConfFile):
- log.warning('Not disabling ipv6, %s exists' % ipv6ConfFile)
+ if ifcfg.get('bootproto').lower() == 'dhcp':
+ netargs.add("ip=%s:dhcp" % devname)
else:
- log.info('Disabling ipv6 on target system')
- f = open(ipv6ConfFile, "w")
- f.write("# Anaconda disabling ipv6\n")
- f.write("options ipv6 disable=1\n")
- f.close()
-
- def waitForDevicesActivation(self, devices):
- waited_devs_props = {}
-
- bus = dbus.SystemBus()
- nm = bus.get_object(isys.NM_SERVICE, isys.NM_MANAGER_PATH)
- device_paths = nm.get_dbus_method("GetDevices")()
- for device_path in device_paths:
- device = bus.get_object(isys.NM_SERVICE, device_path)
- device_props_iface = dbus.Interface(device, isys.DBUS_PROPS_IFACE)
- iface = str(device_props_iface.Get(isys.NM_DEVICE_IFACE, "Interface"))
- if iface in devices:
- waited_devs_props[iface] = device_props_iface
-
- i = 0
- while True:
- for dev, device_props_iface in waited_devs_props.items():
- state = device_props_iface.Get(isys.NM_DEVICE_IFACE, "State")
- if state == isys.NM_DEVICE_STATE_ACTIVATED:
- waited_devs_props.pop(dev)
- if len(waited_devs_props) == 0 or i >= CONNECTION_TIMEOUT:
- break
- i += 1
- time.sleep(1)
-
- return waited_devs_props.keys()
-
- # write out current configuration state and wait for NetworkManager
- # to bring the device up, watch NM state and return to the caller
- # once we have a state
- def waitForConnection(self):
- bus = dbus.SystemBus()
- nm = bus.get_object(isys.NM_SERVICE, isys.NM_MANAGER_PATH)
- props = dbus.Interface(nm, isys.DBUS_PROPS_IFACE)
-
- i = 0
- while i < CONNECTION_TIMEOUT:
- state = props.Get(isys.NM_SERVICE, "State")
- if nmIsConnected(state):
- return True
- i += 1
- time.sleep(1)
-
- state = props.Get(isys.NM_SERVICE, "State")
- if nmIsConnected(state):
- return True
-
- return False
-
- # write out current configuration state and wait for NetworkManager
- # to bring the device up, watch NM state and return to the caller
- # once we have a state
- def bringUp(self):
- self.write()
- if self.waitForConnection():
- resetResolver()
- return True
- else:
- return False
+ if ifcfg.get('GATEWAY'):
+ gateway = ifcfg.get('GATEWAY')
+ else:
+ gateway = ""
- # get a kernel cmdline string for dracut needed for access to host host
- def dracutSetupArgs(self, networkStorageDevice):
- netargs=set()
+ netmask = ifcfg.get('netmask')
+ prefix = ifcfg.get('prefix')
+ if not netmask and prefix:
+ netmask = isys.prefix2netmask(int(prefix))
- if networkStorageDevice.nic == "default":
- nic = ifaceForHostIP(networkStorageDevice.host_address)
- if not nic:
- return ""
- else:
- nic = networkStorageDevice.nic
+ netargs.add("ip=%s::%s:%s:%s:%s:none" % (ifcfg.get('ipaddr'),
+ gateway, netmask, hostname, devname))
- if nic not in self.netdevices.keys():
- log.error('Unknown network interface: %s' % nic)
- return ""
+ hwaddr = ifcfg.get("HWADDR")
+ if hwaddr:
+ netargs.add("ifname=%s:%s" % (devname, hwaddr.lower()))
- dev = self.netdevices[nic]
+ nettype = ifcfg.get("NETTYPE")
+ subchannels = ifcfg.get("SUBCHANNELS")
+ if iutil.isS390() and nettype and subchannels:
+ znet = "rd.znet=%s,%s" % (nettype, subchannels)
+ options = ifcfg.get("OPTIONS").strip("'\"")
+ if options:
+ options = filter(lambda x: x != '', options.split(' '))
+ znet += ",%s" % (','.join(options))
+ netargs.add(znet)
- if dev.get('BOOTPROTO') == 'ibft':
- netargs.add("ip=ibft")
- elif networkStorageDevice.host_address:
- if self.hostname:
- hostname = self.hostname
- else:
- hostname = ""
-
- # if using ipv6
- if ':' in networkStorageDevice.host_address:
- if dev.get('DHCPV6C') == "yes":
- # XXX combination with autoconf not yet clear,
- # support for dhcpv6 is not yet implemented in NM/ifcfg-rh
- netargs.add("ip=%s:dhcp6" % nic)
- elif dev.get('IPV6_AUTOCONF') == "yes":
- netargs.add("ip=%s:auto6" % nic)
- elif dev.get('IPV6ADDR'):
- ipaddr = "[%s]" % dev.get('IPV6ADDR')
- if dev.get('IPV6_DEFAULTGW'):
- gateway = "[%s]" % dev.get('IPV6_DEFAULTGW')
- else:
- gateway = ""
- netargs.add("ip=%s::%s:%s:%s:%s:none" % (ipaddr, gateway,
- dev.get('PREFIX'), hostname, nic))
- else:
- if dev.get('bootproto').lower() == 'dhcp':
- netargs.add("ip=%s:dhcp" % nic)
- else:
- if dev.get('GATEWAY'):
- gateway = dev.get('GATEWAY')
- else:
- gateway = ""
-
- netmask = dev.get('netmask')
- prefix = dev.get('prefix')
- if not netmask and prefix:
- netmask = isys.prefix2netmask(int(prefix))
-
- netargs.add("ip=%s::%s:%s:%s:%s:none" % (dev.get('ipaddr'),
- gateway, netmask, hostname, nic))
-
- hwaddr = dev.get("HWADDR")
- if hwaddr:
- netargs.add("ifname=%s:%s" % (nic, hwaddr.lower()))
-
- nettype = dev.get("NETTYPE")
- subchannels = dev.get("SUBCHANNELS")
- if iutil.isS390() and nettype and subchannels:
- znet = "rd.znet=%s,%s" % (nettype, subchannels)
- options = dev.get("OPTIONS").strip("'\"")
- if options:
- options = filter(lambda x: x != '', options.split(' '))
- znet += ",%s" % (','.join(options))
- netargs.add(znet)
-
- return netargs
+ return netargs
def kickstartNetworkData(ifcfg, hostname=None):
@@ -1037,3 +689,206 @@ def resetResolver():
isys.resetResolv()
urlgrabber.grabber.reset_curl_obj()
+def setHostname(hn):
+ if flags.imageInstall:
+ log.info("image install -- not setting hostname")
+ return
+
+ log.info("setting installation environment hostname to %s" % hn)
+ iutil.execWithRedirect("hostname", ["-v", hn ],
+ stdout="/dev/tty5", stderr="/dev/tty5")
+
+def _copyFileToPath(file, destPath='', overwrite=False):
+ if not os.path.isfile(file):
+ return False
+ destfile = os.path.join(destPath, file.lstrip('/'))
+ if (os.path.isfile(destfile) and not overwrite):
+ return False
+ if not os.path.isdir(os.path.dirname(destfile)):
+ iutil.mkdirChain(os.path.dirname(destfile))
+ shutil.copy(file, destfile)
+ return True
+
+def _copyIfcfgFiles(destPath):
+ files = os.listdir(netscriptsDir)
+ for cfgFile in files:
+ if cfgFile.startswith(("ifcfg-","keys-")):
+ srcfile = os.path.join(netscriptsDir, cfgFile)
+ _copyFileToPath(srcfile, destPath)
+
+def copyConfigToPath(destPath):
+ if flags.imageInstall:
+ # for image installs we only want to write out
+ # /etc/sysconfig/network
+ destfile = os.path.normpath(destPath + networkConfFile)
+ if not os.path.isdir(os.path.dirname(destfile)):
+ iutil.mkdirChain(os.path.dirname(destfile))
+ shutil.move("/tmp/sysconfig-network", destfile)
+ return
+
+ # /etc/sysconfig/network-scripts/ifcfg-*
+ # /etc/sysconfig/network-scripts/keys-*
+ # we can copy all of them
+ _copyIfcfgFiles(destPath)
+
+ # /etc/dhcp/dhclient-DEVICE.conf
+ # TODORV: do we really don't want overwrite on live cd?
+ for devName in getDevices():
+ dhclientfile = os.path.join("/etc/dhcp/dhclient-%s.conf" % devName)
+ _copyFileToPath(dhclientfile, destPath)
+
+ # /etc/sysconfig/network
+ _copyFileToPath(networkConfFile, destPath,
+ overwrite=flags.livecdInstall)
+
+ # /etc/resolv.conf
+ _copyFileToPath("/etc/resolv.conf", destPath,
+ overwrite=flags.livecdInstall)
+
+ # /etc/udev/rules.d/70-persistent-net.rules
+ _copyFileToPath("/etc/udev/rules.d/70-persistent-net.rules",
+ destPath, overwrite=flags.livecdInstall)
+
+ _copyFileToPath(ipv6ConfFile, destPath,
+ overwrite=flags.livecdInstall)
+
+def get_ksdevice_name(ksspec=""):
+
+ if not ksspec:
+ ksspec = flags.cmdline.get('ksdevice', "")
+ ksdevice = ksspec
+
+ bootif_mac = None
+ if ksdevice == 'bootif' and "BOOTIF" in flags.cmdline:
+ bootif_mac = flags.cmdline["BOOTIF"][3:].replace("-", ":").upper()
+ for dev in sorted(getDevices()):
+ # "eth0"
+ if ksdevice == dev:
+ break
+ # "link"
+ elif ksdevice == 'link' and isys.getLinkStatus(dev):
+ ksdevice = dev
+ break
+ # "XX:XX:XX:XX:XX:XX" (mac address)
+ elif ':' in ksdevice:
+ if ksdevice.upper() == isys.getMacAddress(dev):
+ ksdevice = dev
+ break
+ # "bootif" and BOOTIF==XX:XX:XX:XX:XX:XX
+ elif ksdevice == 'bootif':
+ if bootif_mac == isys.getMacAddress(dev):
+ ksdevice = dev
+ break
+
+ return ksdevice
+
+# note that NetworkDevice.get returns "" if key is not found
+def get_ifcfg_value(iface, key, root_path=""):
+ dev = NetworkDevice(os.path.normpath(root_path + netscriptsDir), iface)
+ dev.loadIfcfgFile()
+ return dev.get(key)
+
+def write_sysconfig_network():
+
+ if flags.imageInstall:
+ # don't write files into host's /etc/sysconfig on image installs
+ newnetwork = "/tmp/sysconfig-network"
+ else:
+ newnetwork = "%s.new" % (networkConfFile)
+
+ f = open(newnetwork, "w")
+ f.write("# Generated by anaconda")
+ f.write("NETWORKING=yes\n")
+ f.write("HOSTNAME=")
+
+ hostname = getHostname()
+ if hostname:
+ f.write(hostname + "\n")
+ else:
+ f.write("localhost.localdomain\n")
+
+ gateway = ipv6_defaultgw = None
+ for iface in reversed(getDevices()):
+ dev = NetworkDevice(netscriptsDir, iface)
+ dev.loadIfcfgFile()
+ if dev.get('DEFROUTE') != "no":
+ continue
+ if dev.get('GATEWAY'):
+ gateway = dev.get('GATEWAY')
+ if dev.get('IPV6_DEFAULTGW'):
+ ipv6_defaultgw = dev.get('IPV6_DEFAULTGW')
+ if gateway and ipv6_defaultgw:
+ break
+
+ if gateway:
+ f.write("GATEWAY=%s\n" % gateway)
+
+ if ipv6_defaultgw:
+ f.write("IPV6_DEFAULTGW=%s\n" % ipv6_defaultgw)
+ f.close()
+
+ if not flags.imageInstall:
+ shutil.move(newnetwork, networkConfFile)
+
+# TODO: do it right in sysroot (instead of copying the files later)?
+def disableIPV6():
+ if ('noipv6' in flags.cmdline
+ and not any(get_ifcfg_value(dev, 'IPV6INIT') == "yes"
+ for dev in getDevices())):
+ if os.path.exists(ipv6ConfFile):
+ log.warning('Not disabling ipv6, %s exists' % ipv6ConfFile)
+ else:
+ log.info('Disabling ipv6 on target system')
+ f = open(ipv6ConfFile, "w")
+ f.write("# Anaconda disabling ipv6\n")
+ f.write("options ipv6 disable=1\n")
+ f.close()
+
+def disableNMForStorageDevices(storage):
+ for devname in getDevices():
+ if (usedByFCoE(devname, storage) or
+ usedByRootOnISCSI(devname, storage)):
+ dev = NetworkDevice(ROOT_PATH + netscriptsDir, devname)
+ if os.access(dev.path, os.R_OK):
+ dev.loadIfcfgFile()
+ dev.set(('NM_CONTROLLED', 'no'))
+ dev.writeIfcfgFile()
+ log.info("network device %s used by storage will not be "
+ "controlled by NM" % devname)
+ else:
+ log.warning("disableNMForStorageDevices: ifcfg file for %s not found" %
+ devname)
+
+def autostartFCoEDevices(storage):
+ for devname in getDevices():
+ if usedByFCoE(devname, storage):
+ dev = NetworkDevice(ROOT_PATH + netscriptsDir, devname)
+ if os.access(dev.path, os.R_OK):
+ dev.loadIfcfgFile()
+ dev.set(('ONBOOT', 'yes'))
+ dev.writeIfcfgFile()
+ log.debug("setting ONBOOT=yes for network device %s used by fcoe"
+ % devname)
+ else:
+ log.warning("autoconnectFCoEDevices: ifcfg file for %s not found" %
+ devname)
+
+def usedByFCoE(iface, storage):
+ for d in storage.devices:
+ if (isinstance(d, FcoeDiskDevice) and
+ d.nic == iface):
+ return True
+ return False
+
+def usedByRootOnISCSI(iface, storage):
+ rootdev = storage.rootDevice
+ for d in storage.devices:
+ if (isinstance(d, iScsiDiskDevice) and
+ rootdev.dependsOn(d)):
+ if d.nic == "default":
+ if iface == ifaceForHostIP(d.host_address):
+ return True
+ elif d.nic == iface:
+ return True
+
+ return False
diff --git a/pyanaconda/packaging/__init__.py b/pyanaconda/packaging/__init__.py
index ca23f53a1..8c5a5606c 100644
--- a/pyanaconda/packaging/__init__.py
+++ b/pyanaconda/packaging/__init__.py
@@ -48,9 +48,7 @@ from pyanaconda.iutil import ProxyString, ProxyStringError
from pykickstart.parser import Group
import logging
-log = logging.getLogger("anaconda")
-
-from pyanaconda.backend_log import log as instlog
+log = logging.getLogger("packaging")
from pyanaconda.errors import *
#from pyanaconda.progress import progress
@@ -169,7 +167,9 @@ class Payload(object):
def _repoNeedsNetwork(self, repo):
""" Returns True if the ksdata repo requires networking. """
- urls = [repo.baseurl] + repo.mirrorlist
+ urls = [repo.baseurl]
+ if repo.mirrorlist:
+ urls.extend(repo.mirrorlist)
network_protocols = ["http:", "ftp:", "nfs:", "nfsiso:"]
for url in urls:
if any([url.startswith(p) for p in network_protocols]):
@@ -236,6 +236,9 @@ class Payload(object):
def groups(self):
raise NotImplementedError()
+ def languageGroups(self, lang):
+ raise NotImplementedError()
+
def description(self, groupid):
raise NotImplementedError()
@@ -466,34 +469,16 @@ class Payload(object):
###
### METHODS FOR INSTALLING THE PAYLOAD
###
- def preInstall(self, packages=None):
+ def preInstall(self, packages=None, groups=None):
""" Perform pre-installation tasks. """
iutil.mkdirChain(ROOT_PATH + "/root")
- if self.data.upgrade.upgrade:
- mode = "upgrade"
- else:
- mode = "install"
-
- log_file_name = "%s.log" % mode
- log_file_path = "%s/root/%s" % (ROOT_PATH, log_file_name)
- try:
- shutil.rmtree (log_file_path)
- except OSError:
- pass
-
- self.install_log = open(log_file_path, "w+")
-
- syslogname = "%s.syslog" % log_file_path
- try:
- shutil.rmtree (syslogname)
- except OSError:
- pass
- instlog.start(ROOT_PATH, syslogname)
-
- if packages is not None:
+ if packages:
map(self.selectPackage, packages)
+ if groups:
+ map(self.selectGroup, groups)
+
def install(self):
""" Install the payload. """
raise NotImplementedError()
@@ -593,10 +578,6 @@ class Payload(object):
self._copyDriverDiskFiles()
- # stop logger
- instlog.stop()
-
-
class ImagePayload(Payload):
""" An ImagePayload installs an OS image to the target system. """
def __init__(self, data):
diff --git a/pyanaconda/packaging/yumpayload.py b/pyanaconda/packaging/yumpayload.py
index 8aea2fb57..f6f940a44 100644
--- a/pyanaconda/packaging/yumpayload.py
+++ b/pyanaconda/packaging/yumpayload.py
@@ -42,9 +42,13 @@
import os
import shutil
import time
+import tempfile
from . import *
+import logging
+log = logging.getLogger("packaging")
+
try:
import rpm
except ImportError:
@@ -72,9 +76,6 @@ from pyanaconda.image import findFirstIsoImage
import gettext
_ = lambda x: gettext.ldgettext("anaconda", x)
-import logging
-log = logging.getLogger("anaconda")
-
from pyanaconda.errors import *
from pyanaconda.packaging import NoSuchGroup, NoSuchPackage
import pyanaconda.progress as progress
@@ -153,7 +154,7 @@ class YumPayload(PackagePayload):
# have group info ready.
self.gatherRepoMetadata()
- def _resetYum(self, root=None):
+ def _resetYum(self, root=None, keep_cache=False):
""" Delete and recreate the payload's YumBase instance. """
import shutil
if root is None:
@@ -161,10 +162,11 @@ class YumPayload(PackagePayload):
with _yum_lock:
if self._yum:
- for repo in self._yum.repos.listEnabled():
- if repo.name == BASE_REPO_NAME and \
- os.path.isdir(repo.cachedir):
- shutil.rmtree(repo.cachedir)
+ if not keep_cache:
+ for repo in self._yum.repos.listEnabled():
+ if repo.name == BASE_REPO_NAME and \
+ os.path.isdir(repo.cachedir):
+ shutil.rmtree(repo.cachedir)
del self._yum
@@ -287,7 +289,7 @@ reposdir=%s
releasever = self._yum.conf.yumvar['releasever']
self._writeYumConfig()
- self._resetYum(root=ROOT_PATH)
+ self._resetYum(root=ROOT_PATH, keep_cache=True)
log.debug("setting releasever to previous value of %s" % releasever)
self._yum.preconf.releasever = releasever
@@ -737,6 +739,16 @@ reposdir=%s
return groups
+ def languageGroups(self, lang):
+ groups = []
+ yum_groups = self._yumGroups
+
+ if yum_groups:
+ with _yum_lock:
+ groups = [g.groupid for g in yum_groups.get_groups() if g.langonly == lang]
+
+ return groups
+
def description(self, groupid):
""" Return name/description tuple for the group specified by id. """
groups = self._yumGroups
@@ -950,9 +962,9 @@ reposdir=%s
if not selected:
log.error("failed to select a kernel from %s" % kernels)
- def preInstall(self, packages=None):
+ def preInstall(self, packages=None, groups=None):
""" Perform pre-installation tasks. """
- super(YumPayload, self).preInstall(packages=packages)
+ super(YumPayload, self).preInstall(packages=packages, groups=groups)
progress.send_message(_("Starting package installation process"))
if self.install_device:
@@ -1001,12 +1013,13 @@ reposdir=%s
self._yum.ts.order()
self._yum.ts.clean()
- # set up rpm logging to go to our log
- self._yum.ts.ts.scriptFd = self.install_log.fileno()
- rpm.setLogFile(self.install_log)
+ # Write scriptlet output to a file to be logged later
+ script_log = tempfile.NamedTemporaryFile(delete=False)
+ self._yum.ts.ts.scriptFd = script_log.fileno()
+ rpm.setLogFile(script_log)
# create the install callback
- rpmcb = RPMCallback(self._yum, self.install_log,
+ rpmcb = RPMCallback(self._yum, script_log,
upgrade=self.data.upgrade.upgrade)
if flags.testing:
@@ -1037,12 +1050,19 @@ reposdir=%s
raise exn
else:
log.info("transaction complete")
- self.install_log.write("*** FINISHED INSTALLING PACKAGES ***")
progress.send_step()
finally:
- self.install_log.close()
self._yum.ts.close()
iutil.resetRpmDb()
+ script_log.close()
+
+ # log the contents of the scriptlet logfile
+ log.info("==== start rpm scriptlet logs ====")
+ with open(script_log.name) as f:
+ for l in f:
+ log.info(l)
+ log.info("==== end rpm scriptlet logs ====")
+ os.unlink(script_log.name)
def postInstall(self):
""" Perform post-installation tasks. """
@@ -1070,7 +1090,7 @@ reposdir=%s
class RPMCallback(object):
def __init__(self, yb, log, upgrade=False):
self._yum = yb # yum.YumBase
- self.install_log = log # file instance
+ self.install_log = log # logfile for yum script logs
self.upgrade = upgrade # boolean
self.package_file = None # file instance (package file management)
@@ -1132,9 +1152,10 @@ class RPMCallback(object):
log_msg = msg_format % (mode, txmbr.po,
self.completed_actions,
self.total_actions)
- self.install_log.write("%s %s\n" % (time.strftime("%H:%M:%S"),
- log_msg))
+ log.info(log_msg)
+ self.install_log.write(log_msg+"\n")
self.install_log.flush()
+
progress.send_message(progress_msg)
self.package_file = None
diff --git a/pyanaconda/rescue.py b/pyanaconda/rescue.py
index 5854e25a1..be84c1a2e 100644
--- a/pyanaconda/rescue.py
+++ b/pyanaconda/rescue.py
@@ -23,7 +23,6 @@
from snack import *
from constants import *
from textw.constants_text import *
-from textw.add_drive_text import addDriveDialog
from text import WaitWindow, OkCancelWindow, ProgressWindow, PassphraseEntryWindow, stepToClasses
from flags import flags
import sys
@@ -92,11 +91,11 @@ class RescueInterface(InstallInterfaceBase):
else:
return OkCancelWindow(self.screen, title, text)
- def enableNetwork(self, anaconda):
- if len(anaconda.network.netdevices) == 0:
+ def enableNetwork(self):
+ if len(network.getDevices()) == 0:
return False
from textw.netconfig_text import NetworkConfiguratorText
- w = NetworkConfiguratorText(self.screen, anaconda)
+ w = NetworkConfiguratorText(self.screen, self)
ret = w.run()
return ret != INSTALL_BACK
@@ -115,9 +114,6 @@ class RescueInterface(InstallInterfaceBase):
def resume(self):
pass
- def run(self, anaconda):
- self.anaconda = anaconda
-
def __init__(self):
InstallInterfaceBase.__init__(self)
self.screen = SnackScreen()
@@ -171,18 +167,6 @@ def makeResolvConf(instPath):
f.write(buf)
f.close()
-#
-# Write out something useful for networking and start interfaces
-#
-def startNetworking(network, intf):
- # do lo first
- if os.system("/usr/sbin/ifconfig lo 127.0.0.1"):
- log.error("Error trying to start lo in rescue.py::startNetworking()")
-
- # start up dhcp interfaces first
- if not network.bringUp():
- log.error("Error bringing up network interfaces")
-
def runShell(screen = None, msg=""):
if screen:
screen.suspend()
@@ -204,7 +188,7 @@ def runShell(screen = None, msg=""):
if os.path.exists("/usr/bin/firstaidkit-qs"):
proc = subprocess.Popen(["/usr/bin/firstaidkit-qs"])
proc.wait()
-
+
if proc is None or proc.returncode!=0:
if os.path.exists("/bin/bash"):
iutil.execConsole()
@@ -215,7 +199,9 @@ def runShell(screen = None, msg=""):
if screen:
screen.finish()
-def doRescue(anaconda):
+def doRescue(rescue_mount, ksdata, platform):
+ import storage
+
for file in [ "services", "protocols", "group", "joe", "man.config",
"nsswitch.conf", "selinux", "mke2fs.conf" ]:
try:
@@ -223,107 +209,82 @@ def doRescue(anaconda):
except OSError:
pass
+ intf = RescueInterface()
+
# see if they would like networking enabled
if not network.hasActiveNetDev():
+ rc = ButtonChoiceWindow(intf.screen, _("Setup Networking"),
+ _("Do you want to start the network interfaces on "
+ "this system?"), [_("Yes"), _("No")])
- while True:
- rc = ButtonChoiceWindow(anaconda.intf.screen, _("Setup Networking"),
- _("Do you want to start the network interfaces on "
- "this system?"), [_("Yes"), _("No")])
-
- if rc != _("No").lower():
- if not anaconda.intf.enableNetwork(anaconda):
- anaconda.intf.messageWindow(_("No Network Available"),
- _("Unable to activate a networking device. Networking "
- "will not be available in rescue mode."))
- break
-
- startNetworking(anaconda.network, anaconda.intf)
- break
- else:
- break
-
- # shutdown the interface now
- anaconda.intf.shutdown()
- anaconda.intf = None
+ if rc != _("No").lower():
+ if not intf.enableNetwork():
+ intf.messageWindow(_("No Network Available"),
+ _("Unable to activate a networking device. Networking "
+ "will not be available in rescue mode."))
# Early shell access with no disk access attempts
- if not anaconda.rescue_mount:
+ if not rescue_mount:
# the %post should be responsible for mounting all needed file systems
# NOTE: 1st script must be bash or simple python as nothing else might be available in the rescue image
- if anaconda.ksdata and anaconda.ksdata.scripts:
+ if flags.automatedInstall and ksdata.scripts:
from kickstart import runPostScripts
- runPostScripts(anaconda.ksdata.scripts)
+ runPostScripts(ksdata.scripts)
else:
runShell()
sys.exit(0)
- anaconda.intf = RescueInterface()
-
- if anaconda.ksdata:
- if anaconda.ksdata.rescue and anaconda.ksdata.rescue.romount:
- readOnly = 1
- else:
- readOnly = 0
+ if flags.automatedInstall:
+ readOnly = ksdata.rescue.romount
else:
# prompt to see if we should try and find root filesystem and mount
# everything in /etc/fstab on that root
while True:
- rc = ButtonChoiceWindow(anaconda.intf.screen, _("Rescue"),
+ rc = ButtonChoiceWindow(intf.screen, _("Rescue"),
_("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. If you want to proceed with this step choose "
"'Continue'. You can also choose to mount your file systems "
"read-only instead of read-write by choosing 'Read-Only'. "
- "If you need to activate SAN devices choose 'Advanced'."
"\n\n"
"If for some reason this process fails you can choose 'Skip' "
"and this step will be skipped and you will go directly to a "
"command shell.\n\n") % (ROOT_PATH,),
- [_("Continue"), _("Read-Only"), _("Skip"), _("Advanced")] )
+ [_("Continue"), _("Read-Only"), _("Skip")] )
if rc == _("Skip").lower():
- runShell(anaconda.intf.screen)
+ runShell(intf.screen)
sys.exit(0)
- elif rc == _("Advanced").lower():
- addDialog = addDriveDialog(anaconda)
- addDialog.addDriveDialog(anaconda.intf.screen)
- continue
- elif rc == _("Read-Only").lower():
- readOnly = 1
else:
- readOnly = 0
- break
+ readOnly = rc == _("Read-Only").lower()
- import storage
- storage.storageInitialize(anaconda)
+ break
- (disks, notUpgradable) = storage.findExistingRootDevices(anaconda, upgradeany=True)
+ sto = storage.Storage(ksdata, platform)
+ storage.storageInitialize(sto, ksdata, [])
+ roots = storage.findExistingInstallations(sto.devicetree)
- if not disks:
+ if not roots:
root = None
- elif (len(disks) == 1) or anaconda.ksdata:
- root = disks[0]
+ elif len(roots) == 1 or ksdata.upgrade.upgrade:
+ root = roots[0]
else:
- height = min (len (disks), 12)
+ height = min (len (roots), 12)
if height == 12:
scroll = 1
else:
scroll = 0
- devList = []
- for (device, relstr) in disks:
- if getattr(device.format, "label", None):
- devList.append("%s (%s) - %s" % (device.name, device.format.label, relstr))
- else:
- devList.append("%s - %s" % (device.name, relstr))
+ lst = []
+ for root in roots:
+ lst.append("%s" % root.name)
(button, choice) = \
- ListboxChoiceWindow(anaconda.intf.screen, _("System to Rescue"),
+ ListboxChoiceWindow(intf.screen, _("System to Rescue"),
_("Which device holds the root partition "
- "of your installation?"), devList,
+ "of your installation?"), lst,
[ _("OK"), _("Exit") ], width = 30,
scroll = scroll, height = height,
help = "multipleroot")
@@ -331,15 +292,15 @@ def doRescue(anaconda):
if button == _("Exit").lower():
root = None
else:
- root = disks[choice]
+ root = roots[choice]
- rootmounted = 0
+ rootmounted = False
if root:
try:
# TODO: add a callback to warn about dirty filesystems
- rc = mountExistingSystem(anaconda.storage.fsset, root,
- allowDirty = 1,
+ rc = mountExistingSystem(sto.fsset, root.device,
+ allowDirty = True,
readOnly = readOnly)
if not flags.imageInstall:
@@ -350,20 +311,20 @@ def doRescue(anaconda):
"when you are finished.") % ANACONDA_CLEANUP
if rc == -1:
- if anaconda.ksdata:
+ if flags.automatedInstall:
log.error("System had dirty file systems which you chose not to mount")
else:
- ButtonChoiceWindow(anaconda.intf.screen, _("Rescue"),
+ ButtonChoiceWindow(intf.screen, _("Rescue"),
_("Your system had dirty file systems which you chose not "
"to mount. Press return to get a shell from which "
"you can fsck and mount your partitions. %s") % msg,
[_("OK")], width = 50)
- rootmounted = 0
+ rootmounted = False
else:
- if anaconda.ksdata:
+ if flags.automatedInstall:
log.info("System has been mounted under: %s" % ROOT_PATH)
else:
- ButtonChoiceWindow(anaconda.intf.screen, _("Rescue"),
+ ButtonChoiceWindow(intf.screen, _("Rescue"),
_("Your system has been mounted under %(rootPath)s.\n\n"
"Press <return> to get a shell. If you would like to "
"make your system the root environment, run the command:\n\n"
@@ -371,12 +332,12 @@ def doRescue(anaconda):
{'rootPath': ROOT_PATH,
'msg': msg},
[_("OK")] )
- rootmounted = 1
+ rootmounted = True
# now turn on swap
if not readOnly:
try:
- anaconda.storage.turnOnSwap()
+ sto.turnOnSwap()
except StorageError:
log.error("Error enabling swap")
@@ -424,7 +385,7 @@ def doRescue(anaconda):
raise
except Exception as e:
log.error("doRescue caught exception: %s" % e)
- if anaconda.ksdata:
+ if flags.automatedInstall:
log.error("An error occurred trying to mount some or all of your system")
else:
if not flags.imageInstall:
@@ -434,16 +395,15 @@ def doRescue(anaconda):
msg = _("Run %s to unmount the system "
"when you are finished.") % ANACONDA_CLEANUP
- ButtonChoiceWindow(anaconda.intf.screen, _("Rescue"),
+ ButtonChoiceWindow(intf.screen, _("Rescue"),
_("An error occurred trying to mount some or all of your "
"system. Some of it may be mounted under %s.\n\n"
"Press <return> to get a shell.") % ROOT_PATH + msg,
[_("OK")] )
else:
- if anaconda.ksdata and \
- anaconda.ksdata.reboot.action in [KS_REBOOT, KS_SHUTDOWN]:
+ if flags.automatedInstall and ksdata.reboot.action in [KS_REBOOT, KS_SHUTDOWN]:
log.info("No Linux partitions found")
- anaconda.intf.screen.finish()
+ intf.screen.finish()
print(_("You don't have any Linux partitions. Rebooting.\n"))
sys.exit(0)
else:
@@ -452,7 +412,7 @@ def doRescue(anaconda):
"from the shell.")
else:
msg = ""
- ButtonChoiceWindow(anaconda.intf.screen, _("Rescue Mode"),
+ ButtonChoiceWindow(intf.screen, _("Rescue Mode"),
_("You don't have any Linux partitions. Press "
"return to get a shell.%s") % msg,
[ _("OK") ], width = 50)
@@ -460,28 +420,27 @@ def doRescue(anaconda):
msgStr = ""
if rootmounted and not readOnly:
- anaconda.storage.makeMtab()
+ sto.makeMtab()
try:
makeResolvConf(ROOT_PATH)
except (OSError, IOError) as e:
log.error("error making a resolv.conf: %s" %(e,))
msgStr = _("Your system is mounted under the %s directory.") % (ROOT_PATH,)
- ButtonChoiceWindow(anaconda.intf.screen, _("Rescue"), msgStr, [_("OK")] )
+ ButtonChoiceWindow(intf.screen, _("Rescue"), msgStr, [_("OK")] )
# we do not need ncurses anymore, shut them down
- anaconda.intf.shutdown()
+ intf.shutdown()
#create /etc/fstab in ramdisk, so it is easier to work with RO mounted filesystems
makeFStab()
# run %post if we've mounted everything
- if rootmounted and not readOnly and anaconda.ksdata:
+ if rootmounted and not readOnly and flags.automatedInstall:
from kickstart import runPostScripts
- runPostScripts(anaconda.ksdata.scripts)
+ runPostScripts(ksdata.scripts)
# start shell if reboot wasn't requested
- if not anaconda.ksdata or \
- not anaconda.ksdata.reboot.action in [KS_REBOOT, KS_SHUTDOWN]:
+ if not flags.automatedInstall or not ksdata.reboot.action in [KS_REBOOT, KS_SHUTDOWN]:
runShell(msg=msgStr)
sys.exit(0)
diff --git a/pyanaconda/security.py b/pyanaconda/security.py
index 50d92e298..2e9532373 100644
--- a/pyanaconda/security.py
+++ b/pyanaconda/security.py
@@ -19,7 +19,7 @@
# Author(s): Jeremy Katz <katzj@redhat.com>
#
-import iutil, shlex
+import iutil
from flags import flags
from pyanaconda.constants import ROOT_PATH
from pykickstart.constants import *
@@ -33,8 +33,6 @@ selinux_states = { SELINUX_DISABLED: "disabled",
class Security:
def __init__(self):
- self.auth = "--enableshadow --passalgo=sha512"
-
if flags.selinux == 1:
self.selinux = SELINUX_ENFORCING
else:
@@ -50,14 +48,6 @@ class Security:
def getSELinux(self):
return self.selinux
- def _addFingerprint(self):
- import rpm
-
- iutil.resetRpmDb()
- ts = rpm.TransactionSet(ROOT_PATH)
- # pylint: disable-msg=E1101
- return ts.dbMatch('provides', 'fprintd-pam').count()
-
def write(self):
args = []
@@ -65,22 +55,9 @@ class Security:
log.error("unknown selinux state: %s" %(self.selinux,))
return
- args = args + [ "--selinux=%s" %(selinux_states[self.selinux],) ]
-
try:
iutil.execWithRedirect("/usr/sbin/lokkit", args,
root=ROOT_PATH, stdout="/dev/null",
stderr="/dev/null")
except (RuntimeError, OSError) as msg:
log.error ("lokkit run failed: %s" %(msg,))
-
- args = ["--update", "--nostart"] + shlex.split(self.auth)
- if self._addFingerprint():
- args += ["--enablefingerprint"]
-
- try:
- iutil.execWithRedirect("/usr/sbin/authconfig", args,
- stdout = "/dev/tty5", stderr = "/dev/tty5",
- root=ROOT_PATH)
- except RuntimeError as msg:
- log.error("Error running %s: %s", args, msg)
diff --git a/pyanaconda/storage/__init__.py b/pyanaconda/storage/__init__.py
index 88038a17a..914096234 100644
--- a/pyanaconda/storage/__init__.py
+++ b/pyanaconda/storage/__init__.py
@@ -97,20 +97,15 @@ def storageInitialize(storage, ksdata, protected):
if not storage.disks:
raise NoDisksError
+ # kickstart uses all the disks
+ if flags.automatedInstall:
+ if not ksdata.ignoredisk.onlyuse:
+ ksdata.ignoredisk.onlyuse = [d.name for d in storage.disks \
+ if d.name not in ksdata.ignoredisk.ignoredisk]
+ log.debug("onlyuse is now: %s" % (",".join(ksdata.ignoredisk.onlyuse)))
+
# dispatch.py helper function
def storageComplete(anaconda):
- if anaconda.dir == DISPATCH_BACK:
- rc = anaconda.intf.messageWindow(_("Installation cannot continue."),
- _("The storage configuration you have "
- "chosen has already been activated. You "
- "can no longer return to the disk editing "
- "screen. Would you like to continue with "
- "the installation process?"),
- type = "yesno")
- if rc == 0:
- sys.exit(0)
- return DISPATCH_FORWARD
-
devs = anaconda.storage.devicetree.getDevicesByType("luks/dm-crypt")
existing_luks = False
new_luks = False
@@ -157,32 +152,6 @@ def storageComplete(anaconda):
if anaconda.ksdata:
return
- # Prevent users from installing on s390x with (a) no /boot volume, (b) the
- # root volume on LVM, and (c) the root volume not restricted to a single
- # PV
- # NOTE: There is not really a way for users to create a / volume
- # restricted to a single PV. The backend support is there, but there are
- # no UI hook-ups to drive that functionality, but I do not personally
- # care. --dcantrell
- if iutil.isS390() and \
- not anaconda.storage.mountpoints.has_key('/boot') and \
- anaconda.storage.mountpoints['/'].type == 'lvmlv' and \
- not anaconda.storage.mountpoints['/'].singlePV:
- rc = anaconda.intf.messageWindow(_("Missing /boot Volume"),
- _("This platform requires /boot on "
- "a dedicated partition or logical "
- "volume. If you do not want a "
- "/boot volume, you must place / "
- "on a dedicated non-LVM "
- "partition."),
- type="custom", custom_icon="error",
- custom_buttons=[_("Go _back"),
- _("_Exit installer")],
- default=0)
- if rc == 0:
- return DISPATCH_BACK
- sys.exit(1)
-
rc = anaconda.intf.messageWindow(_("Confirm"),
_("The partitioning options you have selected "
"will now be written to disk. Any "
@@ -241,8 +210,6 @@ def turnOnFilesystems(storage):
storage.write()
writeEscrowPackets(storage)
else:
- from pyanaconda.upgrade import bindMountDevDirectory
-
if upgrade_migrate:
# we should write out a new fstab with the migrated fstype
shutil.copyfile("%s/etc/fstab" % ROOT_PATH,
@@ -250,7 +217,10 @@ def turnOnFilesystems(storage):
storage.fsset.write()
# and make sure /dev is mounted so we can read the bootloader
- bindMountDevDirectory(ROOT_PATH)
+ getFormat("bind",
+ device="/dev",
+ mountpoint="/dev",
+ exists=True).mount(chroot=ROOT_PATH)
def writeEscrowPackets(storage):
escrowDevices = filter(lambda d: d.format.type == "luks" and \
@@ -534,6 +504,10 @@ class Storage(object):
for device in root.mounts.values() + root.swaps:
used_devices.extend(device.ancestors)
+ if getattr(device, "isLogical", False):
+ extended = device.disk.format.extendedPartition.path
+ used_devices.append(self.devicetree.getDeviceByPath(extended))
+
for new in [d for d in self.devicetree.leaves if not d.exists]:
if new in self.swaps or getattr(new.format, "mountpoint", None):
used_devices.extend(new.ancestors)
@@ -1111,13 +1085,6 @@ class Storage(object):
None),
**kwargs.pop("fmt_args", {}))
- if kwargs.has_key("disks"):
- parents = kwargs.pop("disks")
- if isinstance(parents, Device):
- kwargs["parents"] = [parents]
- else:
- kwargs["parents"] = parents
-
if kwargs.has_key("name"):
name = kwargs.pop("name")
else:
@@ -1146,7 +1113,7 @@ class Storage(object):
def newVG(self, *args, **kwargs):
""" Return a new LVMVolumeGroupDevice instance. """
- pvs = kwargs.pop("pvs", [])
+ pvs = kwargs.pop("parents", [])
for pv in pvs:
if pv not in self.devices:
raise ValueError("pv is not in the device tree")
@@ -1170,9 +1137,7 @@ class Storage(object):
def newLV(self, *args, **kwargs):
""" Return a new LVMLogicalVolumeDevice instance. """
- if kwargs.has_key("vg"):
- vg = kwargs.pop("vg")
-
+ vg = kwargs.get("parents", [None])[0]
mountpoint = kwargs.pop("mountpoint", None)
if kwargs.has_key("fmt_type"):
kwargs["format"] = getFormat(kwargs.pop("fmt_type"),
@@ -1193,7 +1158,7 @@ class Storage(object):
if name in self.names:
raise ValueError("name already in use")
- return LVMLogicalVolumeDevice(name, vg, *args, **kwargs)
+ return LVMLogicalVolumeDevice(name, *args, **kwargs)
def newBTRFS(self, *args, **kwargs):
""" Return a new BTRFSVolumeDevice or BRFSSubVolumeDevice. """
@@ -1470,6 +1435,21 @@ class Storage(object):
"megabytes which is usually too small to "
"install %s.") % (productName,))
+ # Prevent users from installing on s390x with (a) no /boot volume, (b) the
+ # root volume on LVM, and (c) the root volume not restricted to a single
+ # PV
+ # NOTE: There is not really a way for users to create a / volume
+ # restricted to a single PV. The backend support is there, but there are
+ # no UI hook-ups to drive that functionality, but I do not personally
+ # care. --dcantrell
+ if iutil.isS390() and \
+ not self.mountpoints.has_key('/boot') and \
+ root.type == 'lvmlv' and not root.singlePV:
+ errors.append(_("This platform requires /boot on a dedicated "
+ "partition or logical volume. If you do not "
+ "want a /boot volume, you must place / on a "
+ "dedicated non-LVM partition."))
+
# FIXME: put a check here for enough space on the filesystems. maybe?
# livecds have to have the rootfs type match up
@@ -1867,11 +1847,10 @@ class Storage(object):
return 0
-def mountExistingSystem(fsset, rootEnt,
+def mountExistingSystem(fsset, rootDevice,
allowDirty=None, dirtyCB=None,
readOnly=None):
""" Mount filesystems specified in rootDevice's /etc/fstab file. """
- rootDevice = rootEnt[0]
rootPath = ROOT_PATH
if dirtyCB is None:
dirtyCB = lambda l: False
@@ -2699,8 +2678,9 @@ def findExistingInstallations(devicetree):
log.warning("setup of %s failed: %s" % (device.name, e))
continue
+ options = device.format.options + ",ro"
try:
- device.format.mount(options="ro", mountpoint=ROOT_PATH)
+ device.format.mount(options=options, mountpoint=ROOT_PATH)
except Exception as e:
log.warning("mount of %s as %s failed: %s" % (device.name,
device.format.type,
@@ -2720,8 +2700,11 @@ def findExistingInstallations(devicetree):
name = "%s Linux %s for %s" % (product, version, arch)
(mounts, swaps) = parseFSTab(devicetree, chroot=ROOT_PATH)
- roots.append(Root(mounts=mounts, swaps=swaps, name=name))
device.teardown()
+ if not mounts and not swaps:
+ # empty /etc/fstab. weird, but I've seen it happen.
+ continue
+ roots.append(Root(mounts=mounts, swaps=swaps, name=name))
return roots
@@ -2782,17 +2765,18 @@ def parseFSTab(devicetree, chroot=None):
for line in f.readlines():
# strip off comments
(line, pound, comment) = line.partition("#")
- fields = line.split(None, 3)
+ fields = line.split(None, 4)
- if len(fields) < 4:
+ if len(fields) < 5:
continue
- (devspec, mountpoint, fstype, rest) = fields
+ (devspec, mountpoint, fstype, options, rest) = fields
# find device in the tree
device = devicetree.resolveDevice(devspec,
cryptTab=cryptTab,
- blkidTab=blkidTab)
+ blkidTab=blkidTab,
+ options=options)
if device is None:
continue
@@ -2804,8 +2788,3 @@ def parseFSTab(devicetree, chroot=None):
return (mounts, swaps)
-def doKickstartStorage(storage, ksdata, instclass, checker):
- ksdata.clearpart.execute(storage, ksdata, instclass)
- ksdata.bootloader.execute(storage, ksdata, instclass)
- ksdata.autopart.execute(storage, ksdata, instclass)
- checker.run()
diff --git a/pyanaconda/storage/deviceaction.py b/pyanaconda/storage/deviceaction.py
index b16573ad4..617d8b299 100644
--- a/pyanaconda/storage/deviceaction.py
+++ b/pyanaconda/storage/deviceaction.py
@@ -288,7 +288,10 @@ class ActionDestroyDevice(DeviceAction):
# Make sure libparted does not keep cached info for this device
# and returns it when we create a new device with the same name
if self.device.partedDevice:
- self.device.partedDevice.removeFromCache()
+ try:
+ self.device.partedDevice.removeFromCache()
+ except Exception:
+ pass
def requires(self, action):
""" Return True if self requires action.
diff --git a/pyanaconda/storage/devicelibs/crypto.py b/pyanaconda/storage/devicelibs/crypto.py
index ee573229d..35c813f84 100644
--- a/pyanaconda/storage/devicelibs/crypto.py
+++ b/pyanaconda/storage/devicelibs/crypto.py
@@ -29,6 +29,8 @@ from ..errors import *
import gettext
_ = lambda x: gettext.ldgettext("anaconda", x)
+LUKS_METADATA_SIZE = 2.0 # MB
+
# Keep the character set size a power of two to make sure all characters are
# equally likely
GENERATED_PASSPHRASE_CHARSET = ("0123456789"
diff --git a/pyanaconda/storage/devicelibs/swap.py b/pyanaconda/storage/devicelibs/swap.py
index 274b4d03a..5113787c3 100644
--- a/pyanaconda/storage/devicelibs/swap.py
+++ b/pyanaconda/storage/devicelibs/swap.py
@@ -31,6 +31,8 @@ from . import dm
import gettext
_ = lambda x: gettext.ldgettext("anaconda", x)
+import logging
+log = logging.getLogger("anaconda")
def mkswap(device, label=''):
# We use -f to force since mkswap tends to refuse creation on lvs with
@@ -122,3 +124,42 @@ def swapstatus(device):
return status
+def swapSuggestion(quiet=False, hibernation=False):
+ """
+ Suggest the size of the swap partition that will be created.
+
+ @param quiet: log size information
+ @param hibernation: calculate swap size big enough for hibernation
+ @return: calculated swap size
+
+ """
+
+ mem = iutil.memInstalled()/1024
+ mem = ((mem/16)+1)*16
+ if not quiet:
+ log.info("Detected %sM of memory", mem)
+
+ #chart suggested in the discussion with other teams
+ if mem < 2048:
+ swap = 2 * mem
+
+ elif 2048 <= mem < 8192:
+ swap = mem
+
+ elif 8192 <= mem < 65536:
+ swap = mem / 2
+
+ else:
+ swap = 4096
+
+ if hibernation:
+ if mem <= 65536:
+ swap = mem + swap
+ else:
+ log.info("Ignoring --hibernation option on systems with 64 GB of RAM or more")
+
+ if not quiet:
+ log.info("Swap attempt of %sM", swap)
+
+ return swap
+
diff --git a/pyanaconda/storage/devices.py b/pyanaconda/storage/devices.py
index 6b979e850..8fef3d168 100644
--- a/pyanaconda/storage/devices.py
+++ b/pyanaconda/storage/devices.py
@@ -105,6 +105,7 @@ from devicelibs import lvm
from devicelibs import dm
from devicelibs import loop
from devicelibs import btrfs
+from devicelibs import crypto
import parted
import _ped
import block
@@ -1934,8 +1935,7 @@ class LUKSDevice(DMCryptDevice):
@property
def size(self):
if not self.exists or not self.partedDevice:
- # the LUKS metadata area is 2MB
- size = float(self.slave.size) - 2.0
+ size = float(self.slave.size) - crypto.LUKS_METADATA_SIZE
else:
size = self.partedDevice.getSize()
return size
@@ -1964,7 +1964,7 @@ class LVMVolumeGroupDevice(DMDevice):
_type = "lvmvg"
_packages = ["lvm2"]
- def __init__(self, name, parents, size=None, free=None,
+ def __init__(self, name, parents=None, size=None, free=None,
peSize=None, peCount=None, peFree=None, pvCount=None,
uuid=None, exists=False, sysfsPath=''):
""" Create a LVMVolumeGroupDevice instance.
@@ -2387,7 +2387,7 @@ class LVMLogicalVolumeDevice(DMDevice):
_resizable = True
_packages = ["lvm2"]
- def __init__(self, name, vgdev, size=None, uuid=None,
+ def __init__(self, name, parents=None, size=None, uuid=None,
stripes=1, logSize=0, snapshotSpace=0,
format=None, exists=False, sysfsPath='',
grow=None, maxsize=None, percent=None,
@@ -2418,15 +2418,15 @@ class LVMLogicalVolumeDevice(DMDevice):
percent -- percent of VG space to take
"""
- if isinstance(vgdev, list):
- if len(vgdev) != 1:
+ if isinstance(parents, list):
+ if len(parents) != 1:
raise ValueError("constructor requires a single LVMVolumeGroupDevice instance")
- elif not isinstance(vgdev[0], LVMVolumeGroupDevice):
+ elif not isinstance(parents[0], LVMVolumeGroupDevice):
raise ValueError("constructor requires a LVMVolumeGroupDevice instance")
- elif not isinstance(vgdev, LVMVolumeGroupDevice):
+ elif not isinstance(parents, LVMVolumeGroupDevice):
raise ValueError("constructor requires a LVMVolumeGroupDevice instance")
DMDevice.__init__(self, name, size=size, format=format,
- sysfsPath=sysfsPath, parents=vgdev,
+ sysfsPath=sysfsPath, parents=parents,
exists=exists)
self.singlePVerr = ("%(mountpoint)s is restricted to a single "
@@ -3642,19 +3642,36 @@ class iScsiDiskDevice(DiskDevice, NetworkStorageDevice):
self.ibft = kwargs.pop("ibft")
self.nic = kwargs.pop("nic")
self.initiator = kwargs.pop("initiator")
- DiskDevice.__init__(self, device, **kwargs)
- NetworkStorageDevice.__init__(self, host_address=self.node.address,
- nic=self.nic)
- log.debug("created new iscsi disk %s %s:%d via %s:%s" % (self.node.name,
- self.node.address,
- self.node.port,
- self.node.iface,
- self.nic))
+
+ if self.node is None:
+ # qla4xxx partial offload
+ name = kwargs.pop("fw_name")
+ address = kwargs.pop("fw_address")
+ port = kwargs.pop("fw_port")
+ DiskDevice.__init__(self, device, **kwargs)
+ NetworkStorageDevice.__init__(self,
+ host_address=address,
+ nic=self.nic)
+ log.debug("created new iscsi disk %s %s:%s using fw initiator %s"
+ % (name, address, port, self.initiator))
+ else:
+ DiskDevice.__init__(self, device, **kwargs)
+ NetworkStorageDevice.__init__(self, host_address=self.node.address,
+ nic=self.nic)
+ log.debug("created new iscsi disk %s %s:%d via %s:%s" % (self.node.name,
+ self.node.address,
+ self.node.port,
+ self.node.iface,
+ self.nic))
def dracutSetupArgs(self):
if self.ibft:
return set(["iscsi_firmware"])
+ # qla4xxx partial offload
+ if self.node is None:
+ return set()
+
address = self.node.address
# surround ipv6 addresses with []
if ":" in address:
@@ -3943,19 +3960,31 @@ class BTRFSDevice(StorageDevice):
def _temp_dir_prefix(self):
return "btrfs-tmp.%s" % self.id
- def _do_temp_mount(self):
+ def _do_temp_mount(self, orig=False):
if self.format.status or not self.exists:
return
tmpdir = tempfile.mkdtemp(prefix=self._temp_dir_prefix)
- self.format.mount(mountpoint=tmpdir)
+ if orig:
+ fmt = self.originalFormat
+ else:
+ fmt = self.format
+
+ fmt.mount(mountpoint=tmpdir)
def _undo_temp_mount(self):
- if self.format.status:
- mountpoint = self.format._mountpoint
- if os.path.basename(mountpoint).startswith(self._temp_dir_prefix):
- self.format.unmount()
- os.rmdir(mountpoint)
+ if getattr(self.format, "_mountpoint", None):
+ fmt = self.format
+ elif getattr(self.originalFormat, "_mountpoint", None):
+ fmt = self.originalFormat
+ else:
+ return
+
+ mountpoint = fmt._mountpoint
+
+ if os.path.basename(mountpoint).startswith(self._temp_dir_prefix):
+ fmt.unmount()
+ os.rmdir(mountpoint)
@property
def path(self):
@@ -3990,6 +4019,7 @@ class BTRFSVolumeDevice(BTRFSDevice):
label=label,
volUUID=self.uuid,
device=self.path)
+ self.originalFormat = self.format
label = getattr(self.format, "label", None)
if label:
@@ -4058,7 +4088,7 @@ class BTRFSVolumeDevice(BTRFSDevice):
subvols = []
self.setup(orig=True)
try:
- self._do_temp_mount()
+ self._do_temp_mount(orig=True)
except FSError as e:
log.debug("btrfs temp mount failed: %s" % e)
return subvols
@@ -4127,10 +4157,10 @@ class BTRFSSubVolumeDevice(BTRFSDevice):
def _destroy(self):
log_method_call(self, self.name, status=self.status)
- self.volume._do_temp_mount()
- mountpoint = self.volume.format._mountpoint
+ self.volume._do_temp_mount(orig=True)
+ mountpoint = self.volume.originalFormat._mountpoint
if not mountpoint:
raise RuntimeError("btrfs subvol destroy requires mounted volume")
btrfs.delete_subvolume(mountpoint, self.name)
- self.volume._removeSubVolume()
+ self.volume._removeSubVolume(self.name)
self.volume._undo_temp_mount()
diff --git a/pyanaconda/storage/devicetree.py b/pyanaconda/storage/devicetree.py
index ae69be2b8..df0d15f81 100644
--- a/pyanaconda/storage/devicetree.py
+++ b/pyanaconda/storage/devicetree.py
@@ -354,6 +354,7 @@ class DeviceTree(object):
# don't include "req%d" partition names
if ((newdev.type != "partition" or
not newdev.name.startswith("req")) and
+ newdev.type != "btrfs volume" and
newdev.name not in self.names):
self.names.append(newdev.name)
log.info("added %s %s (id %d) to device tree" % (newdev.type,
@@ -598,6 +599,20 @@ class DeviceTree(object):
self.addIgnoredDisk(name)
return True
+ # Ignore any readonly disks
+ if (udev_device_is_disk(info) and not
+ (udev_device_is_cdrom(info) or
+ udev_device_is_partition(info) or
+ udev_device_is_dm_partition(info) or
+ udev_device_is_dm_lvm(info) or
+ udev_device_is_dm_crypt(info) or
+ (udev_device_is_md(info) and not
+ udev_device_get_md_container(info)))):
+ if iutil.get_sysfs_attr(info["sysfs_path"], 'ro') == '1':
+ log.debug("Ignoring read only device %s" % name)
+ self.addIgnoredDisk(name)
+ return True
+
# FIXME: check for virtual devices whose slaves are on the ignore list
def addUdevLVDevice(self, info):
@@ -845,16 +860,26 @@ class DeviceTree(object):
kwargs = { "serial": serial, "vendor": vendor, "bus": bus }
if udev_device_is_iscsi(info):
diskType = iScsiDiskDevice
- node = self.iscsi.getNode(
- udev_device_get_iscsi_name(info),
- udev_device_get_iscsi_address(info),
- udev_device_get_iscsi_port(info),
- udev_device_get_iscsi_nic(info))
- kwargs["node"] = node
- kwargs["nic"] = self.iscsi.ifaces.get(node.iface, node.iface)
- kwargs["ibft"] = node in self.iscsi.ibftNodes
- kwargs["initiator"] = self.iscsi.initiator
- log.info("%s is an iscsi disk" % name)
+ initiator = udev_device_get_iscsi_initiator(info)
+ target = udev_device_get_iscsi_name(info)
+ address = udev_device_get_iscsi_address(info)
+ port = udev_device_get_iscsi_port(info)
+ nic = udev_device_get_iscsi_nic(info)
+ kwargs["initiator"] = initiator
+ if initiator == self.iscsi.initiator:
+ node = self.iscsi.getNode(target, address, port, nic)
+ kwargs["node"] = node
+ kwargs["ibft"] = node in self.iscsi.ibftNodes
+ kwargs["nic"] = self.iscsi.ifaces.get(node.iface, node.iface)
+ log.info("%s is an iscsi disk" % name)
+ else:
+ # qla4xxx partial offload
+ kwargs["node"] = None
+ kwargs["ibft"] = False
+ kwargs["nic"] = "offload:not_accessible_via_iscsiadm"
+ kwargs["fw_address"] = address
+ kwargs["fw_port"] = port
+ kwargs["fw_name"] = name
elif udev_device_is_fcoe(info):
diskType = FcoeDiskDevice
kwargs["nic"] = udev_device_get_fcoe_nic(info)
@@ -1529,11 +1554,12 @@ class DeviceTree(object):
if vol_path in [sv.name for sv in btrfs_dev.subvolumes]:
continue
fmt = getFormat("btrfs", device=btrfs_dev.path, exists=True,
- mountopts="subvol=%d" % vol_id)
+ mountopts="subvol=%s" % vol_path)
subvol = BTRFSSubVolumeDevice(vol_path,
vol_id=vol_id,
format=fmt,
- parents=[btrfs_dev])
+ parents=[btrfs_dev],
+ exists=True)
self._addDevice(subvol)
def handleUdevDeviceFormat(self, info, device):
@@ -2148,7 +2174,7 @@ class DeviceTree(object):
""" Return a list of a device's children. """
return [c for c in self._devices if device in c.parents]
- def resolveDevice(self, devspec, blkidTab=None, cryptTab=None):
+ def resolveDevice(self, devspec, blkidTab=None, cryptTab=None, options=None):
# find device in the tree
device = None
if devspec.startswith("UUID="):
@@ -2232,6 +2258,22 @@ class DeviceTree(object):
lv = "%s-%s" % (vg_name, lv_name)
device = self.getDeviceByName(lv)
+ # check mount options for btrfs volumes in case it's a subvol
+ if device and device.type == "btrfs volume" and options:
+ attr = None
+ if "subvol=" in options:
+ attr = "name"
+ val = iutil.get_option_value("subvol", options)
+ elif "subvolid=" in options:
+ attr = "vol_id"
+ val = iutil.get_option_value("subvolid", options)
+
+ if attr and val:
+ for subvol in device.subvolumes:
+ if getattr(subvol, attr, None) == val:
+ device = subvol
+ break
+
if device:
log.debug("resolved '%s' to '%s' (%s)" % (devspec, device.name, device.type))
else:
diff --git a/pyanaconda/storage/fcoe.py b/pyanaconda/storage/fcoe.py
index d263dcf80..fc2ad4017 100644
--- a/pyanaconda/storage/fcoe.py
+++ b/pyanaconda/storage/fcoe.py
@@ -122,14 +122,16 @@ class fcoe(object):
iutil.execWithRedirect("dcbtool", [ "sc", nic, "app:fcoe",
"e:1", "a:1", "w:1" ],
stdout = "/dev/tty5", stderr="/dev/tty5")
- iutil.execWithRedirect("fipvlan", [ nic, "-c", "-s" ],
+ iutil.execWithRedirect("fipvlan", [ "-c", "-s", "-f",
+ "'-fcoe'", nic],
stdout = "/dev/tty5", stderr="/dev/tty5")
else:
if auto_vlan:
# certain network configrations require the VLAN layer module:
iutil.execWithRedirect("modprobe", ["8021q"],
stdout = "/dev/tty5", stderr="/dev/tty5")
- iutil.execWithRedirect("fipvlan", ['-c', '-s', nic],
+ iutil.execWithRedirect("fipvlan", ['-c', '-s', '-f',
+ "'-fcoe'", nic],
stdout = "/dev/tty5", stderr="/dev/tty5")
else:
f = open("/sys/module/libfcoe/parameters/create", "w")
diff --git a/pyanaconda/storage/formats/fs.py b/pyanaconda/storage/formats/fs.py
index 248dd9c3f..eaf2405d3 100644
--- a/pyanaconda/storage/formats/fs.py
+++ b/pyanaconda/storage/formats/fs.py
@@ -408,6 +408,7 @@ class FS(DeviceFormat):
# the other option is to actually replace this instance with an
# instance of the new filesystem type.
self._type = self.migrationTarget
+ self._mountType = self.migrationTarget
@property
def resizeArgs(self):
@@ -1123,6 +1124,10 @@ class BTRFS(FS):
# filesystem creation is done in storage.devicelibs.btrfs.create_volume
pass
+ def destroy(self, *args, **kwargs):
+ # filesystem creation is done in storage.devicelibs.btrfs.delete_volume
+ pass
+
def setup(self, *args, **kwargs):
log_method_call(self, type=self.mountType, device=self.device,
mountpoint=self.mountpoint)
diff --git a/pyanaconda/storage/formats/swap.py b/pyanaconda/storage/formats/swap.py
index 42458d595..e961afe49 100644
--- a/pyanaconda/storage/formats/swap.py
+++ b/pyanaconda/storage/formats/swap.py
@@ -45,6 +45,9 @@ class SwapSpace(DeviceFormat):
_supported = True # is supported
_linuxNative = True # for clearpart
+ #see rhbz#744129 for details
+ _maxSize = 128 * 1024
+
def __init__(self, *args, **kwargs):
""" Create a SwapSpace instance.
diff --git a/pyanaconda/storage/partitioning.py b/pyanaconda/storage/partitioning.py
index 74eb68537..e9c06f6d0 100644
--- a/pyanaconda/storage/partitioning.py
+++ b/pyanaconda/storage/partitioning.py
@@ -75,7 +75,8 @@ def _scheduleImplicitPartitions(storage, disks):
for disk in disks:
if storage.encryptedAutoPart:
fmt_type = "luks"
- fmt_args = {"escrow_cert": storage.autoPartEscrowCert,
+ fmt_args = {"passphrase": storage.encryptionPassphrase,
+ "escrow_cert": storage.autoPartEscrowCert,
"add_backup_passphrase": storage.autoPartAddBackupPassphrase}
else:
if storage.autoPartType == AUTOPART_TYPE_LVM:
@@ -86,7 +87,7 @@ def _scheduleImplicitPartitions(storage, disks):
part = storage.newPartition(fmt_type=fmt_type,
fmt_args=fmt_args,
grow=True,
- disks=[disk])
+ parents=[disk])
storage.createDevice(part)
devs.append(part)
@@ -156,16 +157,21 @@ def _schedulePartitions(storage, disks):
request.fstype = storage.liveImage.format.type
if request.encrypted and storage.encryptedAutoPart:
- fstype = "luks"
+ fmt_type = "luks"
+ fmt_args = {"passphrase": storage.encryptionPassphrase,
+ "escrow_cert": storage.autoPartEscrowCert,
+ "add_backup_passphrase": storage.autoPartAddBackupPassphrase}
else:
- fstype = request.fstype
+ fmt_type = request.fstype
+ fmt_args = {}
- dev = storage.newPartition(fmt_type=fstype,
+ dev = storage.newPartition(fmt_type=fmt_type,
+ fmt_args=fmt_args,
size=request.size,
grow=request.grow,
maxsize=request.maxSize,
mountpoint=request.mountpoint,
- disks=disks,
+ parents=disks,
weight=request.weight)
# schedule the device for creation
@@ -193,12 +199,10 @@ def _scheduleVolumes(storage, devs):
new_container = storage.newVG
new_volume = storage.newLV
format_name = "lvmpv"
- parent_kw = "pvs"
else:
new_container = storage.newBTRFS
new_volume = storage.newBTRFS
format_name = "btrfs"
- parent_kw = "parents"
if storage.encryptedAutoPart:
pvs = []
@@ -213,7 +217,7 @@ def _scheduleVolumes(storage, devs):
pvs = devs
# create a vg containing all of the autopart pvs
- container = new_container(**{parent_kw: pvs})
+ container = new_container(parents=pvs)
storage.createDevice(container)
#
@@ -252,7 +256,7 @@ def _scheduleVolumes(storage, devs):
kwargs = {"mountpoint": request.mountpoint,
"fmt_type": request.fstype}
if lv:
- kwargs.update({"vg": container,
+ kwargs.update({"parents": [container],
"grow": request.grow,
"maxsize": request.maxSize,
"size": request.size,
@@ -1265,7 +1269,7 @@ class Chunk(object):
def sortRequests(self):
pass
- def growRequests(self):
+ def growRequests(self, uniform=False):
""" Calculate growth amounts for requests in this chunk. """
log.debug("Chunk.growRequests: %r" % self)
@@ -1281,17 +1285,22 @@ class Chunk(object):
while not self.done and self.pool and last_pool != self.pool:
last_pool = self.pool # to keep from getting stuck
self.base = new_base
+ if uniform:
+ growth = last_pool / self.remaining
+
log.debug("%d requests and %d (%dMB) left in chunk" %
(self.remaining, self.pool, self.lengthToSize(self.pool)))
for p in self.requests:
if p.done:
continue
- # Each request is allocated free units from the pool
- # based on the relative _base_ sizes of the remaining
- # growable requests.
- share = p.base / float(self.base)
- growth = int(share * last_pool) # truncate, don't round
+ if not uniform:
+ # Each request is allocated free units from the pool
+ # based on the relative _base_ sizes of the remaining
+ # growable requests.
+ share = p.base / float(self.base)
+ growth = int(share * last_pool) # truncate, don't round
+
p.growth += growth
self.pool -= growth
log.debug("adding %d (%dMB) to %d (%s)" %
diff --git a/pyanaconda/storage/udev.py b/pyanaconda/storage/udev.py
index 09e9c25dd..855ee70a3 100644
--- a/pyanaconda/storage/udev.py
+++ b/pyanaconda/storage/udev.py
@@ -556,19 +556,48 @@ def udev_device_get_iscsi_port(info):
# IPV6 contains : within the address, the part after the last : is the port
return path_components[address_field].split(":")[-1]
-def udev_device_get_iscsi_nic(info):
+def udev_device_get_iscsi_session(info):
# '/devices/pci0000:00/0000:00:02.0/0000:09:00.0/0000:0a:01.0/0000:0e:00.2/host3/session1/target3:0:0/3:0:0:0/block/sda'
# The position of sessionX part depends on device
# (e.g. offload vs. sw; also varies for different offload devs)
+ session = None
match = re.match('/.*/(session\d+)', info["sysfs_path"])
if match:
session = match.groups()[0]
+ else:
+ log.error("udev_device_get_iscsi_session: session not found in %s" % info)
+ return session
+
+
+def udev_device_get_iscsi_nic(info):
+ iface = None
+ session = udev_device_get_iscsi_session(info)
+ if session:
iface = open("/sys/class/iscsi_session/%s/ifacename" %
session).read().strip()
- else:
- iface = None
return iface
+def udev_device_get_iscsi_initiator(info):
+ initiator = None
+ if udev_device_is_partoff_iscsi(info):
+ host = re.match('.*/(host\d+)', info["sysfs_path"]).groups()[0]
+ if host:
+ initiator_file = "/sys/class/iscsi_host/%s/initiatorname" % host
+ if os.access(initiator_file, os.R_OK):
+ initiator = open(initiator_file).read().strip()
+ log.debug("found offload iscsi initiatorname %s in file %s" %
+ (initiator, initiator_file))
+ if initiator.lstrip("(").rstrip(")").lower() == "null":
+ initiator = None
+ if initiator is None:
+ session = udev_device_get_iscsi_session(info)
+ if session:
+ initiator = open("/sys/class/iscsi_session/%s/initiatorname" %
+ session).read().strip()
+ log.debug("found iscsi initiatorname %s" % initiator)
+ return initiator
+
+
# fcoe disks have ID_PATH in the form of:
# For FCoE directly over the NIC (so no VLAN and thus no DCB):
# pci-eth#-fc-${id}
diff --git a/pyanaconda/text.py b/pyanaconda/text.py
index 1b201c1a2..c924ae338 100644
--- a/pyanaconda/text.py
+++ b/pyanaconda/text.py
@@ -36,7 +36,7 @@ from localeinfo import expandLangs
from flags import flags
from textw.constants_text import *
from constants import *
-from network import hasActiveNetDev
+from network import hasActiveNetDev, getDevices
from installinterfacebase import InstallInterfaceBase
import imp
import textw
@@ -241,7 +241,7 @@ class PassphraseEntryWindow:
res = buttons.buttonPressed(rc)
passphrase = None
- if res == TEXT_OK_CHECK:
+ if res == TEXT_OK_CHECK or rc == "F12":
passphrase = passphraseentry.value().strip()
self.rc = passphrase
@@ -389,7 +389,7 @@ class InstallInterface(InstallInterfaceBase):
return passphrase
def enableNetwork(self):
- if len(self.anaconda.network.netdevices) == 0:
+ if len(getDevices) == 0:
return False
from textw.netconfig_text import NetworkConfiguratorText
w = NetworkConfiguratorText(self.screen, self.anaconda)
diff --git a/pyanaconda/textw/add_drive_text.py b/pyanaconda/textw/add_drive_text.py
index b28af4fa1..768681437 100644
--- a/pyanaconda/textw/add_drive_text.py
+++ b/pyanaconda/textw/add_drive_text.py
@@ -24,6 +24,7 @@ from snack import *
from constants_text import *
from pyanaconda.constants import *
import pyanaconda.partIntfHelpers as pih
+from pyanaconda import isys
import gettext
_ = lambda x: gettext.ldgettext("anaconda", x)
@@ -172,7 +173,7 @@ class iSCSITextWizard(pih.iSCSIWizard):
result = grid.run()
button = grid.buttons.buttonPressed(result)
self.screen.popWindow()
- return True if button == TEXT_OK_CHECK else False
+ return bool(button == TEXT_OK_CHECK or result == "F12")
def destroy_dialogs(self):
pass
@@ -363,8 +364,7 @@ class addDriveDialog(object):
return INSTALL_OK
def addFcoeDriveDialog(self, screen):
- netdevs = self.anaconda.network.netdevices
- devs = netdevs.keys()
+ devs = network.getDevices()
devs.sort()
if not devs:
@@ -381,7 +381,7 @@ class addDriveDialog(object):
interfaceList = Listbox(height=len(devs), scroll=1)
for dev in devs:
- hwaddr = netdevs[dev].get("HWADDR")
+ hwaddr = isys.getMacAddress(dev)
if hwaddr:
desc = "%s - %.50s" % (dev, hwaddr)
else:
diff --git a/pyanaconda/textw/netconfig_text.py b/pyanaconda/textw/netconfig_text.py
index aabd7d580..47db65253 100644
--- a/pyanaconda/textw/netconfig_text.py
+++ b/pyanaconda/textw/netconfig_text.py
@@ -32,20 +32,19 @@ _ = lambda x: gettext.ldgettext("anaconda", x)
class NetworkConfiguratorText:
def _handleIPError(self, field, errmsg):
- self.anaconda.intf.messageWindow(_("Error With Data"),
+ self.intf.messageWindow(_("Error With Data"),
_("An error occurred converting the "
"value entered for "
"\"%(field)s\":\n%(errmsg)s") \
% {'field': field, 'errmsg': errmsg})
def _handleIPMissing(self, field):
- self.anaconda.intf.messageWindow(_("Error With Data"),
+ self.intf.messageWindow(_("Error With Data"),
_("A value is required for the field %s") % field)
- def __init__(self, screen, anaconda):
+ def __init__(self, screen, intf):
self.screen = screen
- self.anaconda = anaconda
- self.netdevs = self.anaconda.network.netdevices
+ self.netdevs = network.getDevices()
self._initValues()
@@ -68,16 +67,13 @@ class NetworkConfiguratorText:
dev_list = []
selected_devname = None
- devnames = self.netdevs.keys()
- devnames.sort()
+ devnames = self.netdevs.sort()
# Preselect device set in kickstart
- ksdevice = self.anaconda.network.getKSDevice()
- if ksdevice:
- ksdevice = ksdevice.iface
+ ksdevice = network.get_ksdevice_name()
for devname in devnames:
- hwaddr = self.netdevs[devname].get("HWADDR")
+ hwaddr = isys.getMacAddress(devname)
if hwaddr:
desc = "%s - %.50s" % (devname, hwaddr)
@@ -267,7 +263,7 @@ class NetworkConfiguratorText:
def _checkValues(self):
if not self.ipv4Selected and not self.ipv6Selected:
- self.anaconda.intf.messageWindow(_("Missing protocol"),
+ self.intf.messageWindow(_("Missing protocol"),
_("You must select at least one protocol version"))
return False
@@ -350,7 +346,9 @@ class NetworkConfiguratorText:
Returns True in case of success, False if failed.
"""
- dev = self.netdevs[devname]
+ dev = network.NetworkDevice(ROOT_PATH, devname)
+ dev.loadIfcfgFile()
+
nameservers = ''
if self.ipv4Selected:
@@ -390,22 +388,25 @@ class NetworkConfiguratorText:
else:
dev.set(("IPV6INIT", "no"))
- self.anaconda.network.unsetDNS(devname)
+ self.netdevs[devname].unsetDNS()
if nameservers:
- self.anaconda.network.setDNS(nameservers, devname)
+ self.netdevs[devname].setDNS(nameservers)
dev.set(('ONBOOT', 'yes'))
- w = self.anaconda.intf.waitWindow(_("Configuring Network Interfaces"), _("Waiting for NetworkManager"))
- result = self.anaconda.network.bringUp()
+ w = self.intf.waitWindow(_("Configuring Network Interfaces"), _("Waiting for NetworkManager"))
+ dev.writeIfcfgFile()
+ result = network.waitForConnection()
w.pop()
if not result:
- self.anaconda.intf.messageWindow(_("Network Error"),
+ self.intf.messageWindow(_("Network Error"),
_("There was an error configuring "
"network device %s") % dev.iface)
dev.set(("ONBOOT", "no"))
+ dev.writeIfcfgFile()
return False
+ network.resetResolver()
return True
def _ipv4MethodToggled(self, *args):
diff --git a/pyanaconda/textw/network_text.py b/pyanaconda/textw/network_text.py
index 58eb81e73..a33d7f544 100644
--- a/pyanaconda/textw/network_text.py
+++ b/pyanaconda/textw/network_text.py
@@ -26,8 +26,7 @@ from pyanaconda import network
class HostnameWindow:
def __call__(self, screen, anaconda):
- hname = network.getDefaultHostname(anaconda)
- anaconda.network.hostname = hname
+ hname = network.getHostname()
return INSTALL_OK
# vim:tw=78:ts=4:et:sw=4
diff --git a/pyanaconda/ui/__init__.py b/pyanaconda/ui/__init__.py
index 91252c1b5..7e9e31b27 100644
--- a/pyanaconda/ui/__init__.py
+++ b/pyanaconda/ui/__init__.py
@@ -76,9 +76,9 @@ class UserInterface(object):
### MESSAGE HANDLING METHODS
###
def showError(self, message):
- """Display an error dialog with the given message. After this dialog
- is displayed, anaconda will quit. There is no return value. This
- method must be implemented by all UserInterface subclasses.
+ """Display an error dialog with the given message. There is no return
+ value. This method must be implemented by all UserInterface
+ subclasses.
In the code, this method should be used sparingly and only for
critical errors that anaconda cannot figure out how to recover from.
@@ -111,3 +111,16 @@ class UserInterface(object):
key=lambda obj: obj.priority))
return actionClasses
+
+ def mainExceptionWindow(self, text, exn_file):
+ """Return window with the exception and buttons for debugging, bug
+ reporting and exitting the installer.
+
+ This method will be called only when unhandled exception appears.
+ """
+ raise NotImplementedError
+
+ def saveExceptionWindow(self, account_manager, signature):
+ """Show a window that provides a way to report a bug."""
+ raise NotImplementedError
+
diff --git a/pyanaconda/ui/common.py b/pyanaconda/ui/common.py
index b8944c162..0b8489c3e 100644
--- a/pyanaconda/ui/common.py
+++ b/pyanaconda/ui/common.py
@@ -146,6 +146,7 @@ class Spoke(UIObject):
self.storage = storage
self.payload = payload
self.instclass = instclass
+ self.applyOnSkip = False
def apply(self):
"""Apply the selections made on this Spoke to the object's preset
diff --git a/pyanaconda/ui/gui/Makefile.am b/pyanaconda/ui/gui/Makefile.am
index 7a3958cc3..51d43a8da 100644
--- a/pyanaconda/ui/gui/Makefile.am
+++ b/pyanaconda/ui/gui/Makefile.am
@@ -24,4 +24,4 @@ guidir = $(pkgpyexecdir)/ui/gui
gui_PYTHON = *.py
uidir = $(datadir)/$(PACKAGE_NAME)/ui/
-dist_ui_DATA = *.ui
+dist_ui_DATA = *.glade
diff --git a/pyanaconda/ui/gui/TODO b/pyanaconda/ui/gui/TODO
index 1eccc6b07..f4170bf24 100644
--- a/pyanaconda/ui/gui/TODO
+++ b/pyanaconda/ui/gui/TODO
@@ -20,14 +20,14 @@ Fedora 18
s-c-ks into an anaconda wrapper first, though.
- Wall of anaconda needs to be taken into account.
- Why does the first hub jump left a little bit the first time you use the keyboard to navigate?
-- We need a way for users to look at the list of problems resulting from the
- software dependency check and the storage sanity check
- In custom storage, it'd be nice to be able to say something like "Use this
previous installation as the starting storage configuration for the system
I'm installing now"
- In custom storage, it'd be really nice to be able to drag entries from old
roots into the "New Fedora" root as a way of saying "Use this device with
the same mountpoint in the new system."
+- We're probably going to need to update the auto-generated names of container
+ devices any time the user updates the hostname.
Beyond
======
diff --git a/pyanaconda/ui/gui/__init__.py b/pyanaconda/ui/gui/__init__.py
index dbd0a28b6..5ebb53d15 100644
--- a/pyanaconda/ui/gui/__init__.py
+++ b/pyanaconda/ui/gui/__init__.py
@@ -19,6 +19,7 @@
# Red Hat Author(s): Chris Lumens <clumens@redhat.com>
#
import importlib, inspect, os, sys
+import meh.ui.gui
from pyanaconda.ui import UserInterface, common, collect
from pyanaconda.ui.gui.utils import enlightbox
@@ -124,13 +125,23 @@ class GraphicalUserInterface(UserInterface):
return bool(rc)
+ def mainExceptionWindow(self, text, exn_file, *args, **kwargs):
+ meh_intf = meh.ui.gui.GraphicalIntf()
+
+ return meh_intf.mainExceptionWindow(text, exn_file)
+
+
+ def saveExceptionWindow(self, account_manager, signature, *args, **kwargs):
+ meh_intf = meh.ui.gui.GraphicalIntf()
+ meh_intf.saveExceptionWindow(account_manager, signature)
+
###
### SIGNAL HANDLING METHODS
###
def _on_continue_clicked(self):
- # If we're on the last screen, clicking Continue is the same as clicking Quit.
+ # If we're on the last screen, clicking Continue quits.
if len(self._actions) == 1:
- self._on_quit_clicked()
+ sys.exit(0)
return
# If the current action wants us to jump to an arbitrary point ahead,
@@ -246,6 +257,7 @@ class GUIObject(common.UIObject):
self._origStrings = {}
self.skipTo = None
+ self.applyOnSkip = False
from gi.repository import Gtk
@@ -341,7 +353,7 @@ class GUIObject(common.UIObject):
class QuitDialog(GUIObject):
builderObjects = ["quitDialog"]
mainWidgetName = "quitDialog"
- uiFile = "main.ui"
+ uiFile = "main.glade"
def run(self):
rc = self.window.run()
diff --git a/pyanaconda/ui/gui/hubs/Makefile.am b/pyanaconda/ui/gui/hubs/Makefile.am
index d1dad0a50..ae520a6e3 100644
--- a/pyanaconda/ui/gui/hubs/Makefile.am
+++ b/pyanaconda/ui/gui/hubs/Makefile.am
@@ -22,4 +22,4 @@ hubsdir = $(pkgpyexecdir)/ui/gui/hubs
hubs_PYTHON = *.py
uidir = $(datadir)/$(PACKAGE_NAME)/ui/hubs
-dist_ui_DATA = *.ui
+dist_ui_DATA = *.glade
diff --git a/pyanaconda/ui/gui/hubs/__init__.py b/pyanaconda/ui/gui/hubs/__init__.py
index 8b8ee099a..f5e211f3c 100644
--- a/pyanaconda/ui/gui/hubs/__init__.py
+++ b/pyanaconda/ui/gui/hubs/__init__.py
@@ -101,8 +101,10 @@ class Hub(GUIObject, common.Hub):
# prevent the user from switching away. It's up to the spoke's back
# button handler to kill its own layer of main loop.
Gtk.main()
- action.apply()
- action.execute()
+
+ if not action.skipTo or (action.skipTo and action.applyOnSkip):
+ action.apply()
+ action.execute()
def _createBox(self):
from gi.repository import Gtk, AnacondaWidgets
diff --git a/pyanaconda/ui/gui/hubs/progress.ui b/pyanaconda/ui/gui/hubs/progress.glade
index 1eb32c97e..1eb32c97e 100644
--- a/pyanaconda/ui/gui/hubs/progress.ui
+++ b/pyanaconda/ui/gui/hubs/progress.glade
diff --git a/pyanaconda/ui/gui/hubs/progress.py b/pyanaconda/ui/gui/hubs/progress.py
index 2eb1b75de..b1c1e9314 100644
--- a/pyanaconda/ui/gui/hubs/progress.py
+++ b/pyanaconda/ui/gui/hubs/progress.py
@@ -31,6 +31,8 @@ import os
from pyanaconda.localeinfo import expandLangs
from pyanaconda.product import productName
+from pyanaconda.flags import flags
+from pykickstart.constants import KS_WAIT, KS_SHUTDOWN, KS_REBOOT
from pyanaconda.ui.gui.hubs import Hub
from pyanaconda.ui.gui.utils import gdk_threaded
@@ -40,7 +42,7 @@ __all__ = ["ProgressHub"]
class ProgressHub(Hub):
builderObjects = ["progressWindow"]
mainWidgetName = "progressWindow"
- uiFile = "hubs/progress.ui"
+ uiFile = "hubs/progress.glade"
def __init__(self, data, storage, payload, instclass):
Hub.__init__(self, data, storage, payload, instclass)
@@ -81,6 +83,11 @@ class ProgressHub(Hub):
GLib.source_remove(self._rnotes_id)
self._progressNotebook.next_page()
+
+ # kickstart install, continue automatically if reboot or shutdown selected
+ if flags.automatedInstall and self.data.reboot.action in [KS_REBOOT, KS_SHUTDOWN]:
+ self.continueButton.emit("clicked")
+
return False
q.task_done()
@@ -145,7 +152,7 @@ class ProgressHub(Hub):
args=(self.storage, self.payload, self.data, self.instclass)))
@property
- def quitButton(self):
+ def continueButton(self):
return self.builder.get_object("rebootButton")
def _init_progress_bar(self, steps):
diff --git a/pyanaconda/ui/gui/hubs/summary.ui b/pyanaconda/ui/gui/hubs/summary.glade
index 25aa16564..25aa16564 100644
--- a/pyanaconda/ui/gui/hubs/summary.ui
+++ b/pyanaconda/ui/gui/hubs/summary.glade
diff --git a/pyanaconda/ui/gui/hubs/summary.py b/pyanaconda/ui/gui/hubs/summary.py
index 4973ebe87..c867e496a 100644
--- a/pyanaconda/ui/gui/hubs/summary.py
+++ b/pyanaconda/ui/gui/hubs/summary.py
@@ -26,7 +26,7 @@ __all__ = ["SummaryHub"]
class SummaryHub(Hub):
builderObjects = ["summaryWindow"]
mainWidgetName = "summaryWindow"
- uiFile = "hubs/summary.ui"
+ uiFile = "hubs/summary.glade"
# FIXME: I really hate this.
diff --git a/pyanaconda/ui/gui/main.ui b/pyanaconda/ui/gui/main.glade
index 2f773a48f..2f773a48f 100644
--- a/pyanaconda/ui/gui/main.ui
+++ b/pyanaconda/ui/gui/main.glade
diff --git a/pyanaconda/ui/gui/spokes/Makefile.am b/pyanaconda/ui/gui/spokes/Makefile.am
index 4968a897d..352f4e4fb 100644
--- a/pyanaconda/ui/gui/spokes/Makefile.am
+++ b/pyanaconda/ui/gui/spokes/Makefile.am
@@ -24,4 +24,4 @@ spokesdir = $(pkgpyexecdir)/ui/gui/spokes
spokes_PYTHON = *.py
uidir = $(datadir)/$(PACKAGE_NAME)/ui/spokes
-dist_ui_DATA = *.ui
+dist_ui_DATA = *.glade
diff --git a/pyanaconda/ui/gui/spokes/__init__.py b/pyanaconda/ui/gui/spokes/__init__.py
index 192022ffa..61aea5cad 100644
--- a/pyanaconda/ui/gui/spokes/__init__.py
+++ b/pyanaconda/ui/gui/spokes/__init__.py
@@ -31,6 +31,33 @@ class Spoke(GUIObject, common.Spoke):
GUIObject.__init__(self, data)
common.Spoke.__init__(self, data, storage, payload, instclass)
+ def apply(self):
+ """Apply the selections made on this Spoke to the object's preset
+ data object. This method must be provided by every subclass.
+ """
+ raise NotImplementedError
+
+ @property
+ def completed(self):
+ """Has this spoke been visited and completed? If not, a special warning
+ icon will be shown on the Hub beside the spoke, and a highlighted
+ message will be shown at the bottom of the Hub. Installation will not
+ be allowed to proceed until all spokes are complete.
+ """
+ return False
+
+ def execute(self):
+ """Cause the data object to take effect on the target system. This will
+ usually be as simple as calling one or more of the execute methods on
+ the data object. This method does not need to be provided by all
+ subclasses.
+
+ This method will be called in two different places: (1) Immediately
+ after initialize on kickstart installs. (2) Immediately after apply
+ in all cases.
+ """
+ pass
+
def initialize(self):
GUIObject.initialize(self)
diff --git a/pyanaconda/ui/gui/spokes/custom.ui b/pyanaconda/ui/gui/spokes/custom.glade
index 5f97efad1..5f97efad1 100644
--- a/pyanaconda/ui/gui/spokes/custom.ui
+++ b/pyanaconda/ui/gui/spokes/custom.glade
diff --git a/pyanaconda/ui/gui/spokes/custom.py b/pyanaconda/ui/gui/spokes/custom.py
index 5a44d4ae7..c76146a72 100644
--- a/pyanaconda/ui/gui/spokes/custom.py
+++ b/pyanaconda/ui/gui/spokes/custom.py
@@ -58,7 +58,7 @@ new_install_name = _("New %s %s Installation") % (productName, productVersion)
class AddDialog(GUIObject):
builderObjects = ["addDialog"]
mainWidgetName = "addDialog"
- uiFile = "spokes/custom.ui"
+ uiFile = "spokes/custom.glade"
def __init__(self, *args, **kwargs):
GUIObject.__init__(self, *args, **kwargs)
@@ -88,7 +88,7 @@ class AddDialog(GUIObject):
class ConfirmDeleteDialog(GUIObject):
builderObjects = ["confirmDeleteDialog"]
mainWidgetName = "confirmDeleteDialog"
- uiFile = "spokes/custom.ui"
+ uiFile = "spokes/custom.glade"
def on_delete_cancel_clicked(self, button, *args):
self.window.destroy()
@@ -115,7 +115,7 @@ class CustomPartitioningSpoke(NormalSpoke, StorageChecker):
"partitionStore",
"addImage", "removeImage", "settingsImage"]
mainWidgetName = "customStorageWindow"
- uiFile = "spokes/custom.ui"
+ uiFile = "spokes/custom.glade"
category = StorageCategory
title = N_("MANUAL PARTITIONING")
diff --git a/pyanaconda/ui/gui/spokes/datetime_spoke.ui b/pyanaconda/ui/gui/spokes/datetime_spoke.glade
index cdf1cf5b5..cdf1cf5b5 100644
--- a/pyanaconda/ui/gui/spokes/datetime_spoke.ui
+++ b/pyanaconda/ui/gui/spokes/datetime_spoke.glade
diff --git a/pyanaconda/ui/gui/spokes/datetime_spoke.py b/pyanaconda/ui/gui/spokes/datetime_spoke.py
index 944cdfc4b..0e2ca77a6 100644
--- a/pyanaconda/ui/gui/spokes/datetime_spoke.py
+++ b/pyanaconda/ui/gui/spokes/datetime_spoke.py
@@ -46,7 +46,7 @@ POOL_SERVERS_NOTE = _("Note: pool servers may not be available all the time")
class NTPconfigDialog(GUIObject):
builderObjects = ["ntpConfigDialog", "addImage", "serversStore"]
mainWidgetName = "ntpConfigDialog"
- uiFile = "spokes/datetime_spoke.ui"
+ uiFile = "spokes/datetime_spoke.glade"
def __init__(self, *args):
GUIObject.__init__(self, *args)
@@ -244,7 +244,7 @@ class DatetimeSpoke(NormalSpoke):
]
mainWidgetName = "datetimeWindow"
- uiFile = "spokes/datetime_spoke.ui"
+ uiFile = "spokes/datetime_spoke.glade"
category = LocalizationCategory
diff --git a/pyanaconda/ui/gui/spokes/keyboard.ui b/pyanaconda/ui/gui/spokes/keyboard.glade
index 1243e22fb..1243e22fb 100644
--- a/pyanaconda/ui/gui/spokes/keyboard.ui
+++ b/pyanaconda/ui/gui/spokes/keyboard.glade
diff --git a/pyanaconda/ui/gui/spokes/keyboard.py b/pyanaconda/ui/gui/spokes/keyboard.py
index f0084b398..cbf340b91 100644
--- a/pyanaconda/ui/gui/spokes/keyboard.py
+++ b/pyanaconda/ui/gui/spokes/keyboard.py
@@ -42,7 +42,7 @@ class AddLayoutDialog(GUIObject):
builderObjects = ["addLayoutDialog", "newLayoutStore",
"newLayoutStoreFilter", "newLayoutStoreSort"]
mainWidgetName = "addLayoutDialog"
- uiFile = "spokes/keyboard.ui"
+ uiFile = "spokes/keyboard.glade"
def __init__(self, *args):
GUIObject.__init__(self, *args)
@@ -140,7 +140,7 @@ class KeyboardSpoke(NormalSpoke):
builderObjects = ["addedLayoutStore", "keyboardWindow",
"addImage", "removeImage", "upImage", "downImage", "previewImage"]
mainWidgetName = "keyboardWindow"
- uiFile = "spokes/keyboard.ui"
+ uiFile = "spokes/keyboard.glade"
category = LocalizationCategory
diff --git a/pyanaconda/ui/gui/spokes/lib/Makefile.am b/pyanaconda/ui/gui/spokes/lib/Makefile.am
index edf1be594..ecd597cbc 100644
--- a/pyanaconda/ui/gui/spokes/lib/Makefile.am
+++ b/pyanaconda/ui/gui/spokes/lib/Makefile.am
@@ -22,4 +22,4 @@ spokesdir = $(pkgpyexecdir)/ui/gui/spokes/lib
spokes_PYTHON = *.py
uidir = $(datadir)/$(PACKAGE_NAME)/ui/spokes/lib
-dist_ui_DATA = *.ui
+dist_ui_DATA = *.glade
diff --git a/pyanaconda/ui/gui/spokes/lib/cart.ui b/pyanaconda/ui/gui/spokes/lib/cart.glade
index 92fdfe53a..92fdfe53a 100644
--- a/pyanaconda/ui/gui/spokes/lib/cart.ui
+++ b/pyanaconda/ui/gui/spokes/lib/cart.glade
diff --git a/pyanaconda/ui/gui/spokes/lib/cart.py b/pyanaconda/ui/gui/spokes/lib/cart.py
index dbbfc1d04..3e77a36f0 100644
--- a/pyanaconda/ui/gui/spokes/lib/cart.py
+++ b/pyanaconda/ui/gui/spokes/lib/cart.py
@@ -42,7 +42,7 @@ def size_str(mb):
class SelectedDisksDialog(GUIObject):
builderObjects = ["selected_disks_dialog", "disk_store"]
mainWidgetName = "selected_disks_dialog"
- uiFile = "spokes/lib/cart.ui"
+ uiFile = "spokes/lib/cart.glade"
def initialize(self, disks, free, showRemove=True):
for disk in disks:
diff --git a/pyanaconda/ui/gui/spokes/lib/detailederror.glade b/pyanaconda/ui/gui/spokes/lib/detailederror.glade
new file mode 100644
index 000000000..db9daf262
--- /dev/null
+++ b/pyanaconda/ui/gui/spokes/lib/detailederror.glade
@@ -0,0 +1,90 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+ <!-- interface-requires gtk+ 3.0 -->
+ <object class="GtkTextBuffer" id="detailedTextBuffer"/>
+ <object class="GtkDialog" id="detailedErrorDialog">
+ <property name="can_focus">False</property>
+ <property name="border_width">5</property>
+ <property name="modal">True</property>
+ <property name="window_position">center</property>
+ <property name="type_hint">dialog</property>
+ <property name="decorated">False</property>
+ <child internal-child="vbox">
+ <object class="GtkBox" id="dialog-vbox2">
+ <property name="can_focus">False</property>
+ <property name="orientation">vertical</property>
+ <property name="spacing">2</property>
+ <child>
+ <object class="GtkLabel" id="label2">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="margin_bottom">18</property>
+ <property name="xalign">0</property>
+ <property name="label" translatable="yes">The following software marked for installation has errors. This is likely caused by an error with
+your installation source. You can attempt to remove these packages from your installation,
+change your installation source, or quit the installer.</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkScrolledWindow" id="scrolledwindow1">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="shadow_type">in</property>
+ <child>
+ <object class="GtkTextView" id="detailedTextView">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="editable">False</property>
+ <property name="cursor_visible">False</property>
+ <property name="buffer">detailedTextBuffer</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child internal-child="action_area">
+ <object class="GtkButtonBox" id="dialog-action_area2">
+ <property name="can_focus">False</property>
+ <property name="layout_style">end</property>
+ <child>
+ <object class="GtkButton" id="detailedCancelButton">
+ <property name="label">gtk-cancel</property>
+ <property name="use_action_appearance">False</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="halign">start</property>
+ <property name="use_action_appearance">False</property>
+ <property name="use_stock">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">0</property>
+ <property name="secondary">True</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="pack_type">end</property>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ <action-widgets>
+ <action-widget response="0">detailedCancelButton</action-widget>
+ </action-widgets>
+ </object>
+</interface>
diff --git a/pyanaconda/ui/gui/spokes/lib/detailederror.py b/pyanaconda/ui/gui/spokes/lib/detailederror.py
new file mode 100644
index 000000000..997d58502
--- /dev/null
+++ b/pyanaconda/ui/gui/spokes/lib/detailederror.py
@@ -0,0 +1,58 @@
+# Detailed error dialog class
+#
+# Copyright (C) 2011-2012 Red Hat, Inc.
+#
+# This copyrighted material is made available to anyone wishing to use,
+# modify, copy, or redistribute it subject to the terms and conditions of
+# the GNU General Public License v.2, or (at your option) any later version.
+# This program is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY expressed or implied, including the implied warranties 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, write to the
+# Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+# 02110-1301, USA. Any Red Hat trademarks that are incorporated in the
+# source code or documentation are not subject to the GNU General Public
+# License and may only be used or replicated with the express permission of
+# Red Hat, Inc.
+#
+# Red Hat Author(s): Chris Lumens <clumens@redhat.com>
+#
+
+from gi.repository import Gtk
+
+from pyanaconda.ui.gui import UIObject
+
+__all__ = ["DetailedErrorDialog"]
+
+class DetailedErrorDialog(UIObject):
+ """This class provides a lightboxable dialog to display a very detailed
+ set of error messages, like might be required to display the results
+ of package dependency solving or storage sanity checking.
+
+ By default, this dialog has only a single button - cancel, displayed
+ on the far left hand side of the dialog, with a response ID of 0.
+ For all other buttons, provide a kwarg named "buttons" as a list of
+ translated labels. Each will have an incrementing response ID
+ starting with 1. It is up to the caller of the "run" method to do
+ something with the returned response ID.
+ """
+ builderObjects = ["detailedErrorDialog", "detailedTextBuffer"]
+ mainWidgetName = "detailedErrorDialog"
+ uiFile = "spokes/lib/detailederror.glade"
+
+ def __init__(self, *args, **kwargs):
+ buttons = kwargs.pop("buttons", [])
+ UIObject.__init__(self, *args, **kwargs)
+
+ i = 1
+ for button in buttons:
+ self.window.add_button(button, i)
+ i += 1
+
+ def refresh(self, msg):
+ buf = self.builder.get_object("detailedTextBuffer")
+ buf.set_text(msg, -1)
+
+ def run(self):
+ return self.window.run()
diff --git a/pyanaconda/ui/gui/spokes/network.ui b/pyanaconda/ui/gui/spokes/network.glade
index 49c75c289..49c75c289 100644
--- a/pyanaconda/ui/gui/spokes/network.ui
+++ b/pyanaconda/ui/gui/spokes/network.glade
diff --git a/pyanaconda/ui/gui/spokes/network.py b/pyanaconda/ui/gui/spokes/network.py
index fb68c3fc3..9142ad0e1 100644
--- a/pyanaconda/ui/gui/spokes/network.py
+++ b/pyanaconda/ui/gui/spokes/network.py
@@ -902,7 +902,7 @@ class NetworkControlBox():
class NetworkSpoke(NormalSpoke):
builderObjects = ["networkWindow", "liststore_wireless_network", "liststore_devices"]
mainWidgetName = "networkWindow"
- uiFile = "spokes/network.ui"
+ uiFile = "spokes/network.glade"
title = N_("NETWORK CONFIGURATION")
icon = "network-transmit-receive-symbolic"
@@ -962,7 +962,7 @@ class NetworkSpoke(NormalSpoke):
class NetworkStandaloneSpoke(StandaloneSpoke):
builderObjects = ["networkStandaloneWindow", "networkControlBox_vbox", "liststore_wireless_network", "liststore_devices"]
mainWidgetName = "networkStandaloneWindow"
- uiFile = "spokes/network.ui"
+ uiFile = "spokes/network.glade"
preForHub = SummaryHub
priority = 10
@@ -1040,7 +1040,7 @@ if __name__ == "__main__":
builder = Gtk.Builder()
import os
- ui_file_path = os.environ.get('UIPATH')+'spokes/network.ui'
+ ui_file_path = os.environ.get('UIPATH')+'spokes/network.glade'
builder.add_from_file(ui_file_path)
n = NetworkControlBox(builder)
diff --git a/pyanaconda/ui/gui/spokes/software.ui b/pyanaconda/ui/gui/spokes/software.glade
index 20bbaa32e..d0d91325e 100644
--- a/pyanaconda/ui/gui/spokes/software.ui
+++ b/pyanaconda/ui/gui/spokes/software.glade
@@ -28,6 +28,7 @@
<property name="startup_id">filler</property>
<property name="window_name">SOFTWARE SELECTION</property>
<signal name="back-clicked" handler="on_back_clicked" swapped="no"/>
+ <signal name="info-bar-clicked" handler="on_info_bar_clicked" swapped="no"/>
<child internal-child="main_box">
<object class="GtkBox" id="AnacondaSpokeWindow-main_box1">
<property name="can_focus">False</property>
@@ -181,6 +182,9 @@
<property name="headers_visible">False</property>
<property name="headers_clickable">False</property>
<property name="search_column">0</property>
+ <child internal-child="selection">
+ <object class="GtkTreeSelection" id="treeview-selection2"/>
+ </child>
<child>
<object class="GtkTreeViewColumn" id="addonSelectedCol">
<property name="title" translatable="yes">column</property>
diff --git a/pyanaconda/ui/gui/spokes/software.py b/pyanaconda/ui/gui/spokes/software.py
index 9e56a4bab..cf047ab2d 100644
--- a/pyanaconda/ui/gui/spokes/software.py
+++ b/pyanaconda/ui/gui/spokes/software.py
@@ -25,19 +25,21 @@ N_ = lambda x: x
from pyanaconda.flags import flags
-from pyanaconda.ui.gui import communication
+from pyanaconda.ui.gui import UIObject, communication
from pyanaconda.ui.gui.spokes import NormalSpoke
-from pyanaconda.ui.gui.utils import gdk_threaded
+from pyanaconda.ui.gui.spokes.lib.detailederror import DetailedErrorDialog
+from pyanaconda.ui.gui.utils import enlightbox, gdk_threaded
from pyanaconda.ui.gui.categories.software import SoftwareCategory
-from pyanaconda.ui.gui.utils import enlightbox
from .source import AdditionalReposDialog
+import sys
+
__all__ = ["SoftwareSelectionSpoke"]
class SoftwareSelectionSpoke(NormalSpoke):
builderObjects = ["addonStore", "desktopStore", "softwareWindow"]
mainWidgetName = "softwareWindow"
- uiFile = "spokes/software.ui"
+ uiFile = "spokes/software.glade"
category = SoftwareCategory
@@ -46,7 +48,7 @@ class SoftwareSelectionSpoke(NormalSpoke):
def __init__(self, *args, **kwargs):
NormalSpoke.__init__(self, *args, **kwargs)
- self._error = False
+ self._errorMsgs = None
self._tx_id = None
self.selectedGroups = []
@@ -89,12 +91,12 @@ class SoftwareSelectionSpoke(NormalSpoke):
try:
self.payload.checkSoftwareSelection()
except DependencyError as e:
- self._error = True
+ self._errorMsgs = "\n".join(sorted(e.message))
communication.send_message(self.__class__.__name__,
_("Error checking software dependencies"))
self._tx_id = None
else:
- self._error = False
+ self._errorMsgs = None
self._tx_id = self.payload.txID
finally:
communication.send_ready(self.__class__.__name__)
@@ -105,7 +107,7 @@ class SoftwareSelectionSpoke(NormalSpoke):
from pyanaconda.kickstart import packagesSeen
processingDone = not threadMgr.get("AnaCheckSoftwareThread") and \
- not self._error and \
+ not self._errorMsgs and \
self._tx_id == self.payload.txID
if flags.automatedInstall:
@@ -130,7 +132,7 @@ class SoftwareSelectionSpoke(NormalSpoke):
from pyanaconda.kickstart import packagesSeen
from pyanaconda.threads import threadMgr
- if self._error:
+ if self._errorMsgs:
return _("Error checking software selection")
if threadMgr.get("AnaPayloadMDThread") or self.payload.baseRepo is None:
@@ -187,6 +189,8 @@ class SoftwareSelectionSpoke(NormalSpoke):
self.apply()
def refresh(self):
+ from gi.repository import Gtk
+
from pyanaconda.threads import threadMgr
NormalSpoke.refresh(self)
@@ -229,6 +233,9 @@ class SoftwareSelectionSpoke(NormalSpoke):
self.excludedGroups = [g.name
for g in self.data.packages.excludedGroupList]
+ if self._errorMsgs:
+ self.window.set_info(Gtk.MessageType.WARNING, _("Error checking software dependencies. Click for details."))
+
# Returns the row in the store corresponding to what's selected on the
# left hand panel, or None if nothing's selected.
def _get_selected_desktop(self):
@@ -258,3 +265,28 @@ class SoftwareSelectionSpoke(NormalSpoke):
with enlightbox(self.window, self._addRepoDialog.window):
response = self._addRepoDialog.run()
+ def on_info_bar_clicked(self, *args):
+ if not self._errorMsgs:
+ return
+
+ dialog = DetailedErrorDialog(self.data, buttons=[_("_Quit"), _("_Remove Packages"),
+ _("_Modify Software Source")])
+ with enlightbox(self.window, dialog.window):
+ dialog.refresh(self._errorMsgs)
+ rc = dialog.run()
+
+ dialog.window.destroy()
+
+ if rc == 0:
+ # Close the dialog so the user can change selections.
+ pass
+ elif rc == 1:
+ # Quit.
+ sys.exit(0)
+ elif rc == 2:
+ # TODO: Attempt to remove the affected packages.
+ pass
+ elif rc == 3:
+ # Send the user to the installation source spoke.
+ self.skipTo = "SourceSpoke"
+ self.window.emit("back-clicked")
diff --git a/pyanaconda/ui/gui/spokes/source.ui b/pyanaconda/ui/gui/spokes/source.glade
index 5259431bc..5259431bc 100644
--- a/pyanaconda/ui/gui/spokes/source.ui
+++ b/pyanaconda/ui/gui/spokes/source.glade
diff --git a/pyanaconda/ui/gui/spokes/source.py b/pyanaconda/ui/gui/spokes/source.py
index c8c708dc9..fd40b5cb3 100644
--- a/pyanaconda/ui/gui/spokes/source.py
+++ b/pyanaconda/ui/gui/spokes/source.py
@@ -48,7 +48,7 @@ METADATA_ERROR_MESSAGE = _("Error downloading package metadata...")
class ProxyDialog(GUIObject):
builderObjects = ["proxyDialog"]
mainWidgetName = "proxyDialog"
- uiFile = "spokes/source.ui"
+ uiFile = "spokes/source.glade"
def on_proxy_cancel_clicked(self, *args):
self.window.destroy()
@@ -129,7 +129,7 @@ class ProxyDialog(GUIObject):
class MediaCheckDialog(GUIObject):
builderObjects = ["mediaCheckDialog"]
mainWidgetName = "mediaCheckDialog"
- uiFile = "spokes/source.ui"
+ uiFile = "spokes/source.glade"
def _checkisoEndsCB(self, pid, status):
doneButton = self.builder.get_object("doneButton")
@@ -196,7 +196,7 @@ class MediaCheckDialog(GUIObject):
class IsoChooser(GUIObject):
builderObjects = ["isoChooserDialog", "isoFilter"]
mainWidgetName = "isoChooserDialog"
- uiFile = "spokes/source.ui"
+ uiFile = "spokes/source.glade"
def refresh(self, currentFile=""):
GUIObject.refresh(self)
@@ -238,7 +238,7 @@ class IsoChooser(GUIObject):
class AdditionalReposDialog(GUIObject):
builderObjects = ["additionalReposDialog", "peopleRepositories", "peopleRepositoriesFilter"]
mainWidgetName = "additionalReposDialog"
- uiFile = "spokes/source.ui"
+ uiFile = "spokes/source.glade"
typingTimeout = 1
@@ -409,7 +409,7 @@ class AdditionalReposDialog(GUIObject):
class SourceSpoke(NormalSpoke):
builderObjects = ["isoChooser", "isoFilter", "partitionStore", "sourceWindow", "dirImage"]
mainWidgetName = "sourceWindow"
- uiFile = "spokes/source.ui"
+ uiFile = "spokes/source.glade"
category = SoftwareCategory
diff --git a/pyanaconda/ui/gui/spokes/storage.ui b/pyanaconda/ui/gui/spokes/storage.glade
index 16fa40271..16fa40271 100644
--- a/pyanaconda/ui/gui/spokes/storage.ui
+++ b/pyanaconda/ui/gui/spokes/storage.glade
diff --git a/pyanaconda/ui/gui/spokes/storage.py b/pyanaconda/ui/gui/spokes/storage.py
index ec26f7615..d8cccd8c0 100644
--- a/pyanaconda/ui/gui/spokes/storage.py
+++ b/pyanaconda/ui/gui/spokes/storage.py
@@ -47,7 +47,7 @@ from pyanaconda.ui.gui.spokes.lib.cart import SelectedDisksDialog
from pyanaconda.ui.gui.categories.storage import StorageCategory
from pyanaconda.ui.gui.utils import enlightbox, gdk_threaded
-from pyanaconda.storage import doKickstartStorage
+from pyanaconda.kickstart import doKickstartStorage
from pyanaconda.storage.size import Size
from pyanaconda.product import productName
from pyanaconda.flags import flags
@@ -60,6 +60,9 @@ _ = lambda x: gettext.ldgettext("anaconda", x)
N_ = lambda x: x
P_ = lambda x, y, z: gettext.ldngettext("anaconda", x, y, z)
+import logging
+log = logging.getLogger("anaconda")
+
__all__ = ["StorageSpoke"]
class FakeDiskLabel(object):
@@ -110,7 +113,7 @@ def size_str(mb):
class InstallOptions1Dialog(GUIObject):
builderObjects = ["options1_dialog"]
mainWidgetName = "options1_dialog"
- uiFile = "spokes/storage.ui"
+ uiFile = "spokes/storage.glade"
RESPONSE_CANCEL = 0
RESPONSE_CONTINUE = 1
@@ -246,11 +249,15 @@ class StorageChecker(object):
(StorageChecker.errors,
StorageChecker.warnings) = self.storage.sanityCheck()
communication.send_ready(self._mainSpokeClass, justUpdate=True)
+ for e in StorageChecker.errors:
+ log.error(e)
+ for w in StorageChecker.warnings:
+ log.warn(w)
class StorageSpoke(NormalSpoke, StorageChecker):
builderObjects = ["storageWindow"]
mainWidgetName = "storageWindow"
- uiFile = "spokes/storage.ui"
+ uiFile = "spokes/storage.glade"
category = StorageCategory
@@ -260,6 +267,8 @@ class StorageSpoke(NormalSpoke, StorageChecker):
def __init__(self, *args, **kwargs):
NormalSpoke.__init__(self, *args, **kwargs)
+ self.applyOnSkip = True
+
self._ready = False
self.selected_disks = self.data.ignoredisk.onlyuse[:]
@@ -310,14 +319,9 @@ class StorageSpoke(NormalSpoke, StorageChecker):
# user may have set up before now.
self.storage.config.clearNonExistent = self.data.autopart.autopart
- # Pick the first disk to be the destination device for the bootloader.
- # This appears to be the minimum amount of configuration required to
- # make autopart happy with the bootloader settings.
- if not self.data.bootloader.bootDrive:
- self.data.bootloader.bootDrive = self.storage.bootloader.disks[0].name
-
def execute(self):
- doKickstartStorage(self.storage, self.data, self.instclass, self)
+ doKickstartStorage(self.storage, self.data, self.instclass)
+ self.run()
@property
def completed(self):
diff --git a/pyanaconda/ui/gui/spokes/welcome.ui b/pyanaconda/ui/gui/spokes/welcome.glade
index e5bcc0f7d..e5bcc0f7d 100644
--- a/pyanaconda/ui/gui/spokes/welcome.ui
+++ b/pyanaconda/ui/gui/spokes/welcome.glade
diff --git a/pyanaconda/ui/gui/spokes/welcome.py b/pyanaconda/ui/gui/spokes/welcome.py
index 9c899eb4a..dc4c733ce 100644
--- a/pyanaconda/ui/gui/spokes/welcome.py
+++ b/pyanaconda/ui/gui/spokes/welcome.py
@@ -190,7 +190,7 @@ class LanguageMixIn(object):
class WelcomeLanguageSpoke(LanguageMixIn, StandaloneSpoke):
mainWidgetName = "welcomeWindow"
- uiFile = "spokes/welcome.ui"
+ uiFile = "spokes/welcome.glade"
builderObjects = LanguageMixIn.builderObjects + [mainWidgetName, "betaWarnDialog"]
preForHub = SummaryHub
@@ -234,7 +234,7 @@ class WelcomeLanguageSpoke(LanguageMixIn, StandaloneSpoke):
class LanguageSpoke(LanguageMixIn, NormalSpoke):
mainWidgetName = "languageSpokeWindow"
- uiFile = "spokes/welcome.ui"
+ uiFile = "spokes/welcome.glade"
builderObjects = LanguageMixIn.builderObjects + [mainWidgetName, WelcomeLanguageSpoke.mainWidgetName]
category = LocalizationCategory
diff --git a/pyanaconda/ui/gui/tools/run-spoke.py b/pyanaconda/ui/gui/tools/run-spoke.py
index afb6b0193..afddbd1ff 100755
--- a/pyanaconda/ui/gui/tools/run-spoke.py
+++ b/pyanaconda/ui/gui/tools/run-spoke.py
@@ -97,7 +97,6 @@ instclass = DefaultInstall()
payload = YumPayload(ksdata)
payload.setup(storage)
-payload.install_log = sys.stdout
spoke = spokeClass(ksdata, storage, payload, instclass)
if hasattr(spoke, "register_event_cb"):
diff --git a/pyanaconda/upgrade.py b/pyanaconda/upgrade.py
deleted file mode 100644
index 6b128559b..000000000
--- a/pyanaconda/upgrade.py
+++ /dev/null
@@ -1,341 +0,0 @@
-#
-# upgrade.py - Existing install probe and upgrade procedure
-#
-# Copyright (C) 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/>.
-#
-# Author(s): Matt Wilson <msw@redhat.com>
-#
-
-import isys
-import os
-import iutil
-import time
-import sys
-import os.path
-import shutil
-import string
-import selinux
-from flags import flags
-from constants import *
-from product import productName
-from storage import findExistingRootDevices
-from storage import mountExistingSystem
-from storage.formats import getFormat
-
-import gettext
-_ = lambda x: gettext.ldgettext("anaconda", x)
-
-import rpm
-
-import logging
-log = logging.getLogger("anaconda")
-
-def queryUpgradeContinue(anaconda):
- if anaconda.dir == DISPATCH_FORWARD:
- return
-
- rc = anaconda.intf.messageWindow(_("Proceed with upgrade?"),
- _("The file systems of the Linux installation "
- "you have chosen to upgrade have already been "
- "mounted. You cannot go back past this point. "
- "\n\n") +
- _("Would you like to continue with the upgrade?"),
- type="custom", custom_icon=["error","error"],
- custom_buttons=[_("_Exit installer"), _("_Continue")])
- if rc == 0:
- sys.exit(0)
- return DISPATCH_FORWARD
-
-def setUpgradeRoot(anaconda):
- anaconda.upgradeRoot = []
- root_device = None
- # kickstart can pass device as device name or uuid. No quotes allowed.
- if anaconda.ksdata and anaconda.ksdata.upgrade.root_device is not None:
- root_device = anaconda.ksdata.upgrade.root_device
- for (dev, label) in anaconda.rootParts:
- if ((root_device is not None) and
- (root_device == dev.name or root_device == "UUID=%s" % dev.format.uuid)):
- anaconda.upgradeRoot.insert(0, (dev,label))
- else:
- anaconda.upgradeRoot.append((dev,label))
-
-def findRootParts(anaconda):
- if anaconda.dir == DISPATCH_BACK:
- return
- if anaconda.rootParts is None:
- (anaconda.rootParts, notUpgradable) = findExistingRootDevices(anaconda,
- upgradeany=flags.cmdline.has_key("upgradeany"))
-
- if notUpgradable and not anaconda.rootParts:
- oldInstalls = ""
- for product, version, arch, name, tests in notUpgradable:
- if None in (product, version):
- oldInstalls = _("Unknown release on %s -") % (name)
- else:
- oldInstalls = "%s %s %s on %s -" % (product, version, arch, name)
-
- if not tests["product"]:
- oldInstalls += _(" Product mismatch.")
- if not tests["version"]:
- oldInstalls += _(" Version mismatch.")
- if not tests["arch"]:
- oldInstalls += _(" Architecture mismatch.")
- oldInstalls += "\n"
- rc = anaconda.intf.messageWindow(_("Cannot Upgrade"),
- _("Your current installation cannot be upgraded. This "
- "is likely due to it being too old. Only the previous two "
- "releases may be upgraded. To upgrade older releases "
- "you must first upgrade through all intermediate releases.\n\n"
- "%s") % oldInstalls,
- type="custom", custom_icon=["error","error"],
- custom_buttons=[_("_Exit installer"), _("_Continue")])
- if rc == 0:
- sys.exit(0)
-
- setUpgradeRoot(anaconda)
-
- if anaconda.rootParts is not None and len(anaconda.rootParts) > 0:
- anaconda.dispatch.request_steps_gently("findinstall")
- else:
- anaconda.dispatch.skip_steps("findinstall")
-
-def bindMountDevDirectory(instPath):
- getFormat("bind",
- device="/dev",
- mountpoint="/dev",
- exists=True).mount(chroot=instPath)
-
-# returns None if no filesystem exist to migrate
-def upgradeMigrateFind(anaconda):
- migents = anaconda.storage.migratableDevices
- if not migents or len(migents) < 1:
- anaconda.dispatch.skip_steps("upgrademigratefs")
- else:
- anaconda.dispatch.request_steps("upgrademigratefs")
-
-def copyFromSysimage(filename):
- """Mirrors filename from the sysimage on the ramdisk."""
- sysfile = os.path.normpath("%s/%s" % (ROOT_PATH, filename))
- if os.access(sysfile, os.R_OK):
- try:
- # remove our copy if we have one (think liveinstall)
- os.remove(filename)
- except OSError:
- pass
- try:
- shutil.copyfile(sysfile, filename)
- except OSError as e:
- log.error("Error copying %s to sysimage: %s" %(sysfile, e.strerror))
- return False
- else:
- log.error("Error copying %s to sysimage, file not accessible." % sysfile)
- return False
- return True
-
-def restoreTime(anaconda):
- """Load time setup for upgrade install.
-
- We need to find out the timezone and the UTC parameter of the old system and
- set the system time accordingly, so timestamps are set correctly for the
- files the upgrade procedure will create.
-
- This is pretty much what packages.setupTimezone() does in reverse.
- """
- if anaconda.dir == DISPATCH_BACK:
- return
- if os.environ.has_key("TZ"):
- del os.environ["TZ"]
- copyFromSysimage('/etc/localtime')
- copyFromSysimage('/etc/adjtime')
- if iutil.isS390():
- return
- args = [ "--hctosys" ]
- try:
- iutil.execWithRedirect("/sbin/hwclock", args,stdout = "/dev/tty5",
- stderr = "/dev/tty5")
- except RuntimeError:
- log.error("Failed to set the clock.")
-
-# XXX handle going backwards
-def upgradeMountFilesystems(anaconda):
- # mount everything and turn on swap
-
- try:
- mountExistingSystem(anaconda.storage.fsset, anaconda.upgradeRoot[0], allowDirty = 0)
- except ValueError as e:
- log.error("Error mounting filesystem: %s" % e)
- anaconda.intf.messageWindow(_("Mount failed"),
- _("The following error occurred when mounting the file "
- "systems listed in /etc/fstab. Please fix this problem "
- "and try to upgrade again.\n%s" % e))
- sys.exit(0)
- except IndexError as e:
- # The upgrade root is search earlier but we give the message here.
- log.debug("No upgrade root was found.")
- if anaconda.ksdata and anaconda.ksdata.upgrade.upgrade:
- anaconda.intf.messageWindow(_("Upgrade root not found"),
- _("The root for the previously installed system was not "
- "found."), type="custom",
- custom_icon="info",
- custom_buttons=[_("Exit installer")])
- sys.exit(0)
- else:
- rc = anaconda.intf.messageWindow(_("Upgrade root not found"),
- _("The root for the previously installed system was not "
- "found. You can exit installer or backtrack to choose "
- "installation instead of upgrade."),
- type="custom",
- custom_buttons = [ _("_Back"),
- _("_Exit installer") ],
- custom_icon="question")
- if rc == 0:
- return DISPATCH_BACK
- elif rc == 1:
- sys.exit(0)
-
- checkLinks = ( '/etc', '/var', '/var/lib', '/var/lib/rpm',
- '/boot', '/tmp', '/var/tmp', '/root',
- '/bin/sh', '/usr/tmp')
- badLinks = []
- for n in checkLinks:
- if not os.path.islink(ROOT_PATH + n): continue
- l = os.readlink(ROOT_PATH + n)
- if l[0] == '/':
- badLinks.append(n)
-
- if badLinks:
- message = _("The following files are absolute symbolic "
- "links, which we do not support during an "
- "upgrade. Please change them to relative "
- "symbolic links and restart the upgrade.\n\n")
- for n in badLinks:
- message = message + '\t' + n + '\n'
- anaconda.intf.messageWindow(_("Absolute Symlinks"), message)
- sys.exit(0)
-
- # fix for 80446
- badLinks = []
- mustBeLinks = ( '/usr/tmp', )
- for n in mustBeLinks:
- if not os.path.islink(ROOT_PATH + n):
- badLinks.append(n)
-
- if badLinks:
- message = _("The following are directories which should instead "
- "be symbolic links, which will cause problems with the "
- "upgrade. Please return them to their original state "
- "as symbolic links and restart the upgrade.\n\n")
- for n in badLinks:
- message = message + '\t' + n + '\n'
- anaconda.intf.messageWindow(_("Invalid Directories"), message)
- sys.exit(0)
-
- anaconda.storage.turnOnSwap(upgrading=True)
- anaconda.storage.mkDevRoot()
-
- # Move /etc/rpm/platform out of the way.
- if os.path.exists(ROOT_PATH + "/etc/rpm/platform"):
- shutil.move(ROOT_PATH + "/etc/rpm/platform",
- ROOT_PATH + "/etc/rpm/platform.rpmsave")
-
- # if they've been booting with selinux disabled, then we should
- # disable it during the install as well (#242510)
- try:
- if os.path.exists(ROOT_PATH + "/.autorelabel"):
- ctx = selinux.getfilecon(ROOT_PATH + "/.autorelabel")[1]
- if not ctx or ctx == "unlabeled":
- flags.selinux = False
- log.info("Disabled SELinux for upgrade based on /.autorelabel")
- except Exception as e:
- log.warning("error checking selinux state: %s" %(e,))
-
-def upgradeUsr(anaconda):
- """
- Handle the upgrade of /bin, /sbin, /lib, /lib64 to symlinks into /usr/
- This uses dracut's convertfs module
- """
- dirs = ["/bin", "/sbin", "/lib", "/lib64"]
- dirs = [ROOT_PATH+d for d in dirs]
- if all(map(os.path.islink, dirs)):
- log.info("upgradeusr dirs are already symlinks")
- return
-
- if anaconda.intf is not None:
- w = anaconda.intf.waitWindow(_("Upgrade /usr symlinks"),
- _("Running /usr merge script"))
-
- if iutil.execWithRedirect("/usr/lib/dracut/modules.d/30convertfs/convertfs.sh",
- [ROOT_PATH],
- stdout="/dev/tty5", stderr="/dev/tty5"):
- log.error("convertfs failed")
-
- if anaconda.intf is not None:
- w.pop()
- rc = anaconda.intf.messageWindow(_("/usr merge failed"),
- _("The /usr merge script failed. This is required"
- " for Fedora 17 to work. The upgrade cannot continue."
- "\n\n"))
- sys.exit(0)
- log.info("convertfs was successful")
-
- if anaconda.intf is not None:
- w.pop()
-
-def setSteps(anaconda):
- dispatch = anaconda.dispatch
- dispatch.reset_scheduling() # scrap what is scheduled
- # in case we are scheduling steps from the examine GUI, some of them are
- # already done:
- dispatch.schedule_steps_gently(
- "language",
- "keyboard",
- "filtertype",
- "filter",
- "storageinit",
- "findrootparts",
- "findinstall"
- )
- # schedule the rest:
- dispatch.schedule_steps(
- "upgrademount",
- "restoretime",
- "upgrademigfind",
- "upgrademigratefs",
- "enablefilesystems",
- "upgradecontinue",
- "upgradeusr",
- "reposetup",
- "upgbootloader",
- "postselection",
- "reipl",
- "install",
- "preinstallconfig",
- "installpackages",
- "postinstallconfig",
- "instbootloader",
- "dopostaction",
- "methodcomplete",
- "complete"
- )
-
- if not iutil.isX86() and not iutil.isS390():
- dispatch.skip_steps("bootloader")
-
- if not iutil.isX86():
- dispatch.skip_steps("upgbootloader")
-
- dispatch.skip_steps("cleardiskssel")
diff --git a/pyanaconda/vnc.py b/pyanaconda/vnc.py
index a7ccc7643..b06ac8743 100644
--- a/pyanaconda/vnc.py
+++ b/pyanaconda/vnc.py
@@ -80,12 +80,11 @@ class VncServer:
# see if we can sniff out network info
netinfo = network.Network()
- devices = netinfo.netdevices
active_devs = network.getActiveNetDevs()
self.ip = None
if active_devs != []:
- devname = devices[active_devs[0]].iface
+ devname = active_devs[0]
try:
ips = (isys.getIPAddresses(devname, version=4) +
isys.getIPAddresses(devname, version=6))
@@ -105,7 +104,7 @@ class VncServer:
except Exception as e:
log.debug("Exception caught trying to get host name of %s: %s" %
(ipstr, e))
- self.name = network.getDefaultHostname(self.anaconda)
+ self.name = network.getHostname()
else:
if len(hinfo) == 3:
self.name = hinfo[0]
@@ -187,7 +186,11 @@ class VncServer:
self.log.info(_("Starting VNC..."))
# Lets call it from here for now.
- self.initialize()
+ try:
+ self.initialize()
+ except Exception, e:
+ stdoutLog.critical("Could not initialize the VNC server: %s" % e)
+ sys.exit(1)
if self.password and len(self.password) < 6:
self.changeVNCPasswdWindow()
diff --git a/pyanaconda/yuminstall.py b/pyanaconda/yuminstall.py
index 0a3f29229..bb98b860c 100644
--- a/pyanaconda/yuminstall.py
+++ b/pyanaconda/yuminstall.py
@@ -1623,10 +1623,11 @@ reposdir=/etc/anaconda.repos.d,/tmp/updates/anaconda.repos.d,/tmp/product/anacon
if os.access("/etc/modprobe.d/anaconda.conf", os.R_OK):
shutil.copyfile("/etc/modprobe.d/anaconda.conf",
ROOT_PATH + "/etc/modprobe.d/anaconda.conf")
+ network.write_sysconfig_network()
+ network.disableIPV6()
+ network.copyConfigToPath(ROOT_PATH)
if not anaconda.ksdata:
- anaconda.instClass.setNetworkOnbootDefault(anaconda.network)
- anaconda.network.write()
- anaconda.network.copyConfigToPath()
+ anaconda.instClass.setNetworkOnbootDefault()
anaconda.storage.write()
else:
# ensure that /etc/mtab is a symlink to /proc/self/mounts