anaconda: update to 22.20.13-1

Apply diff anaconda-21.48.21-1..anaconda-22.20.13-1
pull/1/head
Marek Marczykowski-Górecki 9 years ago committed by Jon Griffiths
parent 47a0aea0c6
commit 701ced5ddb

@ -1,9 +0,0 @@
[anaconda.f21-branch]
file_filter = po/<lang>.po
source_file = po/anaconda.pot
source_lang = en
type = PO
[main]
host = https://www.transifex.com

@ -44,18 +44,16 @@ dist_sbin_SCRIPTS = anaconda
ARCHIVE_TAG = $(PACKAGE_NAME)-$(PACKAGE_VERSION)-$(PACKAGE_RELEASE)
TX_PULL_ARGS = -a -f
TX_PUSH_ARGS = -s
INSTALLATION_GUIDE_REPO_URL = git://git.fedorahosted.org/git/docs/install-guide.git
ZANATA_PULL_ARGS = --transdir $(srcdir)/po/
ZANATA_PUSH_ARGS = --srcdir $(srcdir)/po/ --push-type source --force
tag:
@git tag -s -a -m "Tag as $(ARCHIVE_TAG)" $(ARCHIVE_TAG)
@echo "Tagged as $(ARCHIVE_TAG)"
po-pull:
rpm -q transifex-client &>/dev/null || ( echo "need to run: yum install transifex-client"; exit 1 )
tx pull $(TX_PULL_ARGS)
rpm -q zanata-python-client &>/dev/null || ( echo "need to run: yum install zanata-python-client"; exit 1 )
zanata pull $(ZANATA_PULL_ARGS)
po-empty:
for lingua in $$(grep -v '^#' $(srcdir)/po/LINGUAS) ; do \
@ -64,12 +62,12 @@ po-empty:
exit 1 ; \
done
scratch: po-empty get-help
scratch: po-empty
$(MAKE) ARCHIVE_TAG=HEAD dist
git checkout -- $(srcdir)/po/$(PACKAGE_NAME).pot
scratch-bumpver: po-empty get-help
@opts="-n $(PACKAGE_NAME) -v $(PACKAGE_VERSION) -r $(PACKAGE_RELEASE) -b $(PACKAGE_BUGREPORT)" ; \
scratch-bumpver: po-empty
@opts="-S -n $(PACKAGE_NAME) -v $(PACKAGE_VERSION) -r $(PACKAGE_RELEASE) -b $(PACKAGE_BUGREPORT)" ; \
if [ ! -z "$(IGNORE)" ]; then \
opts="$${opts} -i $(IGNORE)" ; \
fi ; \
@ -79,18 +77,12 @@ scratch-bumpver: po-empty get-help
if [ ! -z "$(BZDEBUG)" ]; then \
opts="$${opts} -d" ; \
fi ; \
if [ ! -z "$(SKIP_ACKS)" ]; then \
opts="$${opts} -s" ; \
fi ; \
( cd $(srcdir) && scripts/makebumpver $${opts} ) || exit 1 ; \
$(MAKE) -C po $(PACKAGE_NAME).pot-update ;
( cd $(srcdir) && scripts/makebumpver --skip-zanata $${opts} ) || exit 1 ; \
$(MAKE) -C po $(PACKAGE_NAME).pot-update ;
release: get-help
release:
$(MAKE) dist && $(MAKE) tag && git checkout -- $(srcdir)/po/$(PACKAGE_NAME).pot
api:
doxygen docs/api.cfg
bumpver: po-pull
@opts="-n $(PACKAGE_NAME) -v $(PACKAGE_VERSION) -r $(PACKAGE_RELEASE) -b $(PACKAGE_BUGREPORT)" ; \
if [ ! -z "$(IGNORE)" ]; then \
@ -107,21 +99,21 @@ bumpver: po-pull
fi ; \
( cd $(srcdir) && scripts/makebumpver $${opts} ) || exit 1 ; \
$(MAKE) -C po $(PACKAGE_NAME).pot-update && \
tx push $(TX_PUSH_ARGS)
zanata push $(ZANATA_PUSH_ARGS)
# Install all packages specified as BuildRequires in the Anaconda specfile
# -> installs packages needed to build Anaconda
install-buildrequires:
srcdir="$(srcdir)" && \
: $${srcdir:=.} && \
yum install $$(grep BuildRequires: $${srcdir}/anaconda.spec.in | cut -d ' ' -f 2)
yum install $$(grep ^BuildRequires: $${srcdir}/anaconda.spec.in | cut -d ' ' -f 2)
# Install all packages specified as Requires in the Anaconda specfile
# -> installs packages needed to run Anaconda and the Anaconda unit tests
install-requires:
srcdir="$(srcdir)" && \
: $${srcdir:=.} && \
yum install $$(grep Requires: $${srcdir}/anaconda.spec.in | cut -d ' ' -f 2)
yum install $$(grep ^Requires: $${srcdir}/anaconda.spec.in | cut -d ' ' -f 2 | grep -v ^anaconda)
# Generate an updates.img based on the changed files since the release
# was tagged. Updates are copied to ./updates-img and then the image is
@ -141,43 +133,6 @@ unittests-logpicker:
PYTHONPATH=$(builddir)/pyanaconda/isys/.libs:tests/:$(srcdir):utils/ nosetests -v old_tests/logpicker_test
# GUI TESTING
runspoke:
ANACONDA_DATA=$(srcdir)/data \
ANACONDA_WIDGETS_OVERRIDES=$(srcdir)/widgets/python \
ANACONDA_WIDGETS_DATA=$(srcdir)/widgets/data \
ANACONDA_INSTALL_CLASSES=$(srcdir)/pyanaconda/installclasses \
PYTHONPATH=$(srcdir):$(builddir)/pyanaconda/isys/.libs:$(srcdir)/widgets/python/:$(builddir)/widgets/src/.libs/ \
LD_LIBRARY_PATH=$(builddir)/widgets/src/.libs \
UIPATH=$(srcdir)/pyanaconda/ui/gui/ \
GI_TYPELIB_PATH=$(builddir)/widgets/src/ \
$(srcdir)/pyanaconda/ui/gui/tools/run-spoke.py ${SPOKE_MODULE} ${SPOKE_CLASS}
runhub:
ANACONDA_DATA=$(srcdir)/data \
ANACONDA_WIDGETS_OVERRIDES=$(srcdir)/widgets/python \
ANACONDA_WIDGETS_DATA=$(srcdir)/widgets/data \
ANACONDA_INSTALL_CLASSES=$(srcdir)/pyanaconda/installclasses \
PYTHONPATH=$(srcdir):$(builddir)/pyanaconda/isys/.libs:$(srcdir)/widgets/python/:$(builddir)/widgets/src/.libs/ \
LD_LIBRARY_PATH=$(builddir)/widgets/src/.libs \
UIPATH=$(srcdir)/pyanaconda/ui/gui/ \
GI_TYPELIB_PATH=$(builddir)/widgets/src/ \
$(srcdir)/pyanaconda/ui/gui/tools/run-hub.py ${HUB_MODULE} ${HUB_CLASS}
runtextspoke:
ANACONDA_DATA=$(srcdir)/data \
ANACONDA_INSTALL_CLASSES=$(srcdir)/pyanaconda/installclasses \
PYTHONPATH=$(srcdir):$(builddir)/pyanaconda/isys/.libs:$(srcdir)/widgets/python/:$(builddir)/widgets/src/.libs/ \
LD_LIBRARY_PATH=$(builddir)/widgets/src/.libs \
$(srcdir)/pyanaconda/ui/tui/tools/run-text-spoke.py ${SPOKE_MODULE} ${SPOKE_CLASS}
runtexthub:
ANACONDA_DATA=$(srcdir)/data \
ANACONDA_INSTALL_CLASSES=$(srcdir)/pyanaconda/installclasses \
PYTHONPATH=$(srcdir):$(builddir)/pyanaconda/isys/.libs:$(srcdir)/widgets/python/:$(builddir)/widgets/src/.libs/ \
LD_LIBRARY_PATH=$(builddir)/widgets/src/.libs \
$(srcdir)/pyanaconda/ui/tui/tools/run-text-hub.py ${HUB_MODULE} ${HUB_CLASS}
runglade:
ANACONDA_DATA=$(srcdir)/data \
ANACONDA_WIDGETS_OVERRIDES=$(srcdir)/widgets/python \
@ -190,24 +145,3 @@ runglade:
GLADE_CATALOG_SEARCH_PATH=$(srcdir)/widgets/glade \
GLADE_MODULE_SEARCH_PATH=$(builddir)/widgets/src/.libs \
glade ${GLADE_FILE}
# Get content for the Anaconda built-in help system by cloning the
# installation guide git repository and running the help processing
# script (it is part of the repository).
# Once the help content has been generated copy it to our help folder,
# so that it can be included in the tarball.
# Skip the git clone if the repository already exists but run git pull
# to make sure it is up to date. We also clone the repository
# without history as it si rather big (>400 MB!), which is quite
# an overkill for <100 kB of help conent. :)
get-help:
if [ ! -d "install-guide" ]; then \
if [ -f "installation_guide_repo_url" ]; then \
git clone --depth=1 `cat installation_guide_repo_url` ; \
else \
git clone --depth=1 $(INSTALLATION_GUIDE_REPO_URL) ; \
fi ; \
fi ; \
( cd install-guide && git pull --rebase && python prepare_anaconda_help_content.py )
cp -r install-guide/anaconda_help_content/* $(srcdir)/data/help

@ -0,0 +1,75 @@
dnl autoconf macros for anaconda
dnl
dnl Copyright (C) 2014 Red Hat, Inc.
dnl
dnl This program is free software; you can redistribute it and/or modify
dnl it under the terms of the GNU Lesser General Public License as published
dnl by the Free Software Foundation; either version 2.1 of the License, or
dnl (at your option) any later version.
dnl
dnl This program is distributed in the hope that it will be useful,
dnl but WITHOUT ANY WARRANTY; without even the implied warranty of
dnl MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
dnl GNU Lesser General Public License for more details.
dnl
dnl You should have received a copy of the GNU Lesser General Public License
dnl along with this program. If not, see <http://www.gnu.org/licenses/>.
dnl
dnl Author: David Shea <dshea@redhat.com>
dnl ANACONDA_SOFT_FAILURE(MESSAGE)
dnl
dnl Store a message that in some contexts could be considered indicative
dnl of a failure, but in other contexts could be indicative of who cares.
dnl
dnl For example, the anaconda widgets require a version of gtk3-devel of
dnl particular newness, and the widgets will fail to build if this library
dnl and headers are not available. On the other hand, gtk3 isn't required at
dnl all for most everything else, so it would be nice if a missing or old
dnl gtk3-devel didn't halt the configure script.
dnl
dnl Any message sent to this macro will be stored, and they can all be
dnl displayed at the end of configure using the ANACONDA_FAILURES macro.
AC_DEFUN([ANACONDA_SOFT_FAILURE], [dnl
AS_IF([test x"$anaconda_failure_messages" = x],
[anaconda_failure_messages="[$1]"],
[anaconda_failure_messages="$anaconda_failure_messages
[$1]"
])])dnl
dnl ANACONDA_PKG_CHECK_MODULES(VARIABLE-PREFIX, MODULES)
dnl
dnl Check whether a module is available, using pkg-config. Instead of failing
dnl if a module is not found, store the failure in a message that can be
dnl printed using the ANACONDA_FAILURES macro.
dnl
dnl The syntax and behavior of VARIABLE-PREFIX and MODULES is the same as for
dnl PKG_CHECK_MODULES.
AC_DEFUN([ANACONDA_PKG_CHECK_MODULES], [dnl
PKG_CHECK_MODULES([$1], [$2], [], [ANACONDA_SOFT_FAILURE($[$1]_PKG_ERRORS)])
])dnl
dnl ANACONDA_PKG_CHECK_EXISTS(MODULES)
dnl
dnl Check whether a module exists, using pkg-config. Instead of failing
dnl if a module is not found, store the failure in a message that can be
dnl printed using the ANACONDA_FAILURES macro.
dnl
dnl The syntax and behavior of MOUDLES is the same as for
dnl PKG_CHECK_EXISTS.
AC_DEFUN([ANACONDA_PKG_CHECK_EXISTS], [dnl
PKG_CHECK_EXISTS([$1], [], [ANACONDA_SOFT_FAILURE([Check for $1 failed])])
])dnl
dnl ANACONDA_FAILURES
dnl
dnl Print the failure messages collected by ANACONDA_SOFT_FAILURE and
dnl ANACONDA_PKG_CHECK_MODULES
AC_DEFUN([ANACONDA_FAILURES], [dnl
AS_IF([test x"$anaconda_failure_messages" = x], [], [dnl
echo ""
echo "*** Anaconda encountered the following issues during configuration:"
echo "$anaconda_failure_messages"
echo ""
echo "*** Anaconda will not successfully build without these missing dependencies"
])])dnl

@ -1,4 +1,4 @@
#!/usr/bin/python
#!/usr/bin/python2
#
# anaconda: The Red Hat Linux Installation program
#
@ -44,85 +44,11 @@ if ("debug=1" in proc_cmdline) or ("debug" in proc_cmdline):
cov.start()
import atexit, sys, os, time, subprocess, signal, errno
import atexit, sys, os, time, signal, stat, errno
# Install a global SIGCHLD handler to keep track of things that should be
# running for as long as anaconda does. The dictionary is of the form
# {pid: name, ...}. The handler will raise ExitError (defined below), so if
# not caught a SIGCHLD from a watched process will halt anaconda.
forever_pids = {}
class ExitError(RuntimeError):
pass
def sigchld_handler(num, frame):
# Check whether anything in the list of processes being watched has
# exited. We don't want to call waitpid(-1), since that would break
# anything else using wait/waitpid (like the subprocess module).
exited_pids = []
exn_message = []
for child_pid in forever_pids:
try:
pid_result, status = iutil.eintr_retry_call(os.waitpid, child_pid, os.WNOHANG)
except OSError as e:
if e.errno == errno.ECHILD:
continue
if pid_result:
proc_name = forever_pids[child_pid]
exited_pids.append(child_pid)
if os.WIFEXITED(status):
status_str = "with status %s" % os.WEXITSTATUS(status)
elif os.WIFSIGNALED(status):
status_str = "on signal %s" % os.WTERMSIG(status)
else:
status_str = "with unknown status code %s" % status
exn_message.append("%s exited %s" % (proc_name, status_str))
for child_pid in exited_pids:
del forever_pids[child_pid]
if exn_message:
raise ExitError(", ".join(exn_message))
signal.signal(signal.SIGCHLD, sigchld_handler)
# Fork a new process and add it to forever_pids. The return values are the
# the same as os.fork, but neither the parent nor the child will return
# until the process is watched.
def start_watched_pid(proc_name):
readpipe, writepipe = os.pipe()
childpid = os.fork()
if childpid == 0:
# No need for the write pipe in the child
iutil.eintr_retry_call(os.close, writepipe)
# Wait for the parent to signal that it's ready to return
iutil.eintr_retry_call(os.read, readpipe, 1)
# Ready to go
iutil.eintr_retry_call(os.close, readpipe)
return childpid
else:
# No need for the read pipe in the parent
iutil.eintr_retry_call(os.close, readpipe)
# Add the pid to the list of watched pids
forever_pids[childpid] = proc_name
# Signal to the child that we're ready to return
# D is for Done
iutil.eintr_retry_call(os.write, writepipe, 'D')
iutil.eintr_retry_call(os.close, writepipe)
return childpid
def exitHandler(rebootData, storage, exitCode=None):
def exitHandler(rebootData, storage):
# Clear the list of watched PIDs.
global forever_pids
forever_pids = {}
iutil.unwatchAllProcesses()
# stop and save coverage here b/c later the file system may be unavailable
if coverage is not None:
@ -133,9 +59,6 @@ def exitHandler(rebootData, storage, exitCode=None):
if flags.usevnc:
vnc.shutdownServer()
if exitCode:
anaconda.intf.shutdown()
if "nokill" in flags.cmdline:
iutil.vtActivate(1)
print("anaconda halting due to nokill flag.")
@ -158,10 +81,18 @@ def exitHandler(rebootData, storage, exitCode=None):
uninhibit_screensaver(anaconda.dbus_session_connection, anaconda.dbus_inhibit_id)
anaconda.dbus_inhibit_id = None
# Unsetup the payload, which most usefully unmounts live images
if anaconda.payload:
anaconda.payload.unsetup()
# Clean up the PID file
if pidfile_created:
os.unlink(pidfile_path)
if not flags.imageInstall and not flags.livecdInstall \
and not flags.dirInstall:
from pykickstart.constants import KS_SHUTDOWN, KS_WAIT
from pyanaconda.iutil import dracut_eject, get_mount_paths
from pyanaconda.iutil import dracut_eject, get_mount_paths, execWithRedirect
if flags.eject or rebootData.eject:
for cdrom in storage.devicetree.getDevicesByType("cdrom"):
@ -169,11 +100,11 @@ def exitHandler(rebootData, storage, exitCode=None):
dracut_eject(cdrom.path)
if rebootData.action == KS_SHUTDOWN:
subprocess.Popen(["systemctl", "--no-wall", "poweroff"])
execWithRedirect("systemctl", ["--no-wall", "poweroff"])
elif rebootData.action == KS_WAIT:
subprocess.Popen(["systemctl", "--no-wall", "halt"])
execWithRedirect("systemctl", ["--no-wall", "halt"])
else: # reboot action is KS_REBOOT or None
subprocess.Popen(["systemctl", "--no-wall", "reboot"])
execWithRedirect("systemctl", ["--no-wall", "reboot"])
def startSpiceVDAgent():
status = iutil.execWithRedirect("spice-vdagent", [])
@ -184,125 +115,37 @@ def startSpiceVDAgent():
log.info("Started spice-vdagent.")
def startX11():
# Start X11 with its USR1 handler set to ignore, which will make it send
# us SIGUSR1 if it succeeds. If it fails, catch SIGCHLD and bomb out.
# Use a list so the value can be modified from the handler
x11_started = [False]
def sigusr1_handler(num, frame):
log.debug("X server has signalled a successful start.")
x11_started[0] = True
# Fail after, let's say a minute, in case something weird happens
# and we don't receive SIGUSR1
def sigalrm_handler(num, frame):
# Check that it didn't make it under the wire
if x11_started[0]:
return
log.error("Timeout trying to start the X server")
raise ExitError("Timeout trying to start the X server")
# Open /dev/tty5 for stdout and stderr redirects
xfd = open("/dev/tty5", "a")
try:
old_sigusr1_handler = signal.signal(signal.SIGUSR1, sigusr1_handler)
old_sigalrm_handler = signal.signal(signal.SIGALRM, sigalrm_handler)
# Start Xorg and wait for it become ready
iutil.startX(["Xorg", "-br", "-logfile", "/tmp/X.log",
":%s" % constants.X_DISPLAY_NUMBER, "vt6", "-s", "1440", "-ac",
"-nolisten", "tcp", "-dpi", "96",
"-noreset"], output_redirect=xfd)
childpid = start_watched_pid("Xorg")
# function to handle X startup special issues for anaconda
def doStartupX11Actions():
"""Start window manager"""
from copy import copy
if not childpid:
# after this point the method should never return (or throw an exception
# outside)
try:
# dup /dev/tty5 to stdout and stderr
xfd = iutil.eintr_retry_call(os.open, "/dev/tty5", os.O_WRONLY | os.O_APPEND)
iutil.eintr_retry_call(os.dup2, xfd, sys.stdout.fileno())
iutil.eintr_retry_call(os.dup2, xfd, sys.stderr.fileno())
# Close all other file descriptors
try:
maxfd = os.sysconf("SC_OPEN_MAX")
except ValueError:
maxfd = 1024
os.closerange(3, maxfd)
# Replace the SIGUSR1 handler with SIG_IGN as described above
signal.signal(signal.SIGUSR1, signal.SIG_IGN)
# Run it
os.execlp("Xorg", "Xorg", "-br",
"-logfile", "/tmp/X.log",
":1", "vt6", "-s", "1440", "-ac",
"-nolisten", "tcp", "-dpi", "96",
"-noreset")
# We should never get here
raise OSError(0, "Unable to exec")
except BaseException as e:
# catch all possible exceptions
# Reopen the logger in case it was closed by closerange.
# Use a different name because assigning to variables across a
# fork confuses the hell out of python.
child_log = logging.getLogger("anaconda")
child_log.error("Problems running Xorg: %s", e)
os._exit(1)
# Parent process
# Start the timer
signal.alarm(60)
# Wait for SIGUSR1
while not x11_started[0]:
signal.pause()
# Now that X is started, make $DISPLAY available
os.environ["DISPLAY"] = ":1"
finally:
# Put everything back where it was
signal.alarm(0)
signal.signal(signal.SIGUSR1, old_sigusr1_handler)
signal.signal(signal.SIGALRM, old_sigalrm_handler)
def startMetacityWM():
# When metacity actually connects to the X server is unknowable, but
# fortunately it doesn't matter. metacity does not need to be the first
# connection to Xorg, and if anaconda starts up before metacity, metacity
# will just take over and maximize the window and make everything right,
# fingers crossed.
# Add XDG_DATA_DIRS to the environment to pull in our overridden schema
# files.
env_bak = copy(os.environ)
if 'XDG_DATA_DIRS' in os.environ:
xdg_data_dirs = '/usr/share/anaconda/window-manager:' + os.environ['XDG_DATA_DIRS']
else:
xdg_data_dirs = '/usr/share/anaconda/window-manager:/usr/share'
childpid = start_watched_pid("metacity")
if not childpid:
# after this point the method should never return (or throw an exception
# outside)
try:
returncode = iutil.execWithRedirect('metacity', ["--display", ":1", "--sm-disable"])
except BaseException as e:
# catch all possible exceptions
log.error("Problems running the window manager: %s", e)
os._exit(1)
log.info("The window manager has terminated.")
os._exit(returncode)
return childpid
def startAuditDaemon():
childpid = os.fork()
if not childpid:
cmd = '/sbin/auditd'
try:
os.execl(cmd, cmd)
except OSError as e:
log.error("Error running the audit daemon: %s", e)
os._exit(0)
# auditd will turn into a daemon so catch the immediate child pid now:
iutil.eintr_retry_call(os.waitpid, childpid, 0)
# function to handle X startup special issues for anaconda
def doStartupX11Actions():
"""Start window manager"""
# now start up the window manager
wm_pid = startMetacityWM()
log.info("Starting window manager, pid %s.", wm_pid)
childproc = iutil.startProgram(["metacity", "--display", ":1", "--sm-disable"],
env_add={'XDG_DATA_DIRS': xdg_data_dirs})
os.environ = env_bak
iutil.watchProcess(childproc, "metacity")
def set_x_resolution(runres):
if runres and opts.display_mode == 'g' and not flags.usevnc:
@ -474,6 +317,7 @@ def parseArguments(argv=None, boot_cmdline=None):
# Obvious
ap.add_argument("--loglevel", metavar="LEVEL", help=help_parser.help_text("loglevel"))
ap.add_argument("--syslog", metavar="HOST[:PORT]", help=help_parser.help_text("syslog"))
ap.add_argument("--remotelog", metavar="HOST:PORT", help=help_parser.help_text("remotelog"))
from pykickstart.constants import SELINUX_DISABLED, SELINUX_ENFORCING
from pyanaconda.constants import SELINUX_DEFAULT
@ -519,8 +363,8 @@ def parseArguments(argv=None, boot_cmdline=None):
help=help_parser.help_text("extlinux"))
ap.add_argument("--nombr", action="store_true", default=False,
help=help_parser.help_text("nombr"))
ap.add_argument("--dnf", action="store_true", default=False,
help=help_parser.help_text("dnf"))
ap.add_argument("--nodnf", action="store_false", dest="dnf", default=True,
help=help_parser.help_text("nodnf"))
ap.add_argument("--mpathfriendlynames", action="store_true", default=True,
help=help_parser.help_text("mpathfriendlynames"))
@ -541,6 +385,12 @@ def setupPythonPath():
sys.path.extend(ADDON_PATHS)
def setupEnvironment():
from pyanaconda.users import createLuserConf
# This method is run before any threads are started, so this is the one
# point where it's ok to modify the environment.
# pylint: disable=environment-modify
# Silly GNOME stuff
if 'HOME' in os.environ and not "XAUTHORITY" in os.environ:
os.environ['XAUTHORITY'] = os.environ['HOME'] + '/.Xauthority'
@ -559,6 +409,16 @@ def setupEnvironment():
if "LD_PRELOAD" in os.environ:
del os.environ["LD_PRELOAD"]
# Go ahead and set $DISPLAY whether we're going to use X or not
if 'DISPLAY' in os.environ:
flags.preexisting_x11 = True
else:
os.environ["DISPLAY"] = ":%s" % constants.X_DISPLAY_NUMBER
# Call createLuserConf now to setup $LIBUSER_CONF
# the config file can change later but the environment variable cannot
createLuserConf(iutil.getSysroot())
def setupLoggingFromOpts(options):
if (options.debug or options.updateSrc) and not options.loglevel:
# debugging means debug logging if an explicit level hasn't been st
@ -567,15 +427,24 @@ def setupLoggingFromOpts(options):
if options.loglevel and options.loglevel in anaconda_log.logLevelMap:
log.info("Switching logging level to %s", options.loglevel)
level = anaconda_log.logLevelMap[options.loglevel]
anaconda_log.logger.tty_loglevel = level
anaconda_log.logger.loglevel = level
anaconda_log.setHandlersLevel(log, level)
storage_log = logging.getLogger("storage")
anaconda_log.setHandlersLevel(storage_log, level)
packaging_log = logging.getLogger("packaging")
anaconda_log.setHandlersLevel(packaging_log, level)
if options.syslog:
anaconda_log.logger.updateRemote(options.syslog)
if can_touch_runtime_system("syslog setup"):
if options.syslog:
anaconda_log.logger.updateRemote(options.syslog)
if options.remotelog:
try:
host, port = options.remotelog.split(":", 1)
port = int(port)
anaconda_log.logger.setup_remotelog(host, port)
except ValueError:
log.error("Could not setup remotelog with %s", options.remotelog)
def gtk_warning(title, reason):
from gi.repository import Gtk
@ -643,6 +512,8 @@ def check_memory(anaconda, options, display_mode=None):
reason % reason_args,
buttons = (_("OK"),))
screen.finish()
iutil.ipmi_report(constants.IPMI_ABORTED)
sys.exit(1)
# override display mode if machine cannot nicely run X
@ -658,6 +529,7 @@ def check_memory(anaconda, options, display_mode=None):
stdoutLog.warning(reason % reason_args)
title = livecd_title
gtk_warning(title, reason % reason_args)
iutil.ipmi_report(constants.IPMI_ABORTED)
sys.exit(1)
else:
reason += nolivecd_extra
@ -693,7 +565,7 @@ def setupDisplay(anaconda, options, addons=None):
# Only consider vncconnect when vnc is a param
if options.vncconnect:
cargs = string.split(options.vncconnect, ":")
cargs = options.vncconnect.split(":")
vncS.vncconnecthost = cargs[0]
if len(cargs) > 1 and len(cargs[1]) > 0:
if len(cargs[1]) > 0:
@ -755,9 +627,6 @@ def setupDisplay(anaconda, options, addons=None):
stdoutLog.warning("Not asking for VNC because we don't have Xvnc")
flags.vncquestion = False
if 'DISPLAY' in os.environ:
flags.preexisting_x11 = True
# Should we try to start Xorg?
want_x = anaconda.displayMode == 'g' and \
not (flags.preexisting_x11 or flags.usevnc)
@ -845,6 +714,7 @@ def prompt_for_ssh():
if not ip:
stdoutLog.error("No IP addresses found, cannot continue installation.")
iutil.ipmi_report(constants.IPMI_ABORTED)
sys.exit(1)
ipstr = ip
@ -915,8 +785,7 @@ if __name__ == "__main__":
initThreading()
from pyanaconda.threads import threadMgr
import gettext
_ = lambda x: gettext.ldgettext("anaconda", x)
from pyanaconda.i18n import _
from pyanaconda import constants
from pyanaconda.addons import collect_addon_paths
@ -950,7 +819,7 @@ if __name__ == "__main__":
# see if we're on s390x and if we've got an ssh connection
uname = os.uname()
if uname[4] == 's390x':
if 'TMUX' not in os.environ and not flags.ksprompt and not flags.imageInstall:
if 'TMUX' not in os.environ and 'ks' not in flags.cmdline and not flags.imageInstall:
prompt_for_ssh()
sys.exit(0)
@ -969,12 +838,13 @@ if __name__ == "__main__":
from pyanaconda import isys
import string
from pyanaconda import iutil
iutil.ipmi_report(constants.IPMI_STARTED)
if opts.images and opts.dirinstall:
stdoutLog.error("--images and --dirinstall cannot be used at the same time")
iutil.ipmi_report(constants.IPMI_ABORTED)
sys.exit(1)
elif opts.dirinstall:
if opts.dirinstall is True:
@ -1031,20 +901,97 @@ if __name__ == "__main__":
from pyanaconda.anaconda import Anaconda
anaconda = Anaconda()
iutil.setup_translations(gettext)
iutil.setup_translations()
# reset python's default SIGINT handler
signal.signal(signal.SIGINT, signal.SIG_IGN)
signal.signal(signal.SIGSEGV, isys.handleSegv)
signal.signal(signal.SIGTERM, lambda num, frame: sys.exit(1))
# synchronously-delivered signals such as SIGSEGV and SIGILL cannot be
# handled properly from python, so install signal handlers from the C
# function in isys.
isys.installSyncSignalHandlers()
setupEnvironment()
# make sure we have /var/log soon, some programs fail to start without it
iutil.mkdirChain("/var/log")
pidfile = open("/var/run/anaconda.pid", "w")
pidfile.write("%s\n" % (os.getpid(),))
del pidfile
# Share these with the exit handler, installed later
pidfile_path = '/var/run/anaconda.pid'
pidfile_created = False
# Look for a stale pid file
try:
with open(pidfile_path, 'r') as pidfile:
pid = int(pidfile.read())
except IOError as e:
# Ignore errors due to the file not existing. Other errors mean (most
# likely) that we're not running as root, there's a filesystem error,
# or someone filled our PID file with garbage, so just let those be
# raised.
if e.errno != errno.ENOENT:
raise
else:
# Is the PID still running?
if not os.path.isdir("/proc/%s" % pid):
log.info("Removing stale PID file: %s no longer running", pid)
os.unlink(pidfile_path)
# Is the PID anaconda?
else:
try:
with open("/proc/%s/stat" % pid, "r") as pidstat:
# The part we care about is in the start, "PID (name) ..."
procname = pidstat.read().split(' ', 2)[1]
if procname != "(anaconda)":
log.info("Removing stale PID file: PID %s is now %s", pid, procname)
os.unlink(pidfile_path)
# If it is anaconda, let the pidfile creation below fail
# and print an error
except IOError as e:
# Ignore failures due to the file not existing in case the
# process ended while we were trying to read about it. Assume
# in this case that the process was another anaconda instance,
# and the PID file was cleaned up.
# If the process ended between open and read, we'll get ESRCH
if e.errno not in (errno.ENOENT, errno.ESRCH):
raise
# Attempt to create the pidfile
try:
# Set all of the read/write bits and let umask make it make sense
pidfile = iutil.eintr_retry_call(os.open, pidfile_path, os.O_WRONLY|os.O_CREAT|os.O_EXCL,
stat.S_IRUSR|stat.S_IWUSR|stat.S_IRGRP|stat.S_IWGRP|stat.S_IROTH|stat.S_IWOTH)
pidfile_created = True
iutil.eintr_retry_call(os.write, pidfile, "%s\n" % os.getpid())
iutil.eintr_retry_call(os.close, pidfile)
except OSError as e:
# If the failure was anything other than EEXIST during the open call,
# just re-raise the exception
if e.errno != errno.EEXIST:
raise
log.error("%s already exists, exiting", pidfile_path)
# If we had a $DISPLAY at start and zenity is available, we may be
# running in a live environment and we can display an error dialog.
# Otherwise just print an error.
if flags.preexisting_x11 and os.access("/usr/bin/zenity", os.X_OK):
# The module-level _() calls are ok here because the language may
# be set from the live environment in this case, and anaconda's
# language setup hasn't happened yet.
# pylint: disable=found-_-in-module-class
iutil.execWithRedirect("zenity",
["--error", "--title", _("Unable to create PID file"), "--text",
_("Anaconda is unable to create %s because the file" +
" already exists. Anaconda is already running, or a previous instance" +
" of anaconda has crashed.") % pidfile_path])
else:
print("%s already exists, exiting" % pidfile_path)
iutil.ipmi_report(constants.IPMI_FAILED)
sys.exit(1)
# add our own additional signal handlers
signal.signal(signal.SIGHUP, startDebugger)
@ -1101,16 +1048,20 @@ if __name__ == "__main__":
# text console with a traceback instead of being left looking at a blank
# screen. python-meh will replace this excepthook with its own handler
# once it gets going.
if not flags.imageInstall and not flags.livecdInstall \
and not flags.dirInstall:
if can_touch_runtime_system("early exception handler"):
def _earlyExceptionHandler(ty, value, traceback):
iutil.ipmi_report(constants.IPMI_FAILED)
iutil.vtActivate(1)
return sys.__excepthook__(ty, value, traceback)
sys.excepthook = _earlyExceptionHandler
if can_touch_runtime_system("start audit daemon"):
startAuditDaemon()
# auditd will turn into a daemon and exit. Ignore startup errors
try:
iutil.execWithRedirect("/sbin/auditd", [])
except OSError:
pass
# setup links required for all install types
for i in ("services", "protocols", "nsswitch.conf", "joe", "selinux",
@ -1124,7 +1075,7 @@ if __name__ == "__main__":
log.info("anaconda called with cmdline = %s", sys.argv)
log.info("Default encoding = %s ", sys.getdefaultencoding())
os.system("udevadm control --env=ANACONDA=1")
iutil.execWithRedirect("udevadm", ["control", "--env=ANACONDA=1"])
# Collect all addon paths
addon_paths = collect_addon_paths(constants.ADDON_PATHS)
@ -1134,6 +1085,11 @@ if __name__ == "__main__":
# shipped with the installation media.
ksdata = None
if opts.ksfile and not opts.liveinst:
if not os.path.exists(opts.ksfile):
stdoutLog.error("Kickstart file %s is missing.", opts.ksfile)
iutil.ipmi_report(constants.IPMI_ABORTED)
sys.exit(1)
flags.automatedInstall = True
flags.eject = False
ksFiles = [opts.ksfile]
@ -1197,14 +1153,14 @@ if __name__ == "__main__":
log.info("Failed to parse proxy \"%s\": %s", anaconda.proxy, e)
else:
# Set environmental variables to be used by pre/post scripts
os.environ["PROXY"] = proxy.noauth_url
os.environ["PROXY_USER"] = proxy.username or ""
os.environ["PROXY_PASSWORD"] = proxy.password or ""
iutil.setenv("PROXY", proxy.noauth_url)
iutil.setenv("PROXY_USER", proxy.username or "")
iutil.setenv("PROXY_PASSWORD", proxy.password or "")
# Variables used by curl, libreport, etc.
os.environ["http_proxy"] = proxy.url
os.environ["ftp_proxy"] = proxy.url
os.environ["HTTPS_PROXY"] = proxy.url
iutil.setenv("http_proxy", proxy.url)
iutil.setenv("ftp_proxy", proxy.url)
iutil.setenv("HTTPS_PROXY", proxy.url)
if flags.noverifyssl:
ksdata.method.noverifyssl = flags.noverifyssl
@ -1263,34 +1219,57 @@ if __name__ == "__main__":
from pyanaconda import localization
# Set the language before loading an interface, when it may be too late.
# check if the LANG environmental variable is set
env_lang = os.environ.get("LANG")
if env_lang is not None:
# parse it using langtable
env_langs = localization.get_language_locales(env_lang)
# if parsed LANG is the same as our default language - ignore it;
# otherwise use it as valid language candidate
if env_langs and env_langs[0] != constants.DEFAULT_LANG:
env_lang = env_langs[0] # the first language is the best match
else:
env_lang = None
requested_lang = opts.lang or ksdata.lang.lang or env_lang
if requested_lang:
locales = localization.get_language_locales(requested_lang)
if locales:
localization.setup_locale(locales[0], ksdata.lang)
ksdata.lang.seen = True
else:
log.error("Invalid locale '%s' given on command line or in kickstart", requested_lang)
# GNU defines four (four!) ways to set the locale via the environment.
# At least three of those are just going to get in the way of anaconda's
# ability to set the language and locale after startup. If any of, in
# order, $LANGUAGE, $LC_ALL, or $LC_MESSAGES is in the environment, copy
# the information to $LANG, and then clear the rest.
for varname in ("LANGUAGE", "LC_ALL", "LC_MESSAGES"):
if varname in os.environ:
os.environ["LANG"] = os.environ[varname] # pylint: disable=environment-modify
break
for varname in ("LANGUAGE", "LC_ALL", "LC_MESSAGES"):
if varname in os.environ:
del os.environ[varname] # pylint: disable=environment-modify
# first, try to load firmware language if nothing is already set in
# the environment
if "LANG" not in os.environ:
localization.load_firmware_language(ksdata.lang)
# If command line options or kickstart set LANG, override the environment
if opts.lang:
os.environ["LANG"] = opts.lang # pylint: disable=environment-modify
ksdata.lang.seen = True
elif ksdata.lang.lang:
os.environ["LANG"] = ksdata.lang.lang # pylint: disable=environment-modify
# Make sure LANG is set to something
if "LANG" not in os.environ:
os.environ["LANG"] = constants.DEFAULT_LANG # pylint: disable=environment-modify
# parse it using langtable and update the environment
# If langtable returns no locales, fall back to the default
env_langs = localization.get_language_locales(os.environ["LANG"])
if env_langs:
# the first language is the best match
os.environ["LANG"] = env_langs[0] # pylint: disable=environment-modify
else:
# no kickstart or bootoption - use default
localization.setup_locale(constants.DEFAULT_LANG, ksdata.lang)
log.error("Invalid locale '%s' given on command line or in kickstart", os.environ["LANG"])
os.environ["LANG"] = constants.DEFAULT_LANG # pylint: disable=environment-modify
localization.setup_locale(os.environ["LANG"], ksdata.lang)
import blivet
blivet.enable_installer_mode()
# Initialize the network now, in case the display needs it
from pyanaconda.network import networkInitialize, wait_for_connecting_NM_thread
networkInitialize(ksdata)
threadMgr.add(AnacondaThread(name=constants.THREAD_WAIT_FOR_CONNECTING_NM, target=wait_for_connecting_NM_thread, args=(ksdata,)))
# now start the interface
setupDisplay(anaconda, opts, addon_paths)
@ -1323,15 +1302,14 @@ if __name__ == "__main__":
flags.imageInstall = True
except ValueError as e:
stdoutLog.error("error specifying image file: %s", e)
iutil.ipmi_report(constants.IPMI_ABORTED)
sys.exit(1)
if image_count:
anaconda.storage.setupDiskImages()
# only install interactive exception handler in interactive modes
if ksdata.displaymode.displayMode != DISPLAY_MODE_CMDLINE or flags.debug:
from pyanaconda import exception
anaconda.mehConfig = exception.initExceptionHandling(anaconda)
from pyanaconda import exception
anaconda.mehConfig = exception.initExceptionHandling(anaconda)
# add our own additional signal handlers
signal.signal(signal.SIGUSR1, lambda signum, frame:
@ -1339,9 +1317,8 @@ if __name__ == "__main__":
signal.signal(signal.SIGUSR2, lambda signum, frame: anaconda.dumpState())
atexit.register(exitHandler, ksdata.reboot, anaconda.storage)
from blivet import storageInitialize
from blivet.osinstall import storageInitialize
from pyanaconda.packaging import payloadMgr
from pyanaconda.network import networkInitialize, wait_for_connecting_NM_thread
from pyanaconda.timezone import time_initialize
if flags.rescue_mode:
@ -1349,14 +1326,12 @@ if __name__ == "__main__":
else:
cleanPStore()
networkInitialize(ksdata)
if not flags.dirInstall:
threadMgr.add(AnacondaThread(name=constants.THREAD_STORAGE, target=storageInitialize,
args=(anaconda.storage, ksdata, anaconda.protected)))
threadMgr.add(AnacondaThread(name=constants.THREAD_TIME_INIT, target=time_initialize,
args=(ksdata.timezone, anaconda.storage, anaconda.bootloader)))
threadMgr.add(AnacondaThread(name=constants.THREAD_WAIT_FOR_CONNECTING_NM, target=wait_for_connecting_NM_thread, args=(ksdata,)))
# Fallback to default for interactive or for a kickstart with no installation method.
fallback = not (flags.automatedInstall and ksdata.method.method)
@ -1390,14 +1365,12 @@ if __name__ == "__main__":
# setup ntp servers and start NTP daemon if not requested otherwise
if can_touch_runtime_system("start chronyd"):
if anaconda.ksdata.timezone.ntpservers:
ntp.save_servers_to_config(anaconda.ksdata.timezone.ntpservers)
pools, servers = ntp.internal_to_pools_and_servers(anaconda.ksdata.timezone.ntpservers)
ntp.save_servers_to_config(pools, servers)
if not anaconda.ksdata.timezone.nontp:
iutil.start_service("chronyd")
# try to load firmware language
localization.load_firmware_language(ksdata.lang)
# FIXME: This will need to be made cleaner once this file starts to take
# shape with the new UI code.
anaconda._intf.setup(ksdata)

File diff suppressed because it is too large Load Diff

@ -20,7 +20,7 @@
m4_define(python_required_version, 2.5)
AC_PREREQ([2.63])
AC_INIT([anaconda], [21.48.21], [anaconda-devel-list@redhat.com])
AC_INIT([anaconda], [22.20.13], [anaconda-devel-list@redhat.com])
AM_INIT_AUTOMAKE([foreign no-dist-gzip dist-bzip2 tar-ustar])
AC_CONFIG_HEADERS([config.h])
@ -29,13 +29,12 @@ AC_CONFIG_MACRO_DIR([m4])
AC_DEFINE_UNQUOTED([BUILD_DATE], ["`date +%m%d%Y`"], [Date of anaconda build])
AM_SILENT_RULES([yes]) # make --enable-silent-rules the default.
AC_USE_SYSTEM_EXTENSIONS
AC_SYS_LARGEFILE
# Checks for programs.
AC_PROG_AWK
AC_PROG_GREP
AC_PROG_CC
AC_PROG_INSTALL
AC_PROG_LN_S
AC_PROG_MAKE_SET
AC_PROG_LIBTOOL
AC_PROG_MKDIR_P
@ -45,7 +44,7 @@ AC_PROG_MKDIR_P
AC_PATH_PROG([INTLTOOL_EXTRACT], [intltool-extract])
AC_PATH_PROG([INTLTOOL_MERGE], [intltool-merge])
AS_IF([test -z "$INTLTOOL_EXTRACT" -o -z "$INTLTOOL_MERGE"],
[AC_MSG_ERROR([*** intltool not found])])
[ANACONDA_SOFT_FAILURE([intltool not found])])
# Add the bits for Makefile rules
INTLTOOL_V_MERGE='$(INTLTOOL__v_MERGE_$(V))'
@ -69,10 +68,9 @@ AM_GNU_GETTEXT([external])
AM_GNU_GETTEXT_VERSION([0.18.3])
# Checks for header files.
AC_PATH_X
AC_CHECK_HEADERS([fcntl.h stdlib.h string.h sys/time.h unistd.h],
[],
[AC_MSG_FAILURE([*** Header file $ac_header not found.])],
[ANACONDA_SOFT_FAILURE([Header file $ac_header not found.])],
[])
# Checks for typedefs, structures, and compiler characteristics.
@ -86,7 +84,10 @@ AC_TYPE_INT64_T
AC_FUNC_FORK
AC_CHECK_FUNCS([getcwd memset mkdir strchr strdup],
[],
[AC_MSG_FAILURE([*** Required function $ac_func not found.])])
[ANACONDA_SOFT_FAILURE([Function $ac_func not found.])])
AC_CHECK_LIB([audit], [audit_open], [:],
[ANACONDA_SOFT_FAILURE([libaudit not found])])
AM_PATH_PYTHON(python_required_version)
@ -98,69 +99,21 @@ PKG_CHECK_MODULES([PYTHON], [python], [
AC_TRY_LINK_FUNC([Py_Initialize],
[AC_MSG_RESULT([yes])],
[AC_MSG_RESULT([no])
AC_MSG_FAILURE([*** Unable to use python library])])
ANACONDA_SOFT_FAILURE([Unable to use python library])])
LIBS="$LIBS_save"
],
[AC_MSG_FAILURE([*** Unable to find python library])])
[ANACONDA_SOFT_FAILURE([Unable to find python library])])
# Check for libraries we need that provide pkg-config scripts
PKG_PROG_PKG_CONFIG([0.23])
PKG_CHECK_MODULES([RPM], [rpm >= 4.10.0])
PKG_CHECK_MODULES([LIBARCHIVE], [libarchive >= 3.0.4])
# Set $RPM_OPT_FLAGS if we don't have it
if test -z $RPM_OPT_FLAGS ; then
CFLAGS="$CFLAGS -g -pipe -Wp,-D_FORTIFY_SOURCE=2 -fexceptions"
else
CFLAGS="$CFLAGS $RPM_OPT_FLAGS"
fi
# NFS support can, in theory, be enabled or disabled
AC_ARG_ENABLE(nfs,
AC_HELP_STRING([--enable-nfs],
[enable NFS support (default is yes)]),
[nfs=$enableval],
[nfs=yes])
# IPv6 support can be enabled or disabled
AC_ARG_ENABLE(ipv6,
AC_HELP_STRING([--enable-ipv6],
[enable IPv6 support (default is yes)]),
[ipv6=$enableval],
[ipv6=yes])
if test x$ipv6 = xyes ; then
AC_SUBST(IPV6_CFLAGS, [-DENABLE_IPV6])
fi
ANACONDA_PKG_CHECK_MODULES([RPM], [rpm >= 4.10.0])
ANACONDA_PKG_CHECK_MODULES([LIBARCHIVE], [libarchive >= 3.0.4])
# GCC likes to bomb out on some ridiculous warnings. Add your favorites
# here.
SHUT_UP_GCC="-Wno-unused-result"
# Add remaining compiler flags we want to use
CFLAGS="$CFLAGS -Wall -Werror $SHUT_UP_GCC -D_GNU_SOURCE -D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE"
# Filter CFLAGS (remove duplicate flags)
cflags_filter() {
have=
first=1
for flag in $* ; do
if test -z "`echo $have | grep -- $flag`" ; then
if test x$first == x1 ; then
first=2
else
echo -n " "
fi
echo -n $flag
have="$have $flag"
fi
done
}
CFLAGS="`cflags_filter $CFLAGS`"
# Unset $(LIBS) because different programs and libraries will have different
# lists of libraries to link with, we don't want everything linking against
# all libraries we checked for.
LIBS=
CFLAGS="$CFLAGS -Wall -Werror $SHUT_UP_GCC"
# Get the release number from the spec file
rel="`awk '/Release:/ { split($2, r, "%"); print r[[1]] }' $srcdir/anaconda.spec`"
@ -186,8 +139,9 @@ AC_CONFIG_FILES([Makefile
data/liveinst/gnome/Makefile
data/liveinst/pam.d/Makefile
data/systemd/Makefile
data/help/Makefile
data/help/en-US/Makefile
data/window-manager/Makefile
data/window-manager/config/Makefile
data/window-manager/theme/Makefile
po/Makefile.in
scripts/Makefile
pyanaconda/Makefile
@ -201,15 +155,16 @@ AC_CONFIG_FILES([Makefile
pyanaconda/ui/gui/spokes/Makefile
pyanaconda/ui/gui/spokes/advstorage/Makefile
pyanaconda/ui/gui/spokes/lib/Makefile
pyanaconda/ui/gui/tools/Makefile
pyanaconda/ui/gui/Makefile
pyanaconda/ui/tui/hubs/Makefile
pyanaconda/ui/tui/simpleline/Makefile
pyanaconda/ui/tui/spokes/Makefile
pyanaconda/ui/tui/tools/Makefile
pyanaconda/ui/tui/Makefile
data/post-scripts/Makefile
tests/Makefile
utils/Makefile
utils/dd/Makefile])
AC_OUTPUT
# Gently advise the user about the build failures they are about to encounter
ANACONDA_FAILURES

@ -1,272 +0,0 @@
# configure.ac for anaconda
#
# Copyright (C) 2009 Red Hat, Inc.
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published
# by the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
# Author: David Cantrell <dcantrell@redhat.com>
m4_define(python_required_version, 2.5)
AC_PREREQ([2.63])
AC_INIT([anaconda], [20.25.16], [anaconda-devel-list@redhat.com])
AM_INIT_AUTOMAKE([foreign no-dist-gzip dist-bzip2 tar-ustar])
AC_CONFIG_HEADERS([config.h])
AC_CONFIG_MACRO_DIR([m4])
AC_DEFINE_UNQUOTED([BUILD_DATE], ["`date +%m%d%Y`"], [Date of anaconda build])
AM_SILENT_RULES([yes]) # make --enable-silent-rules the default.
# Checks for programs.
AC_PROG_AWK
AC_PROG_GREP
AC_PROG_CC
AC_PROG_INSTALL
AC_PROG_LN_S
AC_PROG_MAKE_SET
AC_PROG_LIBTOOL
# Check for the intltool programs
# These checks and subsitutions are adapted IT_PROG_INTLTOOL provided in
# intltool.m4, but without the parts where it breaks gettext.
AC_PATH_PROG([INTLTOOL_EXTRACT], [intltool-extract])
AC_PATH_PROG([INTLTOOL_MERGE], [intltool-merge])
AS_IF([test -z "$INTLTOOL_EXTRACT" -o -z "$INTLTOOL_MERGE"],
[AC_MSG_ERROR([*** intltool not found])])
# Add the bits for Makefile rules
INTLTOOL_V_MERGE='$(INTLTOOL__v_MERGE_$(V))'
INTLTOOL__v_MERGE_='$(INTLTOOL__v_MERGE_$(AM_DEFAULT_VERBOSITY))'
INTLTOOL__v_MERGE_0='@echo " ITMRG " $@;'
INTLTOOL_V_MERGE_OPTIONS='$(intltool__v_merge_options_$(V))'
intltool__v_merge_options_='$(intltool__v_merge_options_$(AM_DEFAULT_VERBOSITY))'
intltool__v_merge_options_0='-q'
AC_SUBST(INTLTOOL_V_MERGE)
AC_SUBST(INTLTOOL__v_MERGE_)
AC_SUBST(INTLTOOL__v_MERGE_0)
AC_SUBST(INTLTOOL_V_MERGE_OPTIONS)
AC_SUBST(intltool__v_merge_options_)
AC_SUBST(intltool__v_merge_options_0)
INTLTOOL_DESKTOP_RULE='%.desktop: %.desktop.in $(INTLTOOL_MERGE) $(wildcard $(top_srcdir)/po/*.po) ; $(INTLTOOL_V_MERGE)LC_ALL=C $(INTLTOOL_MERGE) $(INTLTOOL_V_MERGE_OPTIONS) -d -u -c $(top_builddir)/po/.intltool-merge-cache $(top_srcdir)/po $< $@'
AC_SUBST(INTLTOOL_DESKTOP_RULE)
AM_GNU_GETTEXT([external])
AM_GNU_GETTEXT_VERSION([0.18.1])
# Checks for libraries.
AC_CHECK_LIB([X11], [XGetWindowAttributes],
[AC_SUBST(X11_LIBS, [-lX11])],
[AC_MSG_FAILURE([*** libX11 not usable.])])
AC_CHECK_LIB([audit], [audit_open],
[AC_SUBST(AUDIT_LIBS, [-laudit])],
[AC_MSG_FAILURE([*** libaudit not usable.])])
AC_CHECK_LIB([z], [zlibVersion],
[AC_SUBST(ZLIB_LIBS, [-lz])],
[AC_MSG_FAILURE([*** libz not usable.])])
# Checks for header files.
AC_PATH_X
AC_FUNC_ALLOCA
AC_HEADER_RESOLV
AC_HEADER_MAJOR
AC_CHECK_HEADERS([argz.h arpa/inet.h fcntl.h inttypes.h libintl.h limits.h \
malloc.h netdb.h netinet/in.h stddef.h stdint.h stdlib.h \
string.h strings.h sys/ioctl.h sys/mount.h sys/param.h \
sys/socket.h sys/time.h sys/vfs.h syslog.h termios.h \
unistd.h utime.h wchar.h],
[],
[AC_MSG_FAILURE([*** Header file $ac_header not found.])],
[])
# Checks for typedefs, structures, and compiler characteristics.
AC_TYPE_UID_T
AC_C_INLINE
AC_TYPE_INT32_T
AC_TYPE_MODE_T
AC_TYPE_OFF_T
AC_TYPE_PID_T
AC_TYPE_SIZE_T
AC_TYPE_SSIZE_T
AC_CHECK_MEMBERS([struct stat.st_rdev])
AC_TYPE_UINT16_T
AC_TYPE_UINT32_T
AC_TYPE_UINT64_T
AC_TYPE_UINT8_T
AC_TYPE_INT64_T
# Checks for library functions.
AC_FUNC_CHOWN
AC_FUNC_ERROR_AT_LINE
AC_FUNC_FORK
AC_FUNC_LSTAT_FOLLOWS_SLASHED_SYMLINK
AC_FUNC_MALLOC
AC_FUNC_MMAP
AC_FUNC_REALLOC
AC_CHECK_FUNCS([dup2 fdatasync ftruncate getcwd gethostbyname gettimeofday \
lchown memmove memset mkdir mkfifo munmap realpath select \
setenv sethostname socket strcasecmp strchr strcspn strdup \
strerror strncasecmp strndup strrchr strstr strtol strtoul \
strverscmp uname utime wcwidth],
[],
[AC_MSG_FAILURE([*** Required function $ac_func not found.])])
AM_PATH_PYTHON(python_required_version)
# Check for the python extension paths
PKG_CHECK_MODULES([PYTHON], [python], [
LIBS_save="$LIBS"
LIBS="$LIBS $PYTHON_LIBS"
AC_MSG_CHECKING([Python libraries])
AC_TRY_LINK_FUNC([Py_Initialize],
[AC_MSG_RESULT([yes])],
[AC_MSG_RESULT([no])
AC_MSG_FAILURE([*** Unable to use python library])])
LIBS="$LIBS_save"
],
[AC_MSG_FAILURE([*** Unable to find python library])])
# Check for libraries we need that provide pkg-config scripts
PKG_PROG_PKG_CONFIG([0.23])
PKG_CHECK_MODULES([X11], [x11 >= 1.3])
PKG_CHECK_MODULES([XCOMPOSITE], [xcomposite >= 0.4.1])
PKG_CHECK_MODULES([GLIB], [glib-2.0 >= 2.16.1])
PKG_CHECK_MODULES([GTK_X11], [gtk+-x11-3.0 >= 3.0])
PKG_CHECK_MODULES([GDK], [gdk-3.0])
PKG_CHECK_MODULES([NETWORKMANAGER], [NetworkManager >= 0.7.1])
PKG_CHECK_MODULES([LIBNL], [libnl-1 >= 1.0])
PKG_CHECK_MODULES([LIBNM_GLIB], [libnm-glib >= 0.7.1 libnm-util >= 0.7.1])
PKG_CHECK_MODULES([RPM], [rpm >= 4.10.0])
PKG_CHECK_MODULES([LIBARCHIVE], [libarchive >= 3.0.4])
# Set $RPM_OPT_FLAGS if we don't have it
if test -z $RPM_OPT_FLAGS ; then
CFLAGS="$CFLAGS -g -pipe -Wp,-D_FORTIFY_SOURCE=2 -fexceptions"
else
CFLAGS="$CFLAGS $RPM_OPT_FLAGS"
fi
# NFS support can, in theory, be enabled or disabled
AC_ARG_ENABLE(nfs,
AC_HELP_STRING([--enable-nfs],
[enable NFS support (default is yes)]),
[nfs=$enableval],
[nfs=yes])
# IPv6 support can be enabled or disabled
AC_ARG_ENABLE(ipv6,
AC_HELP_STRING([--enable-ipv6],
[enable IPv6 support (default is yes)]),
[ipv6=$enableval],
[ipv6=yes])
if test x$ipv6 = xyes ; then
AC_SUBST(IPV6_CFLAGS, [-DENABLE_IPV6])
fi
# GCC likes to bomb out on some ridiculous warnings. Add your favorites
# here.
SHUT_UP_GCC="-Wno-unused-result"
# Add remaining compiler flags we want to use
CFLAGS="$CFLAGS -Wall -Werror $SHUT_UP_GCC -D_GNU_SOURCE -D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE"
# Filter CFLAGS (remove duplicate flags)
cflags_filter() {
have=
first=1
for flag in $* ; do
if test -z "`echo $have | grep -- $flag`" ; then
if test x$first == x1 ; then
first=2
else
echo -n " "
fi
echo -n $flag
have="$have $flag"
fi
done
}
CFLAGS="`cflags_filter $CFLAGS`"
# Unset $(LIBS) because different programs and libraries will have different
# lists of libraries to link with, we don't want everything linking against
# all libraries we checked for.
LIBS=
# Get the release number from the spec file
rel="`awk '/Release:/ { split($2, r, "%"); print r[[1]] }' $srcdir/anaconda.spec`"
AC_SUBST(PACKAGE_RELEASE, [$rel])
# Perform arch related tests
AC_CANONICAL_BUILD
s_arch="`echo $build_cpu | sed -e s/i.86/i386/ -e s/powerpc.*/ppc/`"
AM_CONDITIONAL(IS_LIVEINST_ARCH,
[test x$s_arch = xppc || test x$s_arch = xi386 || test x$s_arch = xx86_64])
AC_CONFIG_SUBDIRS([widgets])
AC_CONFIG_FILES([Makefile
data/Makefile
data/command-stubs/Makefile
docs/Makefile
dracut/Makefile
pyanaconda/installclasses/Makefile
data/liveinst/Makefile
data/liveinst/console.apps/Makefile
data/liveinst/gnome/Makefile
data/liveinst/pam.d/Makefile
data/pixmaps/Makefile
data/icons/Makefile
data/icons/hicolor/Makefile
data/icons/hicolor/16x16/Makefile
data/icons/hicolor/16x16/apps/Makefile
data/icons/hicolor/22x22/Makefile
data/icons/hicolor/22x22/apps/Makefile
data/icons/hicolor/24x24/Makefile
data/icons/hicolor/24x24/apps/Makefile
data/icons/hicolor/32x32/Makefile
data/icons/hicolor/32x32/apps/Makefile
data/icons/hicolor/48x48/Makefile
data/icons/hicolor/48x48/apps/Makefile
data/icons/hicolor/256x256/Makefile
data/icons/hicolor/256x256/apps/Makefile
data/systemd/Makefile
po/Makefile.in
scripts/Makefile
pyanaconda/Makefile
pyanaconda/isys/Makefile
pyanaconda/packaging/Makefile
pyanaconda/ui/Makefile
pyanaconda/ui/lib/Makefile
pyanaconda/ui/gui/categories/Makefile
pyanaconda/ui/gui/hubs/Makefile
pyanaconda/ui/gui/spokes/Makefile
pyanaconda/ui/gui/spokes/advstorage/Makefile
pyanaconda/ui/gui/spokes/lib/Makefile
pyanaconda/ui/gui/tools/Makefile
pyanaconda/ui/gui/Makefile
pyanaconda/ui/tui/hubs/Makefile
pyanaconda/ui/tui/simpleline/Makefile
pyanaconda/ui/tui/spokes/Makefile
pyanaconda/ui/tui/tools/Makefile
pyanaconda/ui/tui/Makefile
data/post-scripts/Makefile
tests/Makefile
utils/Makefile
utils/dd/Makefile])
AC_OUTPUT

@ -17,7 +17,7 @@
#
# Author: Martin Sivak <msivak@redhat.com>
SUBDIRS = command-stubs liveinst systemd post-scripts help
SUBDIRS = command-stubs liveinst systemd post-scripts window-manager
CLEANFILES = *~

@ -160,11 +160,9 @@ AnacondaSpokeWindow #nav-box {
/* These rules were removed when the Adwaita theme moved from
* gnome-themes-standard to gtk. The selectors had been wildcards, but after
* the move they were replaced with more specific selectors, because gtk is
* maintained by garbage people who don't care about how anyone else's
* applications look. We need to apply the old style to anconda's custom
* widgets in order for the selection highlight and insensitive shading to
* appear.
* the move they were replaced with more specific selectors. We need to apply
* the old style to anconda's custom widgets in order for the selection
* highlight and insensitive shading to appear.
*/
@define-color anaconda_selected_bg_color #4a90d9;
@define-color anaconda_selected_fg_color #ffffff;
@ -200,3 +198,9 @@ AnacondaSpokeSelector:insensitive {
GtkTreeView.solid-separator {
-GtkTreeView-horizontal-separator: 0;
}
/* Set the layout indicator colors */
AnacondaLayoutIndicator {
background-color: #fdfdfd;
color: black;
}

@ -223,11 +223,16 @@ installed OS will be booted. /etc/grub.d/40_custom can be used with
manually created menuentrys which can use configfile to point to the
grub.cfg on the newly installed OS.
dnf
Use the experimental DNF package management backend instead of the YUM backend
that is used by default. For more information about the DNF project see:
nodnf
Don't use the DNF package management backend (default since F22) and
use the legacy YUM backend instead.
For more information about the DNF project see:
http://dnf.baseurl.org
mpathfriendlynames
Tell multipathd to use user friendly names when naming devices during the installation.
See the multipathd documentation for more info.
remotelog
Send all the logs to a remote host:port using a TCP connection. The connection will
be retried if there is no listener (ie. won't block the installation).

@ -1,4 +1,4 @@
#!/usr/bin/python
#!/usr/bin/python2
#
# scan system for harddrives and output device name/size
#
@ -35,7 +35,7 @@ def main(argv):
lst = list(lst)
lst.sort()
for dev, size in lst:
print(dev, size)
print("%s %s" % (dev, size))
if __name__ == "__main__":
main(sys.argv)

@ -1,21 +0,0 @@
# data/Makefile.am for anaconda
#
# Copyright (C) 2014 Red Hat, Inc.
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published
# by the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
# Author: Martin Kolman <mkolman@redhat.com>
SUBDIRS = en-US
MAINTAINERCLEANFILES = Makefile.in

@ -1,26 +0,0 @@
# data/Makefile.am for anaconda
#
# Copyright (C) 2014 Red Hat, Inc.
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published
# by the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
# Author: Martin Kolman <mkolman@redhat.com>
enhelpcontentdir = $(datadir)/anaconda/help/en-US
# The $(srcdir)/*.*ml expression matches both the *.html placeholders and *.xml help
# content files. It also works around the fact that autotools complain if the xml files
# are not present but $(srcdir)/*.xml is used.
dist_enhelpcontent_DATA = $(srcdir)/*.*ml
MAINTAINERCLEANFILES = Makefile.in

@ -2,3 +2,10 @@
# This is not loaded if a kickstart file is provided on the command line.
auth --enableshadow --passalgo=sha512
firstboot --enable
%anaconda
# Default password policies
pwpolicy root --notstrict --minlen=6 --minquality=50 --nochanges --emptyok
pwpolicy user --notstrict --minlen=6 --minquality=50 --nochanges --emptyok
pwpolicy luks --notstrict --minlen=6 --minquality=50 --nochanges --emptyok
%end

@ -31,6 +31,9 @@ dist_xinit_SCRIPTS = zz-liveinst.sh
install-exec-local:
$(MKDIR_P) $(DESTDIR)$(bindir)
$(LN_S) consolehelper $(DESTDIR)$(bindir)/liveinst
uninstall-local:
rm -f $(DESTDIR)$(bindir)/liveinst
endif
EXTRA_DIST = README liveinst.desktop.in

@ -18,8 +18,9 @@
# Author: Kalev Lember <kalevlember@gmail.com>
welcomedir = $(datadir)/$(PACKAGE_NAME)/gnome
dist_welcome_DATA = fedora-welcome.desktop install-button.png
dist_welcome_DATA = install-button.png
dist_welcome_SCRIPTS = fedora-welcome
welcome_DATA = fedora-welcome.desktop
EXTRA_DIST = fedora-welcome.desktop.in

@ -77,7 +77,7 @@ fi
export ANACONDA_BUGURL=${ANACONDA_BUGURL:="https://bugzilla.redhat.com/bugzilla/"}
RELEASE=$(rpm -q --qf '%{Release}' fedora-release)
RELEASE=$(rpm -q --qf '%{Release}' --whatprovides system-release)
if [ "${RELEASE:0:2}" = "0." ]; then
export ANACONDA_ISFINAL="false"
else
@ -85,6 +85,7 @@ else
fi
export PATH=/sbin:/usr/sbin:$PATH
export PYTHONPATH=/usr/share/anaconda/site-python
if [ -x /usr/sbin/getenforce ]; then
current=$(/usr/sbin/getenforce)
@ -154,7 +155,7 @@ if [ ! -z "$UPDATES" ]; then
exit 1
fi
curl -L -o /tmp/updates.img $UPDATES
curl -o /tmp/updates.img $UPDATES
# We officially support two updates.img formats: a filesystem image, and
# a compressed cpio blob.

@ -7,6 +7,5 @@ Exec=/usr/bin/liveinst
Terminal=false
Type=Application
Icon=anaconda
Encoding=UTF-8
StartupNotify=true
NoDisplay=true

@ -28,6 +28,7 @@ dist_systemd_DATA = anaconda.service \
anaconda-shell@.service \
instperf.service \
anaconda-sshd.service \
anaconda-nm-config.service \
zram.service
dist_generator_SCRIPTS = anaconda-generator

@ -11,7 +11,7 @@ Environment=HOME=/root MALLOC_CHECK_=2 MALLOC_PERTURB_=204 PATH=/usr/bin:/bin:/s
Type=oneshot
WorkingDirectory=/root
ExecStart=/usr/sbin/anaconda
StandardInput=null
StandardInput=tty
StandardOutput=journal+console
StandardError=journal+console
TimeoutSec=0

@ -31,3 +31,5 @@ for tty in hvc0 hvc1 xvc0 hvsi0 hvsi1 hvsi2; do
service_on_tty anaconda-shell@.service $tty
fi
done
ln -sf $systemd_dir/anaconda-nm-config.service $target_dir/anaconda-nm-config.service

@ -0,0 +1,7 @@
[Unit]
ConditionKernelCommandLine=!ip=ibft
Description=Anaconda NetworkManager configuration
Before=NetworkManager.service
[Service]
ExecStart=/usr/bin/anaconda-disable-nm-ibft-plugin

@ -1,12 +1,16 @@
# tmux.conf for the anaconda environment
bind -n M-tab next
bind -n F1 list-keys
set-option -s exit-unattached off
set-option -g base-index 1
set-option -g set-remain-on-exit on
set-option -g history-limit 10000
new-session -s anaconda -n main "anaconda"
set-option status-right ""
set-option status-right-length 0
set-option status-right '#[fg=blue]#(echo -n "Switch tab: Alt+Tab | Help: F1 ")'
new-window -d -n shell "bash --login"
new-window -d -n log "tail -F /tmp/anaconda.log"

@ -1,4 +1,6 @@
# Copyright (C) 2011 Red Hat, Inc.
# data/window-manager/Makefile.am for anaconda
#
# Copyright (C) 2015 Red Hat, Inc.
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published
@ -13,8 +15,6 @@
# You should have received a copy of the GNU Lesser General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
# Author: Chris Lumens <clumens@redhat.com>
EXTRA_DIST = README run-hub.py run-spoke.py
# Author: David Shea <dshea@redhat.com>
MAINTAINERCLEANFILES = Makefile.in
SUBDIRS = config theme

@ -0,0 +1,39 @@
# data/window-manager/config/Makefile.am for anaconda
#
# Copyright (C) 2015 Red Hat, Inc.
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published
# by the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
# Author: David Shea <dshea@redhat.com>
schemadir = $(pkgdatadir)/window-manager/glib-2.0/schemas
# These files need to be compiled by glib-compile-schemas. This is handled
# in the spec file scriptlets.
dist_schema_DATA = org.gnome.desktop.wm.keybindings.gschema.override \
org.gnome.desktop.wm.preferences.gschema.override
# GSettings insists on the override files being in the same directory as the
# schemas they modify, so pretend that this is the case with symlinks and
# create the compiled schema.
install-data-hook:
$(MKDIR_P) $(DESTDIR)$(schemadir)
$(LN_S) -f /usr/share/glib-2.0/schemas/org.gnome.desktop.wm.keybindings.gschema.xml $(DESTDIR)$(schemadir)
$(LN_S) -f /usr/share/glib-2.0/schemas/org.gnome.desktop.wm.preferences.gschema.xml $(DESTDIR)$(schemadir)
$(LN_S) -f /usr/share/glib-2.0/schemas/org.gnome.desktop.enums.xml $(DESTDIR)$(schemadir)
glib-compile-schemas $(DESTDIR)$(schemadir)
uninstall-local:
rm -f $(DESTDIR)$(schemadir)/*.xml
rm -f $(DESTDIR)$(schemadir)/gschemas.compiled

@ -0,0 +1,35 @@
[org.gnome.desktop.wm.keybindings]
switch-to-workspace-left=[]
switch-to-workspace-right=[]
switch-to-workspace-up=[]
switch-to-workspace-down=[]
switch-to-workspace-1=[]
switch-to-workspace-last=[]
switch-group=[]
switch-windows=[]
switch-panels=[]
cycle-group=[]
cycle-windows=[]
cycle-panels=[]
activate-window-menu=[]
toggle-maximized=[]
minimize=[]
maximize=[]
unmaximize=[]
begin-move=[]
begin-resize=[]
move-to-workspace-1=[]
move-to-workspace-left=[]
move-to-workspace-right=[]
move-to-workspace-up=[]
move-to-workspace-down=[]
move-to-workspace-last=[]
move-to-monitor-left=[]
move-to-monitor-right=[]
move-to-monitor-up=[]
move-to-monitor-down=[]
close=[]
panel-main-menu=[]
panel-run-dialog=[]
switch-applications=[]
switch-input-source=[]

@ -0,0 +1,6 @@
[org.gnome.desktop.wm.preferences]
button-layout=':'
action-right-click-titlebar='none'
num-workspaces=1
theme='Anaconda'
mouse-button-modifier=''

@ -1,6 +1,6 @@
# tests/regex/Makefile.am for anaconda
# data/window-manager/config/Makefile.am for anaconda
#
# Copyright (C) 2010 Red Hat, Inc.
# Copyright (C) 2015 Red Hat, Inc.
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published
@ -15,8 +15,8 @@
# You should have received a copy of the GNU Lesser General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
# Author: Brian C. Lane <bcl@redhat.com>
# Author: David Shea <dshea@redhat.com>
EXTRA_DIST = *.py
themedir = $(datadir)/themes/Anaconda/metacity-1
MAINTAINERCLEANFILES = Makefile.in
dist_theme_DATA = metacity-theme-1.xml

@ -0,0 +1,958 @@
<?xml version="1.0"?>
<!-- This is a copy of the Clearlooks theme, with normal_maximized modified
to hide the titlebar.
-->
<metacity_theme>
<info>
<name>Anaconda</name>
<author>Daniel Borgmann &lt;daniel.borgmann@gmail.com&gt;, Andrea Cimitan &lt;andrea.cimitan@gmail.com&gt;</author>
<copyright>&#194; 2005-2007 Daniel Borgmann, Andrea Cimitan</copyright>
<date>Apr, 2007</date>
<description>The Clearlooks "Gummy" Metacity Theme</description>
</info>
<!-- ::: GEOMETRY ::: -->
<frame_geometry name="normal" rounded_top_left="true" rounded_top_right="true" rounded_bottom_left="false" rounded_bottom_right="false">
<distance name="left_width" value="4"/>
<distance name="right_width" value="4"/>
<distance name="bottom_height" value="4"/>
<distance name="left_titlebar_edge" value="4"/>
<distance name="right_titlebar_edge" value="4"/>
<aspect_ratio name="button" value="1.0"/>
<distance name="title_vertical_pad" value="0"/>
<border name="title_border" left="2" right="2" top="4" bottom="3"/>
<border name="button_border" left="1" right="1" top="2" bottom="2"/>
</frame_geometry>
<frame_geometry name="shaded" parent="normal" rounded_top_left="true" rounded_top_right="true" rounded_bottom_left="true" rounded_bottom_right="true"/>
<frame_geometry name="normal_maximized" parent="normal" rounded_top_left="false" rounded_top_right="false" rounded_bottom_left="false" rounded_bottom_right="false" has_title="false">
<!-- strip frame spacing off the normal geometry when maximised -->
<distance name="left_width" value="0"/>
<distance name="right_width" value="0"/>
<distance name="bottom_height" value="1"/>
<distance name="left_titlebar_edge" value="1"/>
<distance name="right_titlebar_edge" value="1"/>
<border name="title_border" left="0" right="0" top="0" bottom="0"/>
<border name="button_border" left="0" right="0" top="0" bottom="0"/>
</frame_geometry>
<frame_geometry name="utility" title_scale="small" rounded_top_left="false" rounded_top_right="false" rounded_bottom_left="false" rounded_bottom_right="false">
<distance name="left_width" value="4"/>
<distance name="right_width" value="4"/>
<distance name="bottom_height" value="4"/>
<distance name="left_titlebar_edge" value="4"/>
<distance name="right_titlebar_edge" value="4"/>
<distance name="title_vertical_pad" value="0"/>
<border name="title_border" left="2" right="2" top="4" bottom="3"/>
<border name="button_border" left="0" right="0" top="2" bottom="2"/>
<aspect_ratio name="button" value="1"/>
</frame_geometry>
<frame_geometry name="border" has_title="false">
<distance name="left_width" value="4"/>
<distance name="right_width" value="4"/>
<distance name="bottom_height" value="4"/>
<distance name="left_titlebar_edge" value="0"/>
<distance name="right_titlebar_edge" value="0"/>
<distance name="button_width" value="0"/>
<distance name="button_height" value="0"/>
<distance name="title_vertical_pad" value="4"/>
<border name="title_border" left="0" right="0" top="0" bottom="0"/>
<border name="button_border" left="0" right="0" top="0" bottom="0"/>
</frame_geometry>
<!-- button minimum size -->
<constant name="Bmin" value="7"/>
<!-- button inside padding -->
<constant name="Bpad" value="6"/>
<!-- ::: CORNERS ::: -->
<draw_ops name="corners_outline_selected_top">
<!-- top left -->
<line color="shade/gtk:bg[SELECTED]/0.6" x1="1" y1="3" x2="1" y2="3"/>
<line color="shade/gtk:bg[SELECTED]/0.73" x1="1" y1="4" x2="1" y2="4"/>
<line color="shade/gtk:bg[SELECTED]/0.6" x1="2" y1="2" x2="2" y2="2"/>
<line color="shade/gtk:bg[SELECTED]/0.6" x1="3" y1="1" x2="3" y2="1"/>
<line color="shade/gtk:bg[SELECTED]/0.73" x1="4" y1="1" x2="4" y2="1"/>
<!-- top right -->
<line color="shade/gtk:bg[SELECTED]/0.6" x1="width-2" y1="3" x2="width-2" y2="3"/>
<line color="shade/gtk:bg[SELECTED]/0.73" x1="width-2" y1="4" x2="width-2" y2="4"/>
<line color="shade/gtk:bg[SELECTED]/0.6" x1="width-3" y1="2" x2="width-3" y2="2"/>
<line color="shade/gtk:bg[SELECTED]/0.6" x1="width-4" y1="1" x2="width-4" y2="1"/>
<line color="shade/gtk:bg[SELECTED]/0.73" x1="width-5" y1="1" x2="width-5" y2="1"/>
</draw_ops>
<draw_ops name="corners_outline_top">
<!-- top left -->
<line color="shade/gtk:bg[NORMAL]/0.55" x1="1" y1="3" x2="1" y2="3"/>
<line color="shade/gtk:bg[NORMAL]/0.68" x1="1" y1="4" x2="1" y2="4"/>
<line color="shade/gtk:bg[NORMAL]/0.55" x1="2" y1="2" x2="2" y2="2"/>
<line color="shade/gtk:bg[NORMAL]/0.55" x1="3" y1="1" x2="3" y2="1"/>
<line color="shade/gtk:bg[NORMAL]/0.68" x1="4" y1="1" x2="4" y2="1"/>
<!-- top right -->
<line color="shade/gtk:bg[NORMAL]/0.55" x1="width-2" y1="3" x2="width-2" y2="3"/>
<line color="shade/gtk:bg[NORMAL]/0.68" x1="width-2" y1="4" x2="width-2" y2="4"/>
<line color="shade/gtk:bg[NORMAL]/0.55" x1="width-3" y1="2" x2="width-3" y2="2"/>
<line color="shade/gtk:bg[NORMAL]/0.55" x1="width-4" y1="1" x2="width-4" y2="1"/>
<line color="shade/gtk:bg[NORMAL]/0.68" x1="width-5" y1="1" x2="width-5" y2="1"/>
</draw_ops>
<draw_ops name="corners_outline_selected_bottom">
<!-- bottom left -->
<line color="shade/gtk:bg[SELECTED]/0.6" x1="1" y1="height-4" x2="1" y2="height-5"/>
<line color="shade/gtk:bg[SELECTED]/0.6" x1="2" y1="height-3" x2="2" y2="height-3"/>
<line color="shade/gtk:bg[SELECTED]/0.6" x1="2" y1="height-2" x2="4" y2="height-2"/>
<!-- bottom right -->
<line color="shade/gtk:bg[SELECTED]/0.6" x1="width-2" y1="height-4" x2="width-2" y2="height-5"/>
<line color="shade/gtk:bg[SELECTED]/0.6" x1="width-3" y1="height-3" x2="width-3" y2="height-3"/>
<line color="shade/gtk:bg[SELECTED]/0.6" x1="width-4" y1="height-2" x2="width-5" y2="height-2"/>
</draw_ops>
<draw_ops name="corners_outline_bottom">
<!-- bottom left -->
<line color="shade/gtk:bg[NORMAL]/0.55" x1="1" y1="height-4" x2="1" y2="height-5"/>
<line color="shade/gtk:bg[NORMAL]/0.55" x1="2" y1="height-3" x2="2" y2="height-3"/>
<line color="shade/gtk:bg[NORMAL]/0.55" x1="2" y1="height-2" x2="4" y2="height-2"/>
<!-- bottom right -->
<line color="shade/gtk:bg[NORMAL]/0.55" x1="width-2" y1="height-4" x2="width-2" y2="height-5"/>
<line color="shade/gtk:bg[NORMAL]/0.55" x1="width-3" y1="height-3" x2="width-3" y2="height-3"/>
<line color="shade/gtk:bg[NORMAL]/0.55" x1="width-4" y1="height-2" x2="width-5" y2="height-2"/>
</draw_ops>
<draw_ops name="corners_highlight">
<!-- ** corner highlight for left top ** -->
<line color="shade/gtk:bg[SELECTED]/1.18" x1="2" y1="3" x2="2" y2="4"/>
<line color="shade/gtk:bg[SELECTED]/1.18" x1="3" y1="2" x2="4" y2="2"/>
<!-- ** corner highlight for right top ** -->
<line color="shade/gtk:bg[SELECTED]/0.98" x1="width-3" y1="3" x2="width-3" y2="4"/>
<line color="shade/gtk:bg[SELECTED]/1.16" x1="width-5" y1="2" x2="width-4" y2="2"/>
<!-- ** corner highlight for left bottom ** -->
<!--<line color="shade/gtk:bg[NORMAL]/1.3" x1="2" y1="height-4" x2="2" y2="height-5"/>
<line color="shade/gtk:bg[NORMAL]/0.88" x1="3" y1="height-3" x2="4" y2="height-3"/>-->
<!-- ** corner highlight for right bottom ** -->
<!--<line color="shade/gtk:bg[NORMAL]/0.88" x1="width-3" y1="height-4" x2="width-3" y2="height-5"/>
<line color="shade/gtk:bg[NORMAL]/0.88" x1="width-4" y1="height-3" x2="width-5" y2="height-3"/>-->
</draw_ops>
<draw_ops name="corners_highlight_unfocused">
<!-- ** corner highlight for left top ** -->
<line color="shade/gtk:bg[NORMAL]/1.05" x1="2" y1="3" x2="2" y2="4"/>
<line color="shade/gtk:bg[NORMAL]/1.05" x1="3" y1="2" x2="4" y2="2"/>
<!-- ** corner highlight for right top ** -->
<line color="shade/gtk:bg[NORMAL]/0.88" x1="width-3" y1="3" x2="width-3" y2="4"/>
<line color="shade/gtk:bg[NORMAL]/1.04" x1="width-5" y1="2" x2="width-4" y2="2"/>
<!-- ** corner highlight for left bottom ** -->
<!--<line color="shade/gtk:bg[NORMAL]/1.3" x1="2" y1="height-4" x2="2" y2="height-5"/>
<line color="shade/gtk:bg[NORMAL]/0.88" x1="3" y1="height-3" x2="4" y2="height-3"/>-->
<!-- ** corner highlight for right bottom ** -->
<!--<line color="shade/gtk:bg[NORMAL]/0.88" x1="width-3" y1="height-4" x2="width-3" y2="height-5"/>
<line color="shade/gtk:bg[NORMAL]/0.88" x1="width-4" y1="height-3" x2="width-5" y2="height-3"/>-->
</draw_ops>
<draw_ops name="corners_highlight_shaded">
<!-- ** corner highlight for left top ** -->
<line color="shade/gtk:bg[SELECTED]/1.18" x1="2" y1="3" x2="2" y2="4"/>
<line color="shade/gtk:bg[SELECTED]/1.18" x1="3" y1="2" x2="4" y2="2"/>
<!-- ** corner highlight for right top ** -->
<line color="shade/gtk:bg[SELECTED]/0.98" x1="width-3" y1="3" x2="width-3" y2="4"/>
<line color="shade/gtk:bg[SELECTED]/1.16" x1="width-5" y1="2" x2="width-4" y2="2"/>
<!-- ** corner highlight for left bottom ** -->
<line color="shade/gtk:bg[SELECTED]/1.08" x1="2" y1="height-4" x2="2" y2="height-5"/>
<line color="shade/gtk:bg[SELECTED]/0.98" x1="3" y1="height-3" x2="4" y2="height-3"/>
<!-- ** corner highlight for right bottom ** -->
<line color="shade/gtk:bg[SELECTED]/0.98" x1="width-3" y1="height-4" x2="width-3" y2="height-5"/>
<line color="shade/gtk:bg[SELECTED]/0.98" x1="width-4" y1="height-3" x2="width-5" y2="height-3"/>
</draw_ops>
<draw_ops name="corners_highlight_shaded_unfocused">
<!-- ** corner highlight for left top ** -->
<line color="shade/gtk:bg[NORMAL]/1.05" x1="2" y1="3" x2="2" y2="4"/>
<line color="shade/gtk:bg[NORMAL]/1.05" x1="3" y1="2" x2="4" y2="2"/>
<!-- ** corner highlight for right top ** -->
<line color="shade/gtk:bg[NORMAL]/0.88" x1="width-3" y1="3" x2="width-3" y2="4"/>
<line color="shade/gtk:bg[NORMAL]/1.04" x1="width-5" y1="2" x2="width-4" y2="2"/>
<!-- ** corner highlight for left bottom ** -->
<line color="shade/gtk:bg[NORMAL]/1.02" x1="2" y1="height-4" x2="2" y2="height-5"/>
<line color="shade/gtk:bg[NORMAL]/0.88" x1="3" y1="height-3" x2="4" y2="height-3"/>
<!-- ** corner highlight for right bottom ** -->
<line color="shade/gtk:bg[NORMAL]/0.88" x1="width-3" y1="height-4" x2="width-3" y2="height-5"/>
<line color="shade/gtk:bg[NORMAL]/0.88" x1="width-4" y1="height-3" x2="width-5" y2="height-3"/>
</draw_ops>
<draw_ops name="window_bg">
<rectangle color="gtk:bg[NORMAL]" filled="true" x="0" y="0" width="width" height="height"/>
</draw_ops>
<!-- ::: BEVEL FOCUSED ::: -->
<draw_ops name="bevel">
<include name="window_bg"/>
<!-- ** titlebar outline ** -->
<rectangle color="shade/gtk:bg[SELECTED]/0.55" filled="false" x="0" y="0" width="width - 1" height="((title_height + 6) `max` (top_height - 2))"/>
<!-- ** 3d beveled frame ** -->
<line color="shade/gtk:bg[NORMAL]/0.88" x1="1" y1="height - 2" x2="width - 2" y2="height - 2"/>
<line color="shade/gtk:bg[NORMAL]/0.88" x1="width - 2" y1="3" x2="width - 2" y2="height - 2"/>
<line color="shade/gtk:bg[NORMAL]/1.2" x1="3" y1="1" x2="width - 4" y2="1"/>
<line color="shade/gtk:bg[NORMAL]/1.2" x1="1" y1="3" x2="1" y2="height - 2"/>
<line color="shade/gtk:bg[SELECTED]/0.94" x1="2" y1="((title_height + 5) `max` (top_height - 3))" x2="width - 3" y2="((title_height + 5) `max` (top_height - 3))"/>
<line color="shade/gtk:bg[SELECTED]/0.95" x1="width - 2" y1="2" x2="width - 2" y2="((title_height + 6) `max` (top_height - 2))"/>
<line color="shade/gtk:bg[SELECTED]/1.18" x1="1" y1="1" x2="width - 2" y2="1"/>
<line color="shade/gtk:bg[SELECTED]/1.1" x1="1" y1="2" x2="1" y2="((title_height + 5) `max` (top_height - 3))"/>
<!-- ** fancy gradient ** -->
<gradient type="vertical" x="2" y="top_height/2" width="width-4" height="top_height/2-1">
<color value="shade/gtk:bg[SELECTED]/1.0"/>
<color value="shade/gtk:bg[SELECTED]/0.94"/>
</gradient>
<gradient type="vertical" x="2" y="2" width="width-4" height="top_height/2-2">
<color value="shade/gtk:bg[SELECTED]/1.08"/>
<color value="shade/gtk:bg[SELECTED]/1.02"/>
</gradient>
<line color="shade/gtk:bg[SELECTED]/0.7" x1="1" y1="((title_height + 6) `max` (top_height - 2))" x2="width - 2" y2="((title_height + 6) `max` (top_height - 2))"/>
<!-- ** border outline ** -->
<line color="shade/gtk:bg[NORMAL]/0.45" x1="0" y1="((title_height + 6) `max` (top_height - 2))" x2="0" y2="height"/>
<line color="shade/gtk:bg[NORMAL]/0.45" x1="width - 1" y1="((title_height + 6) `max` (top_height - 2))" x2="width - 1" y2="height"/>
<line color="shade/gtk:bg[NORMAL]/0.45" x1="1" y1="height - 1" x2="width - 2" y2="height - 1"/>
</draw_ops>
<draw_ops name="bevel_maximized">
<!-- ** 3d beveled frame ** -->
<line color="shade/gtk:bg[SELECTED]/0.55" x1="0" y1="0" x2="width" y2="0"/>
<line color="shade/gtk:bg[SELECTED]/1.18" x1="0" y1="1" x2="width" y2="1"/>
<line color="shade/gtk:bg[SELECTED]/0.94" x1="0" y1="((title_height + 5) `max` (top_height - 3))" x2="width" y2="((title_height + 5) `max` (top_height - 3))"/>
<!-- ** fancy gradient ** -->
<gradient type="vertical" x="0" y="top_height/2" width="width" height="top_height/2-1">
<color value="shade/gtk:bg[SELECTED]/1.0"/>
<color value="shade/gtk:bg[SELECTED]/0.94"/>
</gradient>
<gradient type="vertical" x="0" y="1" width="width" height="top_height/2-1">
<color value="shade/gtk:bg[SELECTED]/1.08"/>
<color value="shade/gtk:bg[SELECTED]/1.02"/>
</gradient>
<line color="shade/gtk:bg[SELECTED]/0.7" x1="0" y1="((title_height + 6) `max` (top_height - 2))" x2="width" y2="((title_height + 6) `max` (top_height - 2))"/>
<line color="shade/gtk:bg[SELECTED]/0.55" x1="0" y1="height-1" x2="width" y2="height-1"/>
</draw_ops>
<draw_ops name="round_bevel">
<include name="bevel"/>
<include name="corners_outline_selected_top"/>
<!--<include name="corners_outline_bottom"/>-->
<include name="corners_highlight"/>
</draw_ops>
<draw_ops name="bevel_shaded">
<include name="bevel"/>
<line color="shade/gtk:bg[SELECTED]/0.6" x1="0" y1="height-1" x2="width" y2="height-1"/>
</draw_ops>
<draw_ops name="round_bevel_shaded">
<include name="bevel"/>
<include name="corners_outline_selected_top"/>
<include name="corners_outline_selected_bottom"/>
<include name="corners_highlight_shaded"/>
<line color="shade/gtk:bg[SELECTED]/0.6" x1="5" y1="height-1" x2="width-6" y2="height-1"/>
</draw_ops>
<!-- ::: BEVEL UNFOCUSED ::: -->
<draw_ops name="bevel_unfocused">
<include name="window_bg"/>
<line color="shade/gtk:bg[NORMAL]/0.88" x1="1" y1="height - 2" x2="width - 2" y2="height - 2"/>
<line color="shade/gtk:bg[NORMAL]/0.88" x1="width - 2" y1="2" x2="width - 2" y2="height - 2"/>
<line color="shade/gtk:bg[NORMAL]/1.05" x1="1" y1="1" x2="width - 2" y2="1"/>
<line color="shade/gtk:bg[NORMAL]/1.03" x1="1" y1="2" x2="1" y2="height - 2"/>
<line color="shade/gtk:bg[NORMAL]/0.89" x1="2" y1="((title_height + 5) `max` (top_height - 3))" x2="width - 3" y2="((title_height + 5) `max` (top_height - 3))"/>
<!-- ** fancy gradient ** -->
<gradient type="vertical" x="2" y="top_height/2" width="width-4" height="top_height/2-1">
<color value="shade/gtk:bg[NORMAL]/0.93"/>
<color value="shade/gtk:bg[NORMAL]/0.89"/>
</gradient>
<gradient type="vertical" x="2" y="2" width="width-4" height="top_height/2-2">
<color value="shade/gtk:bg[NORMAL]/0.99"/>
<color value="shade/gtk:bg[NORMAL]/0.95"/>
</gradient>
<line color="shade/gtk:bg[NORMAL]/0.65" x1="1" y1="((title_height + 6) `max` (top_height - 2))" x2="width - 2" y2="((title_height + 6) `max` (top_height - 2))"/>
<rectangle color="shade/gtk:bg[NORMAL]/0.55" filled="false" x="0" y="0" width="width - 1" height="height - 1"/>
</draw_ops>
<draw_ops name="bevel_maximized_unfocused">
<!-- ** 3d beveled frame ** -->
<line color="shade/gtk:bg[NORMAL]/0.55" x1="0" y1="0" x2="width" y2="0"/>
<line color="shade/gtk:bg[NORMAL]/1.05" x1="0" y1="1" x2="width" y2="1"/>
<line color="shade/gtk:bg[NORMAL]/0.89" x1="0" y1="((title_height + 5) `max` (top_height - 3))" x2="width" y2="((title_height + 5) `max` (top_height - 3))"/>
<!-- ** fancy gradient ** -->
<gradient type="vertical" x="0" y="top_height/2" width="width" height="top_height/2-1">
<color value="shade/gtk:bg[NORMAL]/0.93"/>
<color value="shade/gtk:bg[NORMAL]/0.89"/>
</gradient>
<gradient type="vertical" x="0" y="2" width="width" height="top_height/2-2">
<color value="shade/gtk:bg[NORMAL]/0.99"/>
<color value="shade/gtk:bg[NORMAL]/0.95"/>
</gradient>
<line color="shade/gtk:bg[NORMAL]/0.65" x1="0" y1="((title_height + 6) `max` (top_height - 2))" x2="width" y2="((title_height + 6) `max` (top_height - 2))"/>
<line color="shade/gtk:bg[NORMAL]/0.55" x1="0" y1="height-1" x2="width" y2="height-1"/>
</draw_ops>
<draw_ops name="round_bevel_unfocused">
<include name="bevel_unfocused"/>
<include name="corners_outline_top"/>
<!--<include name="corners_outline_bottom"/>-->
<include name="corners_highlight_unfocused"/>
</draw_ops>
<draw_ops name="round_bevel_unfocused_shaded">
<include name="bevel_unfocused"/>
<include name="corners_outline_top"/>
<include name="corners_outline_bottom"/>
<include name="corners_highlight_shaded_unfocused"/>
</draw_ops>
<!-- ::: BORDER ::: -->
<draw_ops name="border">
<line color="shade/gtk:bg[NORMAL]/0.88" x1="1" y1="height - 2" x2="width - 2" y2="height - 2"/>
<line color="shade/gtk:bg[NORMAL]/0.88" x1="width - 2" y1="1" x2="width - 2" y2="height - 2"/>
<line color="shade/gtk:bg[NORMAL]/1.2" x1="1" y1="1" x2="width - 2" y2="1"/>
<line color="shade/gtk:bg[NORMAL]/1.2" x1="1" y1="1" x2="1" y2="height - 2"/>
<rectangle color="shade/gtk:bg[NORMAL]/0.55" filled="false" x="0" y="0" width="width - 1" height="height - 1"/>
</draw_ops>
<!-- ::: TITLES ::: -->
<draw_ops name="title_text">
<title color="shade/gtk:bg[SELECTED]/0.7" x="((3 `max` (width-title_width)) / 2)" y="(((height - title_height) / 2) `max` 0) + 1"/>
<title color="shade/gtk:bg[SELECTED]/0.7" x="((3 `max` (width-title_width)) / 2)+1" y="(((height - title_height) / 2) `max` 0)"/>
<title color="shade/gtk:bg[SELECTED]/0.7" x="((3 `max` (width-title_width)) / 2)-1" y="(((height - title_height) / 2) `max` 0)"/>
<title color="shade/gtk:bg[SELECTED]/0.7" x="((3 `max` (width-title_width)) / 2)" y="(((height - title_height) / 2) `max` 0)-1"/>
<title color="#FFFFFF" x="(3 `max` (width-title_width)) / 2" y="(((height - title_height) / 2) `max` 0)"/>
</draw_ops>
<draw_ops name="title_text_unfocused">
<!--<title color="shade/gtk:bg[NORMAL]/1.07" x="5 `max` (width-title_width)/2+1" y="1 `max` ((height-title_height)/2)+1"/>-->
<title color="blend/gtk:fg[NORMAL]/gtk:bg[NORMAL]/0.45" x="4 `max` (width-title_width)/2" y="0 `max` ((height-title_height)/2)"/>
</draw_ops>
<draw_ops name="title">
<include name="title_text"/>
</draw_ops>
<draw_ops name="title_unfocused">
<include name="title_text_unfocused"/>
</draw_ops>
<!-- ::: BUTTONS ::: -->
<draw_ops name="button_bg">
<!-- inset -->
<gradient type="vertical" x="0" y="3" width="width" height="height-6">
<color value="shade/gtk:bg[SELECTED]/0.96"/>
<color value="shade/gtk:bg[SELECTED]/1.05"/>
</gradient>
<line color="shade/gtk:bg[SELECTED]/1.00" x1="2" y1="0" x2="width-3" y2="0"/>
<line color="shade/gtk:bg[SELECTED]/0.99" x1="1" y1="1" x2="width-2" y2="1"/>
<line color="shade/gtk:bg[SELECTED]/0.99" x1="0" y1="2" x2="width-1" y2="2"/>
<line color="shade/gtk:bg[SELECTED]/0.98" x1="3" y1="0" x2="width-4" y2="0"/>
<line color="shade/gtk:bg[SELECTED]/0.91" x1="2" y1="1" x2="width-3" y2="1"/>
<line color="shade/gtk:bg[SELECTED]/0.90" x1="1" y1="2" x2="width-2" y2="2"/>
<line color="shade/gtk:bg[SELECTED]/1.03" x1="2" y1="height-1" x2="width-3" y2="height-1"/>
<line color="shade/gtk:bg[SELECTED]/1.00" x1="1" y1="height-2" x2="width-2" y2="height-2"/>
<line color="shade/gtk:bg[SELECTED]/1.01" x1="0" y1="height-3" x2="width-1" y2="height-3"/>
<line color="shade/gtk:bg[SELECTED]/1.06" x1="3" y1="height-1" x2="width-4" y2="height-1"/>
<line color="shade/gtk:bg[SELECTED]/1.02" x1="2" y1="height-2" x2="width-3" y2="height-2"/>
<line color="shade/gtk:bg[SELECTED]/1.03" x1="1" y1="height-3" x2="width-2" y2="height-3"/>
<!-- border outline -->
<line color="shade/gtk:bg[SELECTED]/0.6" x1="3" y1="1" x2="width-4" y2="1"/>
<line color="shade/gtk:bg[SELECTED]/0.6" x1="3" y1="height-2" x2="width-4" y2="height-2"/>
<line color="shade/gtk:bg[SELECTED]/0.6" x1="1" y1="3" x2="1" y2="height-4"/>
<line color="shade/gtk:bg[SELECTED]/0.6" x1="width-2" y1="3" x2="width-2" y2="height-4"/>
<line color="shade/gtk:bg[SELECTED]/0.6" x1="2" y1="2" x2="width-3" y2="2"/>
<line color="shade/gtk:bg[SELECTED]/0.6" x1="2" y1="height-3" x2="width-3" y2="height-3"/>
<!-- border smooth effect -->
<line color="shade/gtk:bg[SELECTED]/1.02" x1="3" y1="2" x2="width-4" y2="2"/>
<line color="shade/gtk:bg[SELECTED]/1.00" x1="2" y1="3" x2="2" y2="height-4"/>
<line color="shade/gtk:bg[SELECTED]/0.90" x1="width-3" y1="3" x2="width-3" y2="height-4"/>
<!-- inside highlight -->
<line color="shade/gtk:bg[SELECTED]/1.18" x1="4" y1="2" x2="width-5" y2="2"/>
<line color="shade/gtk:bg[SELECTED]/1.1" x1="2" y1="4" x2="2" y2="height-5"/>
<!-- inside shadow -->
<line color="shade/gtk:bg[SELECTED]/1.0" x1="width-3" y1="4" x2="width-3" y2="height-5"/>
<!-- fill gradient -->
<gradient type="vertical" x="3" y="3" width="width-6" height="(height)/2-1">
<color value="shade/gtk:bg[SELECTED]/1.1"/>
<color value="shade/gtk:bg[SELECTED]/1.02"/>
</gradient>
<gradient type="vertical" x="3" y="(height)/2" width="width-6" height="(height)/2-2">
<color value="shade/gtk:bg[SELECTED]/1.0"/>
<color value="shade/gtk:bg[SELECTED]/0.92"/>
</gradient>
<!-- bottom border smooth effect -->
<line color="shade/gtk:bg[SELECTED]/0.84" x1="3" y1="height-3" x2="width-4" y2="height-3"/>
<line color="shade/gtk:bg[SELECTED]/0.92" x1="4" y1="height-3" x2="width-5" y2="height-3"/>
</draw_ops>
<draw_ops name="button_bg_unfocused">
<!-- inset -->
<gradient type="vertical" x="0" y="3" width="width" height="height-6">
<color value="shade/gtk:bg[NORMAL]/0.92"/>
<color value="shade/gtk:bg[NORMAL]/0.96"/>
</gradient>
<line color="shade/gtk:bg[NORMAL]/0.93" x1="2" y1="0" x2="width-3" y2="0"/>
<line color="shade/gtk:bg[NORMAL]/0.92" x1="1" y1="1" x2="width-2" y2="1"/>
<line color="shade/gtk:bg[NORMAL]/0.92" x1="0" y1="2" x2="width-1" y2="2"/>
<line color="shade/gtk:bg[NORMAL]/0.91" x1="3" y1="0" x2="width-4" y2="0"/>
<line color="shade/gtk:bg[NORMAL]/0.87" x1="2" y1="1" x2="width-3" y2="1"/>
<line color="shade/gtk:bg[NORMAL]/0.86" x1="1" y1="2" x2="width-2" y2="2"/>
<line color="shade/gtk:bg[NORMAL]/0.945" x1="2" y1="height-1" x2="width-3" y2="height-1"/>
<line color="shade/gtk:bg[NORMAL]/0.93" x1="1" y1="height-2" x2="width-2" y2="height-2"/>
<line color="shade/gtk:bg[NORMAL]/0.935" x1="0" y1="height-3" x2="width-1" y2="height-3"/>
<line color="shade/gtk:bg[NORMAL]/0.96" x1="3" y1="height-1" x2="width-4" y2="height-1"/>
<line color="shade/gtk:bg[NORMAL]/0.94" x1="2" y1="height-2" x2="width-3" y2="height-2"/>
<line color="shade/gtk:bg[NORMAL]/0.95" x1="1" y1="height-3" x2="width-2" y2="height-3"/>
<!-- border outline -->
<line color="shade/gtk:bg[NORMAL]/0.6" x1="3" y1="1" x2="width-4" y2="1"/>
<line color="shade/gtk:bg[NORMAL]/0.6" x1="3" y1="height-2" x2="width-4" y2="height-2"/>
<line color="shade/gtk:bg[NORMAL]/0.6" x1="1" y1="3" x2="1" y2="height-4"/>
<line color="shade/gtk:bg[NORMAL]/0.6" x1="width-2" y1="3" x2="width-2" y2="height-4"/>
<line color="shade/gtk:bg[NORMAL]/0.6" x1="2" y1="2" x2="width-3" y2="2"/>
<line color="shade/gtk:bg[NORMAL]/0.6" x1="2" y1="height-3" x2="width-3" y2="height-3"/>
<!-- border smooth effect -->
<line color="shade/gtk:bg[NORMAL]/1.02" x1="3" y1="2" x2="width-4" y2="2"/>
<line color="shade/gtk:bg[NORMAL]/1.00" x1="2" y1="3" x2="2" y2="height-4"/>
<line color="shade/gtk:bg[NORMAL]/0.95" x1="width-3" y1="3" x2="width-3" y2="height-4"/>
<!-- inside highlight -->
<line color="shade/gtk:bg[NORMAL]/1.2" x1="4" y1="2" x2="width-5" y2="2"/>
<line color="shade/gtk:bg[NORMAL]/1.1" x1="2" y1="4" x2="2" y2="height-5"/>
<!-- inside shadow -->
<line color="shade/gtk:bg[NORMAL]/1.05" x1="width-3" y1="4" x2="width-3" y2="height-5"/>
<!-- fill gradient -->
<gradient type="vertical" x="3" y="3" width="width-6" height="(height)/2-1">
<color value="shade/gtk:bg[NORMAL]/1.15"/>
<color value="shade/gtk:bg[NORMAL]/1.07"/>
</gradient>
<gradient type="vertical" x="3" y="(height)/2" width="width-6" height="(height)/2-2">
<color value="shade/gtk:bg[NORMAL]/1.05"/>
<color value="shade/gtk:bg[NORMAL]/0.97"/>
</gradient>
<!-- bottom border smooth effect -->
<line color="shade/gtk:bg[NORMAL]/0.89" x1="3" y1="height-3" x2="width-4" y2="height-3"/>
<line color="shade/gtk:bg[NORMAL]/0.97" x1="4" y1="height-3" x2="width-5" y2="height-3"/>
</draw_ops>
<draw_ops name="button_bg_prelight">
<include name="button_bg"/>
<tint color="shade/gtk:bg[SELECTED]/1.5" alpha="0.2" x="3" y="3" width="width-5" height="height-5"/>
<line color="shade/gtk:bg[SELECTED]/0.6" x1="width-3" y1="height-3" x2="width-3" y2="height-3"/>
</draw_ops>
<draw_ops name="button_bg_pressed">
<!-- outside highlight -->
<gradient type="vertical" x="width-2" y="2" width="1" height="height-4">
<color value="shade/gtk:bg[SELECTED]/1.2"/>
<color value="shade/gtk:bg[SELECTED]/1.0"/>
</gradient>
<gradient type="vertical" x="width-1" y="3" width="1" height="height-6">
<color value="shade/gtk:bg[SELECTED]/1.2"/>
<color value="shade/gtk:bg[SELECTED]/1.0"/>
</gradient>
<line color="shade/gtk:bg[SELECTED]/1.0" x1="2" y1="height-2" x2="width-3" y2="height-2"/>
<line color="shade/gtk:bg[SELECTED]/1.0" x1="3" y1="height-1" x2="width-4" y2="height-1"/>
<!-- border outline -->
<line color="shade/gtk:bg[SELECTED]/0.55" x1="3" y1="1" x2="width-4" y2="1"/>
<line color="shade/gtk:bg[SELECTED]/0.55" x1="3" y1="height-2" x2="width-4" y2="height-2"/>
<line color="shade/gtk:bg[SELECTED]/0.55" x1="1" y1="3" x2="1" y2="height-4"/>
<line color="shade/gtk:bg[SELECTED]/0.55" x1="width-2" y1="3" x2="width-2" y2="height-4"/>
<line color="shade/gtk:bg[SELECTED]/0.55" x1="2" y1="2" x2="width-3" y2="2"/>
<line color="shade/gtk:bg[SELECTED]/0.55" x1="2" y1="height-3" x2="width-3" y2="height-3"/>
<!-- inside shadow -->
<line color="shade/gtk:bg[SELECTED]/0.9" x1="3" y1="2" x2="width-4" y2="2"/>
<line color="shade/gtk:bg[SELECTED]/0.85" x1="2" y1="3" x2="2" y2="height-4"/>
<!-- fill gradient -->
<gradient type="vertical" x="3" y="3" width="width-5" height="height-6">
<color value="shade/gtk:bg[SELECTED]/0.95"/>
<color value="shade/gtk:bg[SELECTED]/0.9"/>
</gradient>
<line color="shade/gtk:bg[SELECTED]/0.9" x1="3" y1="height-3" x2="width-4" y2="height-3"/>
</draw_ops>
<draw_ops name="button_bg_unfocused_prelight">
<include name="button_bg_unfocused"/>
<tint color="shade/gtk:bg[NORMAL]/1.5" alpha="0.3" x="3" y="3" width="width-5" height="height-5"/>
<line color="shade/gtk:bg[NORMAL]/0.6" x1="width-3" y1="height-3" x2="width-3" y2="height-3"/>
</draw_ops>
<draw_ops name="button_bg_unfocused_pressed">
<!-- outside highlight -->
<gradient type="vertical" x="width-2" y="2" width="1" height="height-4">
<color value="shade/gtk:bg[NORMAL]/1.25"/>
<color value="shade/gtk:bg[NORMAL]/1.05"/>
</gradient>
<gradient type="vertical" x="width-1" y="3" width="1" height="height-6">
<color value="shade/gtk:bg[NORMAL]/1.25"/>
<color value="shade/gtk:bg[NORMAL]/1.05"/>
</gradient>
<line color="shade/gtk:bg[NORMAL]/1.05" x1="2" y1="height-2" x2="width-3" y2="height-2"/>
<line color="shade/gtk:bg[NORMAL]/1.05" x1="3" y1="height-1" x2="width-4" y2="height-1"/>
<!-- border outline -->
<line color="shade/gtk:bg[NORMAL]/0.55" x1="3" y1="1" x2="width-4" y2="1"/>
<line color="shade/gtk:bg[NORMAL]/0.55" x1="3" y1="height-2" x2="width-4" y2="height-2"/>
<line color="shade/gtk:bg[NORMAL]/0.55" x1="1" y1="3" x2="1" y2="height-4"/>
<line color="shade/gtk:bg[NORMAL]/0.55" x1="width-2" y1="3" x2="width-2" y2="height-4"/>
<line color="shade/gtk:bg[NORMAL]/0.55" x1="2" y1="2" x2="width-3" y2="2"/>
<line color="shade/gtk:bg[NORMAL]/0.55" x1="2" y1="height-3" x2="width-3" y2="height-3"/>
<!-- inside shadow -->
<line color="shade/gtk:bg[NORMAL]/0.8" x1="3" y1="2" x2="width-4" y2="2"/>
<line color="shade/gtk:bg[NORMAL]/0.75" x1="2" y1="3" x2="2" y2="height-4"/>
<!-- fill gradient -->
<gradient type="vertical" x="3" y="3" width="width-5" height="height-6">
<color value="shade/gtk:bg[NORMAL]/0.9"/>
<color value="shade/gtk:bg[NORMAL]/0.85"/>
</gradient>
<line color="shade/gtk:bg[NORMAL]/0.85" x1="3" y1="height-3" x2="width-4" y2="height-3"/>
</draw_ops>
<!-- ::: ICONS ::: -->
<!--
using a minimum icon size until there is a proper way to specify relative sizes
unfortunately it's logically impossible to always center the icons on non-square
buttons (utility windows) without distortion.
icon_size = (Bmin`max`height-Bpad*2)
hpadding = (width - icon_size) / 2 = ((width-(Bmin`max`height-Bpad*2))/2)
vpadding = (height - icon_size) / 2 = ((height-(Bmin`max`height-Bpad*2))/2)
-->
<!-- menu icon -->
<draw_ops name="menu_button_icon">
<!--<icon x="0" y="0" width="width" height="height"/>-->
<icon x="(width-mini_icon_width)/2" y="(height-mini_icon_height)/2" width="mini_icon_width" height="mini_icon_height"/>
</draw_ops>
<draw_ops name="menu_button_icon_unfocused">
<!--<icon x="0" y="0" width="width" height="height" alpha="0.5"/>-->
<icon x="(width-mini_icon_width)/2" y="(height-mini_icon_height)/2" width="mini_icon_width" height="mini_icon_height"/>
</draw_ops>
<draw_ops name="menu_button_normal">
<include name="menu_button_icon"/>
</draw_ops>
<draw_ops name="menu_button_pressed">
<include name="menu_button_icon"/>
</draw_ops>
<draw_ops name="menu_button_unfocused">
<include name="menu_button_icon_unfocused"/>
</draw_ops>
<!-- close icon -->
<draw_ops name="close_button_icon">
<!-- outside border -->
<!-- main cross -->
<line color="shade/gtk:bg[SELECTED]/0.7" width="4"
x1="((width-(Bmin`max`height-Bpad*2))/2)" y1="((height-(Bmin`max`height-Bpad*2))/2)"
x2="width - ((width-(Bmin`max`height-Bpad*2))/2) - 1" y2="height - ((height-(Bmin`max`height-Bpad*2))/2) - 1"/>
<line color="shade/gtk:bg[SELECTED]/0.7" width="4"
x1="((width-(Bmin`max`height-Bpad*2))/2)" y1="height - ((height-(Bmin`max`height-Bpad*2))/2) - 1"
x2="width - ((width-(Bmin`max`height-Bpad*2))/2) - 1" y2="((height-(Bmin`max`height-Bpad*2))/2)"/>
<!-- top-left -->
<tint color="shade/gtk:bg[SELECTED]/0.7" alpha="1.0"
x="((width-(Bmin`max`height-Bpad*2))/2)" y="((height-(Bmin`max`height-Bpad*2))/2)-1"
width="2" height="1"/>
<tint color="shade/gtk:bg[SELECTED]/0.7" alpha="1.0"
x="((width-(Bmin`max`height-Bpad*2))/2)-1" y="((height-(Bmin`max`height-Bpad*2))/2)/1"
width="1" height="2"/>
<!-- top-right -->
<tint color="shade/gtk:bg[SELECTED]/0.7" alpha="1.0"
x="width - ((width-(Bmin`max`height-Bpad*2))/2) -2" y="((height-(Bmin`max`height-Bpad*2))/2)-1"
width="2" height="1"/>
<tint color="shade/gtk:bg[SELECTED]/0.7" alpha="1.0"
x="width - ((width-(Bmin`max`height-Bpad*2))/2)" y="((height-(Bmin`max`height-Bpad*2))/2)"
width="1" height="2"/>
<!-- bottom-left -->
<tint color="shade/gtk:bg[SELECTED]/0.7" alpha="1.0"
x="((width-(Bmin`max`height-Bpad*2))/2)" y="height - ((height-(Bmin`max`height-Bpad*2))/2)"
width="2" height="1"/>
<tint color="shade/gtk:bg[SELECTED]/0.7" alpha="1.0"
x="((width-(Bmin`max`height-Bpad*2))/2)-1" y="height - ((height-(Bmin`max`height-Bpad*2))/2)-2"
width="1" height="2"/>
<!-- bottom-right -->
<tint color="shade/gtk:bg[SELECTED]/0.7" alpha="1.0"
x="width - ((width-(Bmin`max`height-Bpad*2))/2) -2" y="height - ((height-(Bmin`max`height-Bpad*2))/2)"
width="2" height="1"/>
<tint color="shade/gtk:bg[SELECTED]/0.7" alpha="1.0"
x="width - ((width-(Bmin`max`height-Bpad*2))/2)" y="height - ((height-(Bmin`max`height-Bpad*2))/2)-2"
width="1" height="2"/>
<!-- icon -->
<line color="blend/gtk:bg[SELECTED]/#FFFFFF/0.75" width="2"
x1="((width-(Bmin`max`height-Bpad*2))/2)" y1="((height-(Bmin`max`height-Bpad*2))/2)"
x2="width - ((width-(Bmin`max`height-Bpad*2))/2) - 1" y2="height - ((height-(Bmin`max`height-Bpad*2))/2) - 1"/>
<line color="blend/gtk:bg[SELECTED]/#FFFFFF/0.75" width="1"
x1="((width-(Bmin`max`height-Bpad*2))/2)" y1="((height-(Bmin`max`height-Bpad*2))/2)"
x2="width- ((width-(Bmin`max`height-Bpad*2))/2)" y2="height - ((height-(Bmin`max`height-Bpad*2))/2)"/>
<line color="blend/gtk:bg[SELECTED]/#FFFFFF/0.75" width="2"
x1="((width-(Bmin`max`height-Bpad*2))/2)" y1="height - ((height-(Bmin`max`height-Bpad*2))/2) - 1"
x2="width - ((width-(Bmin`max`height-Bpad*2))/2) - 1" y2="((height-(Bmin`max`height-Bpad*2))/2)"/>
<line color="blend/gtk:bg[SELECTED]/#FFFFFF/0.75" width="1"
x1="((width-(Bmin`max`height-Bpad*2))/2)" y1="height - ((height-(Bmin`max`height-Bpad*2))/2) - 1"
x2="width - ((width-(Bmin`max`height-Bpad*2))/2)" y2="((height-(Bmin`max`height-Bpad*2))/2) - 1"/>
</draw_ops>
<draw_ops name="close_button_icon_unfocused">
<line color="blend/gtk:fg[NORMAL]/gtk:bg[NORMAL]/0.45" width="2"
x1="((width-(Bmin`max`height-Bpad*2))/2)" y1="((height-(Bmin`max`height-Bpad*2))/2)"
x2="width-((width-(Bmin`max`height-Bpad*2))/2)-1" y2="height - ((height-(Bmin`max`height-Bpad*2))/2)-1"/>
<line color="blend/gtk:fg[NORMAL]/gtk:bg[NORMAL]/0.45" width="1"
x1="((width-(Bmin`max`height-Bpad*2))/2)" y1="((height-(Bmin`max`height-Bpad*2))/2)"
x2="width-((width-(Bmin`max`height-Bpad*2))/2)" y2="height - ((height-(Bmin`max`height-Bpad*2))/2)"/>
<line color="blend/gtk:fg[NORMAL]/gtk:bg[NORMAL]/0.45" width="2"
x1="((width-(Bmin`max`height-Bpad*2))/2)" y1="height - ((height-(Bmin`max`height-Bpad*2))/2)-1"
x2="width-((width-(Bmin`max`height-Bpad*2))/2)-1" y2="((height-(Bmin`max`height-Bpad*2))/2)"/>
<line color="blend/gtk:fg[NORMAL]/gtk:bg[NORMAL]/0.45" width="1"
x1="((width-(Bmin`max`height-Bpad*2))/2)" y1="height - ((height-(Bmin`max`height-Bpad*2))/2)-1"
x2="width-((width-(Bmin`max`height-Bpad*2))/2)" y2="((height-(Bmin`max`height-Bpad*2))/2) - 1"/>
</draw_ops>
<draw_ops name="close_button_normal">
<include name="button_bg"/>
<include name="close_button_icon"/>
</draw_ops>
<draw_ops name="close_button_prelight">
<include name="button_bg_prelight"/>
<include name="close_button_icon"/>
</draw_ops>
<draw_ops name="close_button_pressed">
<include name="button_bg_pressed"/>
<include name="close_button_icon"/>
</draw_ops>
<draw_ops name="close_button_unfocused">
<include name="button_bg_unfocused"/>
<include name="close_button_icon_unfocused"/>
</draw_ops>
<draw_ops name="close_button_unfocused_prelight">
<include name="button_bg_unfocused_prelight"/>
<include name="close_button_icon_unfocused"/>
</draw_ops>
<draw_ops name="close_button_unfocused_pressed">
<include name="button_bg_unfocused_pressed"/>
<include name="close_button_icon_unfocused"/>
</draw_ops>
<!-- maximize icon -->
<draw_ops name="maximize_button_icon">
<!-- outside border -->
<rectangle color="shade/gtk:bg[SELECTED]/0.7" filled="false"
x="((width-(Bmin`max`height-Bpad*2))/2)-1" y="((height-(Bmin`max`height-Bpad*2))/2)-1"
width="width-((width-(Bmin`max`height-Bpad*2))/2)*2+1" height="height-((height-(Bmin`max`height-Bpad*2))/2)*2+1"/>
<rectangle color="shade/gtk:bg[SELECTED]/0.7" filled="false"
x="((width-(Bmin`max`height-Bpad*2))/2)+1" y="((height-(Bmin`max`height-Bpad*2))/2)+2"
width="width-((width-(Bmin`max`height-Bpad*2))/2)*2-3" height="height-((height-(Bmin`max`height-Bpad*2))/2)*2-4"/>
<!-- icon -->
<rectangle color="blend/gtk:bg[SELECTED]/#FFFFFF/0.75" filled="false"
x="((width-(Bmin`max`height-Bpad*2))/2)" y="((height-(Bmin`max`height-Bpad*2))/2)"
width="width-((width-(Bmin`max`height-Bpad*2))/2)*2-1" height="height-((height-(Bmin`max`height-Bpad*2))/2)*2-1"/>
<line color="blend/gtk:bg[SELECTED]/#FFFFFF/0.75" width="1"
x1="((width-(Bmin`max`height-Bpad*2))/2) + 1" y1="((height-(Bmin`max`height-Bpad*2))/2) + 1"
x2="width-((width-(Bmin`max`height-Bpad*2))/2)" y2="((height-(Bmin`max`height-Bpad*2))/2) + 1"/>
</draw_ops>
<draw_ops name="maximize_button_icon_unfocused">
<rectangle color="blend/gtk:fg[NORMAL]/gtk:bg[NORMAL]/0.45" filled="false"
x="((width-(Bmin`max`height-Bpad*2))/2)" y="((height-(Bmin`max`height-Bpad*2))/2)"
width="width-((width-(Bmin`max`height-Bpad*2))/2)*2-1" height="height-((height-(Bmin`max`height-Bpad*2))/2)*2-1"/>
<line color="blend/gtk:fg[NORMAL]/gtk:bg[NORMAL]/0.45" width="1"
x1="((width-(Bmin`max`height-Bpad*2))/2) + 1" y1="((height-(Bmin`max`height-Bpad*2))/2) + 1"
x2="width-((width-(Bmin`max`height-Bpad*2))/2)" y2="((height-(Bmin`max`height-Bpad*2))/2) + 1"/>
</draw_ops>
<draw_ops name="maximize_button_normal">
<include name="button_bg"/>
<include name="maximize_button_icon"/>
</draw_ops>
<draw_ops name="maximize_button_prelight">
<include name="button_bg_prelight"/>
<include name="maximize_button_icon"/>
</draw_ops>
<draw_ops name="maximize_button_pressed">
<include name="button_bg_pressed"/>
<include name="maximize_button_icon"/>
</draw_ops>
<draw_ops name="maximize_button_unfocused">
<include name="button_bg_unfocused"/>
<include name="maximize_button_icon_unfocused"/>
</draw_ops>
<draw_ops name="maximize_button_unfocused_prelight">
<include name="button_bg_unfocused_prelight"/>
<include name="maximize_button_icon_unfocused"/>
</draw_ops>
<draw_ops name="maximize_button_unfocused_pressed">
<include name="button_bg_unfocused_pressed"/>
<include name="maximize_button_icon_unfocused"/>
</draw_ops>
<!-- restore icon -->
<draw_ops name="restore_button_icon">
<!-- outside border -->
<rectangle color="shade/gtk:bg[SELECTED]/0.7" filled="false"
x="((width-(Bmin`max`height-Bpad*2))/2)" y="((height-(Bmin`max`height-Bpad*2))/2)"
width="width-((width-(Bmin`max`height-Bpad*2))/2)*2-1" height="height-((height-(Bmin`max`height-Bpad*2))/2)*2-1"/>
<rectangle color="shade/gtk:bg[SELECTED]/0.7" filled="false"
x="((width-(Bmin`max`height-Bpad*2))/2)+2" y="((height-(Bmin`max`height-Bpad*2))/2)+3"
width="width-((width-(Bmin`max`height-Bpad*2))/2)*2-5" height="height-((height-(Bmin`max`height-Bpad*2))/2)*2-6"/>
<!-- icon -->
<rectangle color="blend/gtk:bg[SELECTED]/#FFFFFF/0.75" filled="false"
x="((width-(Bmin`max`height-Bpad*2))/2) + 1" y="((height-(Bmin`max`height-Bpad*2))/2) + 1"
width="width-((width-(Bmin`max`height-Bpad*2))/2)*2-3" height="height-((height-(Bmin`max`height-Bpad*2))/2)*2-3"/>
<line color="blend/gtk:bg[SELECTED]/#FFFFFF/0.75" width="1"
x1="((width-(Bmin`max`height-Bpad*2))/2) + 2" y1="((height-(Bmin`max`height-Bpad*2))/2) + 2"
x2="width-((width-(Bmin`max`height-Bpad*2))/2) - 2" y2="((height-(Bmin`max`height-Bpad*2))/2) + 2"/>
</draw_ops>
<draw_ops name="restore_button_icon_unfocused">
<rectangle color="blend/gtk:fg[NORMAL]/gtk:bg[NORMAL]/0.45" filled="false"
x="((width-(Bmin`max`height-Bpad*2))/2) + 1" y="((height-(Bmin`max`height-Bpad*2))/2) + 1"
width="width-((width-(Bmin`max`height-Bpad*2))/2)*2-3" height="height-((height-(Bmin`max`height-Bpad*2))/2)*2-3"/>
<line color="blend/gtk:fg[NORMAL]/gtk:bg[NORMAL]/0.45" width="1"
x1="((width-(Bmin`max`height-Bpad*2))/2) + 2" y1="((height-(Bmin`max`height-Bpad*2))/2) + 2"
x2="width-((width-(Bmin`max`height-Bpad*2))/2) - 2" y2="((height-(Bmin`max`height-Bpad*2))/2) + 2"/>
</draw_ops>
<draw_ops name="restore_button_normal">
<include name="button_bg"/>
<include name="restore_button_icon"/>
</draw_ops>
<draw_ops name="restore_button_prelight">
<include name="button_bg_prelight"/>
<include name="restore_button_icon"/>
</draw_ops>
<draw_ops name="restore_button_pressed">
<include name="button_bg_pressed"/>
<include name="restore_button_icon"/>
</draw_ops>
<draw_ops name="restore_button_unfocused">
<include name="button_bg_unfocused"/>
<include name="restore_button_icon_unfocused"/>
</draw_ops>
<draw_ops name="restore_button_unfocused_prelight">
<include name="button_bg_unfocused_prelight"/>
<include name="restore_button_icon_unfocused"/>
</draw_ops>
<draw_ops name="restore_button_unfocused_pressed">
<include name="button_bg_unfocused_pressed"/>
<include name="restore_button_icon_unfocused"/>
</draw_ops>
<!-- minimize icon -->
<draw_ops name="minimize_button_icon">
<!-- outside border -->
<rectangle color="shade/gtk:bg[SELECTED]/0.7" filled="false"
x="((width-(Bmin`max`height-Bpad*2))/2)-1" y="height - ((height-(Bmin`max`height-Bpad*2))/2) - 3"
width="width-((width-(Bmin`max`height-Bpad*2))/2)*2+1" height="3"/>
<!-- icon -->
<rectangle color="blend/gtk:bg[SELECTED]/#FFFFFF/0.75" filled="true"
x="((width-(Bmin`max`height-Bpad*2))/2)" y="height - ((height-(Bmin`max`height-Bpad*2))/2) - 2"
width="width-((width-(Bmin`max`height-Bpad*2))/2)*2" height="2"/>
</draw_ops>
<draw_ops name="minimize_button_icon_unfocused">
<rectangle color="blend/gtk:fg[NORMAL]/gtk:bg[NORMAL]/0.45" filled="true"
x="((width-(Bmin`max`height-Bpad*2))/2)" y="height - ((height-(Bmin`max`height-Bpad*2))/2) - 2"
width="width-((width-(Bmin`max`height-Bpad*2))/2)*2" height="2"/>
</draw_ops>
<draw_ops name="minimize_button_normal">
<include name="button_bg"/>
<include name="minimize_button_icon"/>
</draw_ops>
<draw_ops name="minimize_button_prelight">
<include name="button_bg_prelight"/>
<include name="minimize_button_icon"/>
</draw_ops>
<draw_ops name="minimize_button_pressed">
<include name="button_bg_pressed"/>
<include name="minimize_button_icon"/>
</draw_ops>
<draw_ops name="minimize_button_unfocused">
<include name="button_bg_unfocused"/>
<include name="minimize_button_icon_unfocused"/>
</draw_ops>
<draw_ops name="minimize_button_unfocused_prelight">
<include name="button_bg_unfocused_prelight"/>
<include name="minimize_button_icon_unfocused"/>
</draw_ops>
<draw_ops name="minimize_button_unfocused_pressed">
<include name="button_bg_unfocused_pressed"/>
<include name="minimize_button_icon_unfocused"/>
</draw_ops>
<draw_ops name="blank">
<!-- nothing -->
</draw_ops>
<!-- ::: FRAME STYLES ::: -->
<frame_style name="normal" geometry="normal">
<piece position="entire_background" draw_ops="round_bevel_unfocused"/>
<piece position="title" draw_ops="title_unfocused"/>
<button function="close" state="normal" draw_ops="close_button_unfocused"/>
<button function="close" state="pressed" draw_ops="close_button_unfocused_pressed"/>
<button function="close" state="prelight" draw_ops="close_button_unfocused_prelight"/>
<button function="maximize" state="normal" draw_ops="maximize_button_unfocused"/>
<button function="maximize" state="pressed" draw_ops="maximize_button_unfocused_pressed"/>
<button function="maximize" state="prelight" draw_ops="maximize_button_unfocused_prelight"/>
<button function="minimize" state="normal" draw_ops="minimize_button_unfocused"/>
<button function="minimize" state="pressed" draw_ops="minimize_button_unfocused_pressed"/>
<button function="minimize" state="prelight" draw_ops="minimize_button_unfocused_prelight"/>
<button function="menu" state="normal" draw_ops="menu_button_normal"/>
<button function="menu" state="pressed" draw_ops="menu_button_pressed"/>
</frame_style>
<frame_style name="normal_shaded" geometry="shaded" parent="normal">
<piece position="entire_background" draw_ops="round_bevel_unfocused_shaded"/>
</frame_style>
<frame_style name="focused" geometry="normal" parent="normal">
<piece position="entire_background" draw_ops="round_bevel"/>
<piece position="title" draw_ops="title"/>
<button function="close" state="normal" draw_ops="close_button_normal"/>
<button function="close" state="pressed" draw_ops="close_button_pressed"/>
<button function="close" state="prelight" draw_ops="close_button_prelight"/>
<button function="maximize" state="normal" draw_ops="maximize_button_normal"/>
<button function="maximize" state="pressed" draw_ops="maximize_button_pressed"/>
<button function="maximize" state="prelight" draw_ops="maximize_button_prelight"/>
<button function="minimize" state="normal" draw_ops="minimize_button_normal"/>
<button function="minimize" state="pressed" draw_ops="minimize_button_pressed"/>
<button function="minimize" state="prelight" draw_ops="minimize_button_prelight"/>
</frame_style>
<frame_style name="focused_shaded" geometry="shaded" parent="focused">
<piece position="entire_background" draw_ops="round_bevel_shaded"/>
</frame_style>
<frame_style name="normal_maximized" geometry="normal_maximized" parent="normal">
<piece position="entire_background" draw_ops="bevel_maximized_unfocused"/>
<button function="maximize" state="normal" draw_ops="restore_button_unfocused"/>
<button function="maximize" state="pressed" draw_ops="restore_button_unfocused_pressed"/>
<button function="maximize" state="prelight" draw_ops="restore_button_unfocused_prelight"/>
</frame_style>
<frame_style name="focused_maximized" geometry="normal_maximized" parent="focused">
<piece position="entire_background" draw_ops="bevel_maximized"/>
<button function="maximize" state="normal" draw_ops="restore_button_normal"/>
<button function="maximize" state="pressed" draw_ops="restore_button_pressed"/>
<button function="maximize" state="prelight" draw_ops="restore_button_prelight"/>
</frame_style>
<frame_style name="border" geometry="border" parent="normal">
<piece position="entire_background" draw_ops="border"/>
<piece position="title" draw_ops="blank"/>
</frame_style>
<frame_style name="utility_normal" geometry="utility" parent="normal">
<piece position="entire_background" draw_ops="bevel_unfocused"/>
</frame_style>
<frame_style name="utility_focused" geometry="utility" parent="focused">
<piece position="entire_background" draw_ops="bevel"/>
</frame_style>
<frame_style name="utility_focused_shaded" geometry="utility" parent="focused_shaded">
<piece position="entire_background" draw_ops="bevel_shaded"/>
</frame_style>
<frame_style_set name="normal">
<frame focus="yes" state="normal" resize="both" style="focused"/>
<frame focus="no" state="normal" resize="both" style="normal"/>
<frame focus="yes" state="maximized" style="focused_maximized"/>
<frame focus="no" state="maximized" style="normal_maximized"/>
<frame focus="yes" state="shaded" style="focused_shaded"/>
<frame focus="no" state="shaded" style="normal_shaded"/>
<frame focus="yes" state="maximized_and_shaded" style="focused_maximized"/>
<frame focus="no" state="maximized_and_shaded" style="normal_maximized"/>
</frame_style_set>
<frame_style_set name="utility" parent="normal">
<frame focus="yes" state="normal" resize="both" style="utility_focused"/>
<frame focus="no" state="normal" resize="both" style="utility_normal"/>
<!-- this is a bunch of crack since utility windows shouldn't be maximized -->
<frame focus="yes" state="maximized" style="focused"/>
<frame focus="no" state="maximized" style="normal"/>
<frame focus="yes" state="shaded" style="utility_focused_shaded"/>
<frame focus="no" state="shaded" style="utility_normal"/>
<frame focus="yes" state="maximized_and_shaded" style="focused_shaded"/>
<frame focus="no" state="maximized_and_shaded" style="normal"/>
</frame_style_set>
<frame_style_set name="border">
<frame focus="yes" state="normal" resize="both" style="border"/>
<frame focus="no" state="normal" resize="both" style="border"/>
<frame focus="yes" state="maximized" style="border"/>
<frame focus="no" state="maximized" style="border"/>
<frame focus="yes" state="shaded" style="border"/>
<frame focus="no" state="shaded" style="border"/>
<frame focus="yes" state="maximized_and_shaded" style="border"/>
<frame focus="no" state="maximized_and_shaded" style="border"/>
</frame_style_set>
<window type="normal" style_set="normal"/>
<window type="dialog" style_set="normal"/>
<window type="modal_dialog" style_set="normal"/>
<window type="menu" style_set="normal"/>
<window type="utility" style_set="utility"/>
<window type="border" style_set="border"/>
<menu_icon function="close" state="normal" draw_ops="close_button_icon_unfocused"/>
<menu_icon function="maximize" state="normal" draw_ops="maximize_button_icon_unfocused"/>
<menu_icon function="unmaximize" state="normal" draw_ops="restore_button_icon_unfocused"/>
<menu_icon function="minimize" state="normal" draw_ops="minimize_button_icon_unfocused"/>
</metacity_theme>

@ -17,8 +17,8 @@
#
# Author: David Cantrell <dcantrell@redhat.com>
EXTRA_DIST = *.rst *.txt
EXTRA_DIST = $(srcdir)/*.rst $(srcdir)/*.txt
CLEANFILES = api *.xml
CLEANFILES = api $(builddir)/*.xml
MAINTAINERCLEANFILES = Makefile.in

File diff suppressed because it is too large Load Diff

@ -312,9 +312,14 @@ Requires the remote syslog process to accept incoming connections.
=== inst.virtiolog ===
Forward logs through the named virtio port (a character device at
`/dev/virtio-ports/<name>`). A port named `org.fedoraproject.anaconda.log.0`
`/dev/virtio-ports/<name>`).
If not provided, a port named `org.fedoraproject.anaconda.log.0`
will be used by default, if found.
See https://fedoraproject.org/wiki/Anaconda/Logging for more info on setting
up logging via virtio.
=== inst.zram ===
Forces/disables (on/off) usage of zRAM swap for the installation process.

@ -0,0 +1,63 @@
Anaconda Kickstart Documentation
================================
Anaconda uses `kickstart <https://github.com/rhinstaller/pykickstart>`_ to automate
installation and as a data store for the user interface. It also extends the kickstart
commands `documented here <https://fedoraproject.org/wiki/Anaconda/Kickstart>`_
by adding a new kickstart section named %anaconda where commands to control the behavior
of Anaconda will be defined.
.. contents:: %anaconda section commands
pwpolicy
========
pwpolicy <name> [--minlen=LENGTH] [--minquality=QUALITY] [--strict|notstrict] [--emptyok|notempty] [--changesok|nochanges]
Set the policy to use for the named password entry.
name
Name of the password entry, currently supported values are: root, user and luks
--minlen (**8**)
Minimum password length. This is passed on to libpwquality.
--minquality (**50**)
Minimum libpwquality to consider good. When using --strict it will not allow
passwords with a quality lower than this.
--strict (**DEFAULT**)
Strict password enforcement. Passwords not meeting the --minquality level will
not be allowed.
--notstrict
Passwords not meeting the --minquality level will be allowed after Done is clicked
twice.
--emptyok (**DEFAULT**)
Allow empty password.
--notempty
Don't allow an empty password
--changesok
Allow UI to be used to change the password/user when it has already been set in
the kickstart.
--nochanges (**DEFAULT**)
Do not allow UI to be used to change the password/user if it has been set in
the kickstart.
The defaults for these are set in the /usr/share/anaconda/interactive-defaults.ks file
provided by Anaconda. If a product, such as Fedora Workstation, wishes to override them
then a product.img needs to be created with a new version of the file included.
When using a kickstart the defaults can be overridded by placing a %anaconda section into
the kickstart, like this::
%anaconda
pwpolicy root --minlen=10 --minquality=60 --strict --notempty --nochanges
%end
.. note:: The commit message for pwpolicy included some incorrect examples.

@ -1,13 +0,0 @@
Updated June 11, 2002
---------------------
Current list of things we check for (good for regression testing):
- That selected PE is smaller than any PV in VG.
- That size requests for LV are a multiple of the PE.
- That maximum LV for a given PE is bigger than any currently defined LV in VG.
- That if you change the PE, all the reclamped LV will fit in VG.
- That VG_name+LV_name is unique for all VG.
- That mount points are used only once.
- Give warning if more than 10% of a PV is lost because of the PE selected.
- That the bootable partition is not a LV.

@ -1,15 +0,0 @@
How to make screenshots:
This will only currently work for graphical installs.
During a graphical installation, you can now press SHIFT-Print Screen
and a screenshot of the current installation screen will be taken.
These are stored in the following directory:
/root/anaconda-screenshots/
The screenshots can be accessed once the newly-installed system is rebooted.

@ -1,25 +0,0 @@
Mediacheck documentation
October, 2008
Mediacheck is a tool we use to test the integrity of ISO images. The
ISO9660 specification allows for a 512 byte region which is for
application use. We use it to store a checksum of the ISO image. Of
couse putting the checksum into this region will change the checksum
of the final ISO image, so when we verify the checksum later we have
to fill in this 512 region with the original contents before the
implantation. It is set to all ASCII 32 decimal spaces.
If you have a set of ISO images you can implant the checksum data with
this command:
implantmd5iso <isoname>
NOTE: You cannot re-implant an ISO once its been implanted.
To check a ISO image you can use this command line:
checkisomd5 <isoname>
The tools are in the isomd5sum package.

@ -1,16 +0,0 @@
Rescue mode
-----------
1/4/1999 Michael Fulbright
Rescue mode is implemented via a bootable CDROM currently. The user
specifies 'linux rescue' at the syslinux prompt when the CDROM boots.
Then the kernel and an initial ramdisk is loaded. The installer is
run and the user is dropped into bash.
The upd-instroot script in the anaconda/ source toplevel directory
is responsible for creating the instimage which is used as the
root for the rescue environment. It is located in /mnt/cdrom/Redhat/instimage
when the CDROM is mounted under /mnt/cdrom.

@ -1,100 +0,0 @@
Threads in anaconda? No!
David Cantrell <dcantrell@redhat.com>
INTRODUCTION
Threads make a lot of people run screaming. That's entirely
understandable because thread concurrency can be a pain. In this short
document, I want to explain why threads are in anaconda and how to work
with them in the code.
MISCONCEPTIONS
Just to make sure everyone is on the same page, threads are similar to
processes. The big advantage we get is easier shared data structures.
Threads can communicate over more methods than just signals. But,
multithreaded does not mean that we are taking every operation out to
a separate thread.
ANACONDA THREADS
The idea for anaconda threads is to simplify life for things that can
or need to run parallel to other operations. So we will reserve the
use of threads for tasks that fit in to this category well (the logging
system, for instance) and keep the bulk of the installer in the main
thread.
THREADS AND PYTHON
Python has a nice model for threading. Threads in Python extend the
threading.Thread class. So an easy way to identify something that will
run or can be run as a thread is seeing a class definition like this:
class SomeClass(threading.Thread):
You still have your __init__ method for the constructor, but threads
also have a run() method and a join() method (there are others, but
I will just discuss these).
The run() method is called when you start the thread. This is where
you want to do the work. Normally this happens in the class
constructor, but in threads we need that separated out to a different
method.
The join() method is to block execution of other threads. Whatever you
put in the join() method will run and other threads will be blocked
while it runs. Now, this method is only run when you call it explicitly
from another thread, so think of it as similar to waitpid().
Python has the thread and threading modules. Use threading as it's
built on top of thread and provides a threading system similar to the
POSIX thread model.
More information:
http://docs.python.org/lib/module-threading.html
THREAD NAMES
Threads have names in Python. They are automatically assigned or you
can specify the name. For anaconda it probably makes more sense to
name our threads since we won't be launching more than one for the
same task.
The convention I'm using is: NameThr
For example: RelNotesThr
The name is arbitrary, but we should probably have some sort of
consistency.
PYGTK AND THREADS
GTK+ presents the biggest challenge for threads, but it's not
impossible. When you call gtk.main(), you need to call it like this:
gtk.threads_enter()
gtk.main()
gtk.threads_leave()
Calls to PyGTK methods or fiddling with GTK objects...all that has to
be wrapped in threads_enter/threads_leave calls. The suggested syntax
is:
gtk.threads_enter()
try:
# do stuff
finally:
gtk.threads_leave()
Suggested reading:
http://www.async.com.br/faq/pygtk/index.py?req=show&file=faq20.006.htp
http://developer.gnome.org/doc/API/2.0/gdk/gdk-Threads.html
I will try to expand this document as I move through more threading code.
Email me if you have any questions.
--
David Cantrell
<dcantrell@redhat.com>

@ -1,129 +0,0 @@
Transifex and anaconda Development
09-Mar-2011
by: David Cantrell <dcantrell@redhat.com>
-----------------------------------------------------------------------------
Setting up the new transifex-client on your system for anaconda builds.
1) Install the transifex-client package:
yum install transifex-client
-or-
yum --enablerepo=updates-testing install transifex-client
-or-
yum --enablerepo=epel-testing install transifex-client
2) Create a Transifex.net account at https://fedora.transifex.net/
NOTE: This system is not linked to FAS, it's hosted by another company,
so it requires another account at this time. I'm sure this will change
in the future, but this is how it is for now.
3) Configure 'tx' on your system:
tx init
Accept default host, fill in your username and password generated in #2.
Now tx is set up on your system. The translation files will only be pulled
when a 'make release' is done. The 'make dist' step will just create a tar
file of the what we have in our repo. The 'make bumpver' step will also
push a new anaconda.pot file to Transifex, so any string changes are pushed
to them on a regular basis.
NOTE: tx pull is slow. This is why I only added it to the 'make bumpver'
step.
There are some other procedures related to tx that will have to be done
when we create new branches or when there are translation errors.
MAKING A RELEASE
----------------
git clean -d -x -f
./autogen.sh && ./configure --disable-static \
--enable-introspection --enable-gtk-doc
make bumpver # tx pull by dependent po-pull target
git commit -a -m "New version." # DO NOT run 'git clean -d -x -f' after
make && make release # signed tag happens after dist now
The process here is mostly the same. I do not recommend that you run
git clean between 'make bumpver' and 'make release'. The reason is you
will have to run 'tx pull' again and that's slow, plus translations may
have changed between the two steps.
The 'make tag' step now runs after 'make dist' in case dist generation
fails. That way you don't end up with a partially created dist AND a
bad tag you have to delete.
The 'make scratch' target will also run po-pull to get translations. If
we need translation files in other targets, we can add po-pull as a
dependent target.
DEALING WITH ERRORS IN *.po FILES
---------------------------------
Translators sometimes introduce errors in the .po files. What we generally
do is try to fix it if it's an obvious typo, or just revert the change and
go back to the old po file. Reverting is harder now since we are not
storing po files in our repo, but in severe cases we can go and fetch the
last build and pull the affected po file from there and use it to revert the
changes.
Here's an example of a po file error that will halt a 'make release':
rm -f af.gmo && /usr/bin/msgfmt -c --statistics -o af.gmo af.po
af.po:7: field `Language-Team' still has initial default value
af.po:1614: number of format specifications in 'msgid' and 'msgstr[1]' does not match
/usr/bin/msgfmt: found 1 fatal error
In this case, I am going to the last known good af.po. To update Transifex,
I do:
cp /path/to/last/known/good/af.po po/af.po
touch po/af.po
tx push -t -l af
The touch is necessary because transifex.net uses timestamps to determine
if it should update its translation data with the po file you are asking
it to use.
CREATING A NEW ANACONDA BRANCH
------------------------------
When we make a new branch, we need to branch the translation files.
First you need to populate the project with the initial po files. I suggest
using the ones from the master branch, e.g.:
git checkout master
git clean -xdf
tx pull -a
# leave the *.po files in the po/ subdirectory
git checkout BRANCH_NAME
Next you need to update the transifex config with the new branch:
tx set --execute --auto-local -r anaconda.BRANCH_NAME -s en -t PO \
-f po/anaconda.pot "po/<lang>.po"
The last argument is correct as-is, it's not a place where you substitute
something for <lang>. The BRANCH_NAME will be something other than 'master'.
For example, when we branch for F-20:
tx set --execute --auto-local -r anaconda.f20-branch -s en -t PO \
-f po/anaconda.pot "po/<lang>.po"
Check the .tx/config file on the branch to ensure it references the correct
anaconda.BRANCH_NAME in Transifex and remove the [anaconda.master] block so
that it doesn't try to push to master and the new branch.
Now you can run:
tx push -s -t
This will push the po files and anaconda.pot from master to the BRANCH_NAME
resource for anaconda in Transifex. This is just an initial seed that the
translation team can work with. And since we branch from master, the code
should be more or less in sync with the po files at branch time.
Don't forget to commit the new .tx/config file to the branch.

@ -0,0 +1,116 @@
Translations and anaconda Development
24-Feb-2015
by: David Cantrell <dcantrell@redhat.com>
Brian C. Lane <bcl@redhat.com>
-----------------------------------------------------------------------------
Anaconda, as of version 23.1, is using https://fedora.zanata.com for
translations. You will need an account in order to pull translations until bug
https://bugzilla.redhat.com/show_bug.cgi?id=1172618 (anonymous pull requests)
has been resolved.
The zanata-python-client is used by the build scripts, not the full zanata
package.
CLIENT SETUP
------------
Setting up the zanata-python-client on your system for anaconda builds.
1) Install the zanata-python-client package:
yum install zanata-python-client
2) Create a Zanata account at https://fedora.zanata.org
NOTE: This system is linked to FAS, but the 'remember my login' option
only appears to work for about an hour, not 7 days as it claims.
3) Navigate to Dashboard->Settings->Client and click 'Generate New API Key'
4) Copy the contents of the Configuration text box to ~/.config/zanata.ini
and make sure permissions are 0600.
Now zanata is set up on your system. The translation files will only be pulled
when a 'make release' is done. The 'make dist' step will just create a tar
file of the what we have in our repo. The 'make bumpver' step will also push a
new anaconda.pot file to Zanata, so any string changes are pushed to them on a
regular basis.
NOTE: zanata pull is slow. This is why I only added it to the 'make bumpver'
step.
There are some other procedures related to zanata that will have to be done
when we create new branches or when there are translation errors.
MAKING A RELEASE
----------------
git clean -d -x -f
./autogen.sh && ./configure --disable-static \
--enable-introspection --enable-gtk-doc
make bumpver # zanata pull by dependent po-pull target
git commit -a -m "New version." # DO NOT run 'git clean -d -x -f' after
make && make release # signed tag happens after dist now
The process here is mostly the same. I do not recommend that you run
git clean between 'make bumpver' and 'make release'. The reason is you
will have to run 'zanata pull' again and that's slow, plus translations may
have changed between the two steps.
The 'make tag' step now runs after 'make dist' in case dist generation
fails. That way you don't end up with a partially created dist AND a
bad tag you have to delete.
The 'make scratch' and 'make po-empty' targets will now create empty
translation files so that local test builds can be created without needing a
zanata account.
DEALING WITH ERRORS IN *.po FILES
---------------------------------
Translators sometimes introduce errors in the .po files. What we generally
do is try to fix it if it's an obvious typo, or just revert the change and
go back to the old po file. Reverting is harder now since we are not
storing po files in our repo, but in severe cases we can go and fetch the
last build and pull the affected po file from there and use it to revert the
changes.
Here's an example of a po file error that will halt a 'make release':
rm -f af.gmo && /usr/bin/msgfmt -c --statistics -o af.gmo af.po
af.po:7: field `Language-Team' still has initial default value
af.po:1614: number of format specifications in 'msgid' and 'msgstr[1]' does not match
/usr/bin/msgfmt: found 1 fatal error
In this case, I am going to the last known good af.po. To update Zanata,
I do:
cp /path/to/last/known/good/af.po po/af.po
zanata push --push-type target --lang af
CREATING A NEW ANACONDA BRANCH
------------------------------
When we make a new branch, we need to branch the translation files.
On https://fedora.zanata.com go to the project and version to use as a base.
Select 'New Version+' from the hidden drop down menu on the right. Give it a
name that matches the git branch. eg. f23-branch and select the version to copy
from (usually master). Wait for it to finish processing documents.
Create a new git branch:
git checkout master
git clean -xdf
git checkout BRANCH_NAME
At https://fedora.zanata.com select the new version and then select the
'Download Config file' from the hidden dropdown menu next to 'Settings'. This
will download a new zanata.xml file. Replace the zanata.xml file in the root
directory of anaconda project with this new one.
git add -u
git commit -m "New Zanata config file"
git push --set-upstream origin BRANCH_NAME

@ -37,6 +37,17 @@ path="$2" # optional, could be empty
modprobe -q loop
# If we're waiting for a cdrom kickstart, the user might need to swap discs.
# So if this is a CDROM drive, make a note of it, but don't mount it (yet).
# Once we get the kickstart either the udev trigger or disk-reinsertion will
# retrigger this script, and we'll mount the disk as normal.
if str_starts "$kickstart" "cdrom" && [ ! -e /tmp/ks.cfg.done ]; then
if dev_is_cdrom "$dev"; then
> /tmp/anaconda-on-cdrom
exit 0
fi
fi
info "anaconda using disk root at $dev"
mount $dev $repodir || warn "Couldn't mount $dev"
anaconda_live_root_dir $repodir $path

@ -165,6 +165,26 @@ when_any_cdrom_appears() {
>> $rulesfile
}
plymouth_running() {
type plymouth >/dev/null 2>&1 && plymouth --ping 2>/dev/null
}
# print something to the display (and put it in the log so we know what's up)
tell_user() {
if plymouth_running; then
# NOTE: if we're doing graphical splash but we don't have all the
# font-rendering libraries, no message will appear.
plymouth display-message --text="$*"
echo "$*" # this goes to journal only
else
echo "$*" >&2 # this goes to journal+console
fi
}
dev_is_cdrom() {
udevadm info --query=property --name=$1 | grep -q 'ID_CDROM=1'
}
# dracut doesn't bring up the network unless:
# a) $netroot is set (i.e. you have a network root device), or
# b) /tmp/net.ifaces exists.

@ -27,9 +27,14 @@ case $repo in
. /lib/nfs-lib.sh
info "anaconda mounting NFS repo at $repo"
str_starts "$repo" "nfsiso:" && repo=nfs:${repo#nfsiso:}
# Replace hex space with a real one. All uses of repo need to be quoted
# after this point.
repo=${repo//\\x20/ }
# HACK: work around some Mysterious NFS4 Badness (#811242 and friends)
# by defaulting to nfsvers=3 when no version is requested
nfs_to_var $repo $netif
nfs_to_var "$repo" $netif
if [ "$nfs" != "nfs4" ] && ! strstr "$options" "vers="; then
repo="nfs:$options,nfsvers=3:$server:$path"
fi

@ -1,4 +1,4 @@
#!/usr/bin/python
#!/usr/bin/python2
#
# Copyright (C) 2013 by Red Hat, Inc. All rights reserved.
#
@ -43,6 +43,7 @@ import os
import subprocess
import time
import glob
import readline # pylint:disable=unused-import
log = logging.getLogger("DD")
@ -312,7 +313,7 @@ def fake_drivers(num):
""" Generate a number of fake drivers for testing
"""
drivers = []
for i in xrange(0, num):
for i in range(0, num):
d = Driver()
d.source = "driver-%d" % i
d.flags = "modules"
@ -475,7 +476,7 @@ def selection_menu(items, title, info_func, multi_choice=True, refresh=False):
num_items = page_length
else:
num_items = len(items) % page_length
for i in xrange(0, num_items):
for i in range(0, num_items):
item_idx = ((page-1) * page_length) + i
if multi_choice:
if items[item_idx].selected:

@ -14,16 +14,27 @@ info "anaconda: fetching kickstart from $dev:$path"
mnt="$(find_mount $dev)"
if [ -n "$mnt" ]; then
cp $mnt$path /tmp/ks.cfg
cp $mnt$path /tmp/ks.cfg 2>&1
else
tmpmnt="$(mkuniqdir /run/install tmpmnt)"
if mount -o ro $dev $tmpmnt; then
cp $tmpmnt/$path /tmp/ks.cfg
cp $tmpmnt/$path /tmp/ks.cfg 2>&1
umount $tmpmnt
rmdir $tmpmnt
fi
fi
# if we're waiting for a cdrom kickstart, tell the user so they can swap discs
if str_starts "$kickstart" "cdrom"; then
if [ ! -f /tmp/ks.cfg ]; then
tell_user "Please insert CDROM containing '$path'..."
exit 0
elif [ -f /tmp/anaconda-on-cdrom ]; then
tell_user "Kickstart loaded. Please re-insert installation media."
fi
fi
if [ -f /tmp/ks.cfg ]; then
parse_kickstart /tmp/ks.cfg
run_kickstart

@ -1,4 +1,4 @@
#!/usr/bin/python
#!/usr/bin/python2
#vim: set fileencoding=utf8
# parse-kickstart - read a kickstart file and emit equivalent dracut boot args
#
@ -88,12 +88,26 @@ class NFS(commands.nfs.FC6_NFS):
else:
method="nfs:%s:%s" % (self.server, self.dir)
# Spaces on the cmdline need to be '\ '
method = method.replace(" ", "\\ ")
return "inst.repo=%s" % method
class URL(commands.url.F18_Url):
def dracut_args(self, args, lineno, obj):
# FIXME: self.proxy, self.noverifyssl
return "inst.repo=%s" % self.url
# Spaces in the url need to be %20
if self.url:
method = self.url.replace(" ", "%20")
else:
method = None
args = ["inst.repo=%s" % method]
if self.noverifyssl:
args.append("rd.noverifyssl")
if self.proxy:
args.append("proxy=%s" % self.proxy)
return "\n".join(args)
class Updates(commands.updates.F7_Updates):
def dracut_args(self, args, lineno, obj):
@ -127,7 +141,7 @@ class DriverDisk(commands.driverdisk.F14_DriverDisk):
# are processed later.
return "\n".join(dd_net)
class Network(commands.network.F21_Network):
class Network(commands.network.F22_Network):
def dracut_args(self, args, lineno, net):
'''
NOTE: The first 'network' line get special treatment:
@ -321,6 +335,16 @@ def ksnet_to_dracut(args, lineno, net, bootdev=False):
return " ".join(line)
def add_s390_settings(dev, ifcfg):
s390cfg = s390_settings(dev)
if s390cfg['SUBCHANNELS']:
ifcfg.pop('HWADDR', None)
ifcfg['SUBCHANNELS'] = s390cfg['SUBCHANNELS']
if s390cfg['NETTYPE']:
ifcfg['NETTYPE'] = s390cfg['NETTYPE']
if s390cfg['OPTIONS']:
ifcfg['OPTIONS'] = s390cfg['OPTIONS']
def ksnet_to_ifcfg(net, filename=None):
'''Write an ifcfg file for the given kickstart network config'''
dev = net.device
@ -329,8 +353,8 @@ def ksnet_to_ifcfg(net, filename=None):
if not dev:
return
if (not os.path.isdir("/sys/class/net/%s" % dev)
and not net.bondslaves and not net.teamslaves):
log.info("can't find device %s" % dev)
and not net.bondslaves and not net.teamslaves and not net.bridgeslaves):
log.info("can't find device %s", dev)
return
ifcfg = dict()
if filename is None:
@ -338,20 +362,16 @@ def ksnet_to_ifcfg(net, filename=None):
if not os.path.isdir("/tmp/ifcfg"):
os.makedirs("/tmp/ifcfg")
ifcfg['DEVICE'] = dev
ifcfg['HWADDR'] = readsysfile("/sys/class/net/%s/address" % dev)
hwaddr = readsysfile("/sys/class/net/%s/address" % dev)
if "ifname={0}:{1}".format(dev, hwaddr).upper() in open("/proc/cmdline").read().upper():
# rename by initscript's 60-net-rules on target system after switchroot
ifcfg['HWADDR'] = hwaddr
ifcfg['UUID'] = str(uuid.uuid4())
# we set real ONBOOT value in anaconda, here
# we use it to activate devcies by NM on start
ifcfg['ONBOOT'] = "yes" if net.activate else "no"
s390cfg = s390_settings(dev)
if s390cfg['SUBCHANNELS']:
ifcfg.pop('HWADDR')
ifcfg['SUBCHANNELS'] = s390cfg['SUBCHANNELS']
if s390cfg['NETTYPE']:
ifcfg['NETTYPE'] = s390cfg['NETTYPE']
if s390cfg['OPTIONS']:
ifcfg['OPTIONS'] = s390cfg['OPTIONS']
add_s390_settings(dev, ifcfg)
# dhcp etc.
ifcfg['BOOTPROTO'] = net.bootProto
@ -417,11 +437,12 @@ def ksnet_to_ifcfg(net, filename=None):
'NAME' : "%s slave %s" % (dev, i),
'UUID' : str(uuid.uuid4()),
'ONBOOT' : "yes",
'MASTER' : dev,
'MASTER' : ifcfg['UUID'],
'HWADDR' : readsysfile("/sys/class/net/%s/address" % slave),
}
add_s390_settings(slave, slave_ifcfg)
slave_filename = "/tmp/ifcfg/ifcfg-%s" % "_".join(slave_ifcfg['NAME'].split(" "))
log.info("writing ifcfg %s for slave %s of bond %s" % (slave_filename, slave, dev))
log.info("writing ifcfg %s for slave %s of bond %s", slave_filename, slave, dev)
write_ifcfg(slave_filename, slave_ifcfg)
if net.teamslaves:
@ -445,7 +466,44 @@ def ksnet_to_ifcfg(net, filename=None):
slave_ifcfg['TEAM_PORT_CONFIG'] = "'" + cfg + "'"
slave_filename = "/tmp/ifcfg/ifcfg-%s" % "_".join(slave_ifcfg['NAME'].split(" "))
log.info("writing ifcfg %s for slave %s of team %s" % (slave_filename, slave, dev))
log.info("writing ifcfg %s for slave %s of team %s", slave_filename, slave, dev)
write_ifcfg(slave_filename, slave_ifcfg)
if net.bridgeslaves:
ifcfg.pop('HWADDR', None)
ifcfg['TYPE'] = "Bridge"
ifcfg['NAME'] = "Bridge connection %s" % dev
options = {}
for opt in net.bridgeopts.split(","):
key, _sep, value = opt.partition("=")
if not value:
log.error("Invalid bridge option %s", opt)
continue
key = key.replace('-', '_')
options[key] = value
stp = options.pop("stp", None)
if stp:
ifcfg['STP'] = stp
delay = options.pop("forward_delay", None)
if delay:
ifcfg['DELAY'] = delay
if options:
keyvalues = ["%s=%s" % (key, options[key]) for key in options]
ifcfg['BRIDGING_OPTS'] = '"' + " ".join(keyvalues) + '"'
for i, slave in enumerate(net.bridgeslaves.split(","), 1):
slave_ifcfg = {
'TYPE' : "Ethernet",
'NAME' : "%s slave %s" % (dev, i),
'UUID' : str(uuid.uuid4()),
'ONBOOT' : "yes",
'BRIDGE' : dev,
'HWADDR' : readsysfile("/sys/class/net/%s/address" % slave),
}
slave_filename = "/tmp/ifcfg/ifcfg-%s" % "_".join(slave_ifcfg['NAME'].split(" "))
log.info("writing ifcfg %s for slave %s of bridge %s", slave_filename, slave, dev)
write_ifcfg(slave_filename, slave_ifcfg)
if net.vlanid:
@ -459,7 +517,7 @@ def ksnet_to_ifcfg(net, filename=None):
ifcfg['PHYSDEV'] = dev
filename = "/tmp/ifcfg/ifcfg-%s" % interface_name
log.info("writing ifcfg %s for %s" % (filename, dev))
log.info("writing ifcfg %s for %s", filename, dev)
if write_ifcfg(filename, ifcfg):
return filename
@ -470,7 +528,7 @@ def write_ifcfg(filename, ifcfg):
for k,v in ifcfg.items():
f.write("%s=%s\n" % (k,v))
except IOError as e:
log.error("can't write %s: %s" % (filename, e))
log.error("can't write %s: %s", filename, e)
return False
return True
@ -479,6 +537,7 @@ def process_kickstart(ksfile):
handler.ksdevice = os.environ.get('ksdevice')
parser = KickstartParser(handler, missingIncludeIsFatal=False, errorsAreFatal=False)
parser.registerSection(NullSection(handler, sectionOpen="%addon"))
parser.registerSection(NullSection(handler, sectionOpen="%anaconda"))
log.info("processing kickstart file %s", ksfile)
processed_file = preprocessKickstart(ksfile)
try:

@ -1,6 +1,7 @@
#!/usr/bin/python
#!/usr/bin/python2
# python-deps - find the dependencies of a given python script.
import copy
import os, sys
from modulefinder import ModuleFinder
# pylint: disable=wildcard-import
@ -9,6 +10,8 @@ from distutils.sysconfig import *
sitedir = get_python_lib()
libdir = get_config_var('LIBDEST')
alsoNeeded = { libdir+"/urllib.py": libdir+"/urllib2.py" }
# A couple helper functions...
def moduledir(pyfile):
'''Given a python file, return the module dir it belongs to, or None.'''
@ -30,10 +33,15 @@ def pyfiles(moddir):
# OK. Use modulefinder to find all the modules etc. this script uses!
mods = []
deps = []
for script in sys.argv[1:]:
scripts = copy.copy(sys.argv[1:])
while scripts:
script = scripts.pop()
finder = ModuleFinder()
finder.run_script(script) # parse the script
for name, mod in finder.modules.iteritems():
for mod in finder.modules.itervalues():
if not mod.__file__: # this module is builtin, so we can skip it
continue
@ -45,6 +53,9 @@ for script in sys.argv[1:]:
deps += list(pyfiles(moddir)) # ...get the whole module
mods.append(moddir)
if mod.__file__ in alsoNeeded and alsoNeeded[mod.__file__] not in deps:
scripts.append(alsoNeeded[mod.__file__])
# Include some bits that the python install itself needs
print(get_makefile_filename())
print(get_config_h_filename())

@ -1,22 +0,0 @@
# tests/Makefile.am for anaconda
#
# Copyright (C) 2009 Red Hat, Inc.
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published
# by the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
# Author: David Cantrell <dcantrell@redhat.com>
SUBDIRS = mock kickstart_test regex pyanaconda_test logpicker_test
MAINTAINERCLEANFILES = Makefile.in

@ -1,21 +0,0 @@
# tests/Makefile.am for anaconda
#
# Copyright (C) 2009 Red Hat, Inc.
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published
# by the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
# Author: David Cantrell <dcantrell@redhat.com>
EXTRA_DIST = *.py
MAINTAINERCLEANFILES = Makefile.in

@ -1,94 +0,0 @@
# Mocking library for module and code injection, replay tests and other
# unit testing purposes
#
# Copyright (C) 2010
# 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): Martin Sivak <msivak@redhat.com>
from disk import *
from mock import *
import unittest
def slow(f):
"""Decorates a test method as being slow, usefull for python-nose filtering"""
f.slow = True
return f
def acceptance(f):
"""Decorates test as belonging to acceptance testing and not useable in common devellopment unit testing. To be used with python-nose filtering."""
f.acceptance = True
return f
class TestCase(unittest.TestCase):
def __init__(self, *args, **kwargs):
unittest.TestCase.__init__(self, *args, **kwargs)
self.injectedModules = {}
def setupModules(self, a):
"""Mock specified list of modules and store the list so it can be
properly unloaded during tearDown"""
import sys
self.preexistingModules = set(sys.modules.keys())
for m in a:
sys.modules[m] = Mock()
self.injectedModules[m] = sys.modules[m]
def modifiedModule(self, mname, mod = None):
"""Mark module (and all it's parents) as tainted"""
oldname=""
for m in mname.split("."):
self.injectedModules[oldname+m] = mod
oldname += m + "."
self.injectedModules[mname] = mod
def tearDownModules(self):
"""Unload previously Mocked modules"""
import sys
for m in sys.modules.keys():
if m in self.preexistingModules and not m in self.injectedModules:
continue
del sys.modules[m]
def take_over_io(self, disk, target_module):
""" Trick target_module into using disk object as the filesystem.
This is achieved by overriding the module's 'open' binding as well
as many global bindings in os.path.
"""
target_module.open = disk.open
self.modifiedModule(target_module.__name__)
import glob
self.modifiedModule("glob")
glob.glob = disk.glob_glob
import os
self.modifiedModule("os.path")
# this is what os.path implementaion points at, reimport:
import posixpath
self.modifiedModule("posixpath")
os.listdir = disk.os_listdir
os.path.exists = disk.os_path_exists
os.path.isdir = disk.os_path_isdir
os.access = disk.os_access

@ -1,128 +0,0 @@
# Copyright (C) 2010
# 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): Martin Sivak <msivak@redhat.com>
from StringIO import StringIO
import fnmatch
import os
class DiskIO(object):
"""Simple object to simplify mocking of file operations in Mock
based testing"""
class TestFile(StringIO):
def __init__(self, store, path, content = ""):
StringIO.__init__(self, content)
self._store = store
self._path = path
self._ro = False
def flush(self):
self._store[self._path] = self.getvalue()
def close(self):
self.flush()
return StringIO.close(self)
def __del__(self):
try:
self.close()
except (AttributeError, ValueError):
pass
def __enter__(self):
return self
def __exit__(self, *_):
self.close()
class Dir(object):
pass
class Link(object):
pass
def __init__(self):
self.reset()
def __getitem__(self, key):
return self.fs[key]
def __setitem__(self, key, value):
self.fs[key] = value
def reset(self):
self.fs = {
"/proc": self.Dir,
"/proc/cmdline": "linux",
}
self._pwd = "/"
#Emulate file objects
def open(self, filename, mode = "r"):
path = os.path.join(self._pwd, filename)
content = self.fs.get(path, None)
if content == self.Dir:
raise IOError("[Errno 21] Is a directory: '%s'" % (path))
elif mode.startswith("w"):
self.fs[path] = ""
f = self.TestFile(self.fs, path, self.fs[path])
elif mode.endswith("a"):
if not path in self.fs:
self.fs[path] = ""
f = self.TestFile(self.fs, path, self.fs[path])
f.seek(0, os.SEEK_END)
elif content == None:
raise IOError("[Errno 2] No such file or directory: '%s'" % (path,))
elif mode.endswith("+"):
f = self.TestFile(self.fs, path, content)
if mode.startswith('r'):
f.seek(0, os.SEEK_SET)
else:
f.seek(0, os.SEEK_END)
else:
f = self.TestFile(self.fs, path, content)
return f
#Emulate os calls
def glob_glob(self, pattern):
return fnmatch.filter(self.fs.keys(), pattern)
def os_listdir(self, path):
return [entry[len(path):].lstrip('/') for entry in self.fs.keys()\
if entry.startswith(path) and entry != path]
def os_path_exists(self, path):
path = os.path.join(self._pwd, path)
return self.fs.has_key(path)
def os_path_isdir(self, path):
if not path.endswith("/"):
path += "/"
path += "*"
return len(fnmatch.filter(self.fs.keys(), path)) > 0
def os_remove(self, path):
path = os.path.join(self._pwd, path)
try:
del self.fs[path]
except KeyError:
raise OSError("[Errno 2] No such file or directory: '%s'" % (path,))
def os_access(self, path, mode):
return self.os_path_exists(path)

@ -1,271 +0,0 @@
# mock.py
# Test tools for mocking and patching.
# Copyright (C) 2007-2009 Michael Foord
# E-mail: fuzzyman AT voidspace DOT org DOT uk
# mock 0.6.0
# http://www.voidspace.org.uk/python/mock/
# Released subject to the BSD License
# Please see http://www.voidspace.org.uk/python/license.shtml
# Scripts maintained at http://www.voidspace.org.uk/python/index.shtml
# Comments, suggestions and bug reports welcome.
__all__ = (
'Mock',
'patch',
'patch_object',
'sentinel',
'DEFAULT'
)
__version__ = '0.6.0'
class SentinelObject(object):
def __init__(self, name):
self.name = name
def __repr__(self):
return '<SentinelObject "%s">' % self.name
class Sentinel(object):
def __init__(self):
self._sentinels = {}
def __getattr__(self, name):
return self._sentinels.setdefault(name, SentinelObject(name))
sentinel = Sentinel()
DEFAULT = sentinel.DEFAULT
class OldStyleClass:
pass
ClassType = type(OldStyleClass)
def _is_magic(name):
return '__%s__' % name[2:-2] == name
def _copy(value):
if type(value) in (dict, list, tuple, set):
return type(value)(value)
return value
class Mock(object):
def __init__(self, spec=None, side_effect=None, return_value=DEFAULT,
name=None, parent=None, wraps=None):
self._parent = parent
self._name = name
if spec is not None and not isinstance(spec, list):
spec = [member for member in dir(spec) if not _is_magic(member)]
self._methods = spec
self._children = {}
self._return_value = return_value
self.side_effect = side_effect
self._wraps = wraps
self.reset_mock()
def reset_mock(self):
self.called = False
self.call_args = None
self.call_count = 0
self.call_args_list = []
self.method_calls = []
for child in self._children.itervalues():
child.reset_mock()
if isinstance(self._return_value, Mock):
self._return_value.reset_mock()
def __get_return_value(self):
if self._return_value is DEFAULT:
self._return_value = Mock()
return self._return_value
def __set_return_value(self, value):
self._return_value = value
return_value = property(__get_return_value, __set_return_value)
def __call__(self, *args, **kwargs):
self.called = True
self.call_count += 1
self.call_args = (args, kwargs)
self.call_args_list.append((args, kwargs))
parent = self._parent
name = self._name
while parent is not None:
parent.method_calls.append((name, args, kwargs))
if parent._parent is None:
break
name = parent._name + '.' + name
parent = parent._parent
ret_val = DEFAULT
if self.side_effect is not None:
if (isinstance(self.side_effect, Exception) or
isinstance(self.side_effect, (type, ClassType)) and
issubclass(self.side_effect, Exception)):
raise self.side_effect
ret_val = self.side_effect(*args, **kwargs)
if ret_val is DEFAULT:
ret_val = self.return_value
if self._wraps is not None and self._return_value is DEFAULT:
return self._wraps(*args, **kwargs)
if ret_val is DEFAULT:
ret_val = self.return_value
return ret_val
def __getattr__(self, name):
if self._methods is not None:
if name not in self._methods:
raise AttributeError("Mock object has no attribute '%s'" % name)
elif _is_magic(name):
raise AttributeError(name)
if name not in self._children:
wraps = None
if self._wraps is not None:
wraps = getattr(self._wraps, name)
self._children[name] = Mock(parent=self, name=name, wraps=wraps)
return self._children[name]
def assert_called_with(self, *args, **kwargs):
assert self.call_args == (args, kwargs), 'Expected: %s\nCalled with: %s' % ((args, kwargs), self.call_args)
def _dot_lookup(thing, comp, import_path):
try:
return getattr(thing, comp)
except AttributeError:
__import__(import_path)
return getattr(thing, comp)
def _importer(target):
components = target.split('.')
import_path = components.pop(0)
thing = __import__(import_path)
for comp in components:
import_path += ".%s" % comp
thing = _dot_lookup(thing, comp, import_path)
return thing
class _patch(object):
def __init__(self, target, attribute, new, spec, create):
self.target = target
self.attribute = attribute
self.new = new
self.spec = spec
self.create = create
self.has_local = False
def __call__(self, func):
if hasattr(func, 'patchings'):
func.patchings.append(self)
return func
def patched(*args, **keywargs):
# don't use a with here (backwards compatability with 2.5)
extra_args = []
for patching in patched.patchings:
arg = patching.__enter__()
if patching.new is DEFAULT:
extra_args.append(arg)
args += tuple(extra_args)
try:
return func(*args, **keywargs)
finally:
for patching in getattr(patched, 'patchings', []):
patching.__exit__()
patched.patchings = [self]
patched.__name__ = func.__name__
patched.compat_co_firstlineno = getattr(func, "compat_co_firstlineno",
func.func_code.co_firstlineno)
return patched
def get_original(self):
target = self.target
name = self.attribute
create = self.create
original = DEFAULT
if _has_local_attr(target, name):
try:
original = target.__dict__[name]
except AttributeError:
# for instances of classes with slots, they have no __dict__
original = getattr(target, name)
elif not create and not hasattr(target, name):
raise AttributeError("%s does not have the attribute %r" % (target, name))
return original
def __enter__(self):
new, spec, = self.new, self.spec
original = self.get_original()
if new is DEFAULT:
# XXXX what if original is DEFAULT - shouldn't use it as a spec
inherit = False
if spec == True:
# set spec to the object we are replacing
spec = original
if isinstance(spec, (type, ClassType)):
inherit = True
new = Mock(spec=spec)
if inherit:
new.return_value = Mock(spec=spec)
self.temp_original = original
setattr(self.target, self.attribute, new)
return new
def __exit__(self, *_):
if self.temp_original is not DEFAULT:
setattr(self.target, self.attribute, self.temp_original)
else:
delattr(self.target, self.attribute)
del self.temp_original
def patch_object(target, attribute, new=DEFAULT, spec=None, create=False):
return _patch(target, attribute, new, spec, create)
def patch(target, new=DEFAULT, spec=None, create=False):
try:
target, attribute = target.rsplit('.', 1)
except (TypeError, ValueError):
raise TypeError("Need a valid target to patch. You supplied: %r" % (target,))
target = _importer(target)
return _patch(target, attribute, new, spec, create)
def _has_local_attr(obj, name):
try:
return name in vars(obj)
except TypeError:
# objects without a __dict__
return hasattr(obj, name)

@ -1,45 +0,0 @@
# tests/pyanaconda_test/Makefile.am for anaconda
#
# Copyright (C) 2010 Red Hat, Inc.
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published
# by the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
# Author: Tomas Mlcoch <tmlcoch@redhat.com>
EXTRA_DIST = *.py
MAINTAINERCLEANFILES = Makefile.in
ANACDIR = $(top_builddir)/pyanaconda
TESTS_ENVIRONMENT = PYTHONPATH=$(top_builddir)/tests:$(ANACDIR)/isys/.libs:$(ANACDIR):$(top_builddir)
TESTS = backend_test.py \
bootloader_test.py \
cmdline_test.py \
desktop_test.py \
flags_test.py \
image_test.py \
language_test.py \
network_test.py \
packages_test.py \
packaging_test.py \
partintfhelpers_test.py \
product_test.py \
rescue_test.py \
security_test.py \
simpleconfig_test.py \
timezone_test.py \
upgrade_test.py \
users_test.py \
vnc_test.py

@ -1,227 +0,0 @@
#!/usr/bin/python
import mock
class BackendTest(mock.TestCase):
def setUp(self):
import pykickstart.commands
self.setupModules(["_isys", "block", 'parted', 'storage',
'pyanaconda.storage.formats', 'logging',
'logging.config',
'ConfigParser', 'pyanaconda.anaconda_log',
'pyanaconda.storage.storage_log',
'pyanaconda.yuminstall'])
import pyanaconda
pyanaconda.anaconda_log = mock.Mock()
import logging
self.logger = mock.Mock()
logging.getLogger.return_value = self.logger
self.DD_EXTRACTED = '/tmp/DD'
import pyanaconda.backend
pyanaconda.backend.os = mock.Mock()
pyanaconda.backend.DD_EXTRACTED = self.DD_EXTRACTED
pyanaconda.backend.glob = mock.Mock()
pyanaconda.backend.shutil = mock.Mock()
self.logger.reset_mock()
def tearDown(self):
self.tearDownModules()
def anaconda_backend_copy_firmware_test(self):
import pyanaconda.backend
FILE = 'foo'
pyanaconda.backend.glob.glob.return_value = [FILE]
anaconda = mock.Mock()
ab = pyanaconda.backend.AnacondaBackend(anaconda)
ab.copyFirmware()
self.assertEqual(pyanaconda.backend.shutil.copyfile.call_args[0][0], FILE)
def anaconda_backend_do_post_install_test(self):
import pyanaconda.backend
from pyanaconda.constants import ROOT_PATH
FILE = 'foo'
A = 'a'
B = 'b'
C = 'c'
pyanaconda.backend.AnacondaBackend.copyFirmware = mock.Mock()
pyanaconda.backend.AnacondaBackend.kernelVersionList = mock.Mock(
return_value=[(A, B, C)])
pyanaconda.backend.packages = mock.Mock()
pyanaconda.backend.glob.glob.return_value = [FILE]
pyanaconda.backend.os.path.exists.return_value=True
pyanaconda.backend.os.path.basename.return_value=""
pyanaconda.backend.storage = mock.Mock()
pyanaconda.backend.sys = mock.Mock()
anaconda = mock.Mock()
anaconda.extraModules = True
ab = pyanaconda.backend.AnacondaBackend(anaconda)
ab.doPostInstall(anaconda)
self.assertEqual(pyanaconda.backend.packages.method_calls[0],
('recreateInitrd', (A, ROOT_PATH), {}))
self.assertEqual(pyanaconda.backend.shutil.method_calls[0],
('copytree', (FILE, ROOT_PATH + '/root/'), {}))
self.assertEqual(pyanaconda.backend.shutil.method_calls[1],
('copytree', (self.DD_EXTRACTED, ROOT_PATH + '/root/DD'), {}))
self.assertTrue(pyanaconda.backend.storage.writeEscrowPackets.called)
def anaconda_backend_do_install_test(self):
import pyanaconda.backend
anaconda = mock.Mock()
ab = pyanaconda.backend.AnacondaBackend(anaconda)
self.assertRaises(NotImplementedError, ab.doInstall, anaconda)
def anaconda_backend_kernel_version_list_test(self):
import pyanaconda.backend
anaconda = mock.Mock()
ab = pyanaconda.backend.AnacondaBackend(anaconda)
ret = ab.kernelVersionList()
self.assertEqual([], ret)
def anaconda_backend_get_minimum_size_mb_test(self):
import pyanaconda.backend
PART = mock.Mock()
anaconda = mock.Mock()
ab = pyanaconda.backend.AnacondaBackend(anaconda)
ret = ab.getMinimumSizeMB(PART)
self.assertEqual(0, ret)
def anaconda_backend_do_backend_setup_test(self):
import pyanaconda.backend
anaconda = mock.Mock()
ab = pyanaconda.backend.AnacondaBackend(anaconda)
ab.doBackendSetup(anaconda)
self.assertTrue(self.logger.warning.called)
def anaconda_backend_group_exists_test(self):
import pyanaconda.backend
GROUP = mock.Mock()
anaconda = mock.Mock()
ab = pyanaconda.backend.AnacondaBackend(anaconda)
ab.groupExists(GROUP)
self.assertTrue(self.logger.warning.called)
def anaconda_backend_select_group_test(self):
import pyanaconda.backend
GROUP = mock.Mock()
anaconda = mock.Mock()
ab = pyanaconda.backend.AnacondaBackend(anaconda)
ab.selectGroup(GROUP)
self.assertTrue(self.logger.warning.called)
def anaconda_backend_deselect_group_test(self):
import pyanaconda.backend
GROUP = mock.Mock()
anaconda = mock.Mock()
ab = pyanaconda.backend.AnacondaBackend(anaconda)
ab.deselectGroup(GROUP)
self.assertTrue(self.logger.warning.called)
def anaconda_backend_package_exists_test(self):
import pyanaconda.backend
PKG = mock.Mock()
anaconda = mock.Mock()
ab = pyanaconda.backend.AnacondaBackend(anaconda)
ab.packageExists(PKG)
self.assertTrue(self.logger.warning.called)
def anaconda_backend_select_package_test(self):
import pyanaconda.backend
PKG = mock.Mock()
anaconda = mock.Mock()
ab = pyanaconda.backend.AnacondaBackend(anaconda)
ab.selectPackage(PKG)
self.assertTrue(self.logger.warning.called)
def anaconda_backend_deselect_package_test(self):
import pyanaconda.backend
PKG = mock.Mock()
anaconda = mock.Mock()
ab = pyanaconda.backend.AnacondaBackend(anaconda)
ab.deselectPackage(PKG)
self.assertTrue(self.logger.warning.called)
def anaconda_backend_get_default_groups_test(self):
import pyanaconda.backend
anaconda = mock.Mock()
ab = pyanaconda.backend.AnacondaBackend(anaconda)
ab.getDefaultGroups(anaconda)
self.assertTrue(self.logger.warning.called)
def anaconda_backend_write_configuration_test(self):
import pyanaconda.backend
anaconda = mock.Mock()
ab = pyanaconda.backend.AnacondaBackend(anaconda)
ab.writeConfiguration()
self.assertTrue(self.logger.warning.called)
def do_backend_setup_1_test(self):
import pyanaconda.backend
RET = -1
pyanaconda.backend.DISPATCH_BACK = RET
anaconda = mock.Mock()
anaconda.backend.doBackendSetup.return_value = RET
ret = pyanaconda.backend.doBackendSetup(anaconda)
self.assertEqual(RET, ret)
def do_post_selection_test(self):
import pyanaconda.backend
anaconda = mock.Mock()
pyanaconda.backend.doPostSelection(anaconda)
self.assertTrue(anaconda.backend.doPostSelection.called)
def do_pre_install_test(self):
import pyanaconda.backend
anaconda = mock.Mock()
pyanaconda.backend.doPreInstall(anaconda)
self.assertTrue(anaconda.backend.doPreInstall.called)
def do_post_install_test(self):
import pyanaconda.backend
anaconda = mock.Mock()
pyanaconda.backend.doPostInstall(anaconda)
self.assertTrue(anaconda.backend.doPostInstall.called)
def do_install_test(self):
import pyanaconda.backend
anaconda = mock.Mock()
pyanaconda.backend.doInstall(anaconda)
self.assertTrue(anaconda.backend.doInstall.called)
def do_base_package_select_1_test(self):
import pyanaconda.backend
pyanaconda.backend.kickstart = mock.Mock()
anaconda = mock.Mock()
anaconda.ksdata = True
pyanaconda.backend.doBasePackageSelect(anaconda)
self.assertTrue(anaconda.backend.resetPackageSelections.called)
def do_base_package_select_2_test(self):
import pyanaconda.backend
anaconda = mock.Mock()
anaconda.ksdata = False
pyanaconda.backend.doBasePackageSelect(anaconda)
self.assertTrue(anaconda.backend.resetPackageSelections.called)
self.assertTrue(anaconda.instClass.setPackageSelection.called)
self.assertTrue(anaconda.instClass.setGroupSelection.called)
def write_configuration_test(self):
import pyanaconda.backend
anaconda = mock.Mock()
pyanaconda.backend.writeConfiguration(anaconda)
self.assertTrue(anaconda.write.called)
self.assertTrue(anaconda.backend.writeConfiguration)

@ -1,59 +0,0 @@
import mock
class ArgumentsTest(mock.TestCase):
def setUp(self):
self.setupModules(
['_isys', 'logging', 'pyanaconda.anaconda_log', 'block',
'pyanaconda.storage',
'pyanaconda.storage.devicelibs',
'pyanaconda.storage.errors'])
import pyanaconda
pyanaconda.anaconda_log = mock.Mock()
def tearDown(self):
self.tearDownModules()
def test_basic(self):
from pyanaconda.bootloader import Arguments
a = Arguments()
a.update(set(["a", "b", "c"]))
b = Arguments()
b.add("b")
diff = a - b
self.assertEqual(diff, set(["a", "c"]))
self.assertIsInstance(diff, Arguments)
assert(str(diff) in ["a c", "c a"])
def test_merge_ip(self):
from pyanaconda.bootloader import Arguments
# test that _merge_ip() doesnt break the simple case:
a = Arguments(["one", "two", "ip=eth0:dhcp"])
a._merge_ip()
self.assertEqual(a, Arguments(["one", "two", "ip=eth0:dhcp"]))
# test that it does what it's supposed to:
a = Arguments(["one", "two", "ip=eth0:dhcp", "ip=eth0:auto6",
"ip=wlan0:dhcp",
"ip=10.34.102.102::10.34.39.255:24:aklab:eth2:none"])
a._merge_ip()
self.assertEqual(a, set([
"one", "two",
"ip=wlan0:dhcp",
"ip=10.34.102.102::10.34.39.255:24:aklab:eth2:none",
"ip=eth0:auto6,dhcp"]))
def test_output_with_merge(self):
from pyanaconda.bootloader import Arguments
a = Arguments(["ip=eth0:dhcp"])
self.assertEqual(str(a), "ip=eth0:dhcp")
a = Arguments(["ip=eth0:dhcp", "ip=eth0:auto6"])
assert(str(a) in ["ip=eth0:auto6,dhcp", "ip=eth0:dhcp,auto6"])
def test_sorting(self):
from pyanaconda.bootloader import Arguments
a = Arguments(["ip=eth0:dhcp", "rhgb", "quiet",
"root=/dev/mapper/destroyers-rubies", "rd.md=0",
"rd.luks=0"])
# 'rhgb quiet' should be the final entries:
assert(str(a).endswith("rhgb quiet"))

@ -1,100 +0,0 @@
#!/usr/bin/python
import mock
import sys
class DesktopTest(mock.TestCase):
def setUp(self):
self.setupModules(["_isys", "block", "ConfigParser"])
self.fs = mock.DiskIO()
self.fs.open('/mnt/sysimage/etc/inittab', 'w').write('id:5:initdefault:')
self.fs.open('/mnt/sysimage/etc/sysconfig/desktop', 'w').write('')
import pyanaconda.desktop
pyanaconda.desktop.log = mock.Mock()
pyanaconda.desktop.open = self.fs.open
def tearDown(self):
self.tearDownModules()
def set_default_run_level_1_test(self):
import pyanaconda.desktop
dskt = pyanaconda.desktop.Desktop()
self.assertRaises(RuntimeError, dskt.setDefaultRunLevel, 1)
self.assertRaises(RuntimeError, dskt.setDefaultRunLevel, 2)
self.assertRaises(RuntimeError, dskt.setDefaultRunLevel, 4)
def set_default_run_level_2_test(self):
import pyanaconda.desktop
dskt = pyanaconda.desktop.Desktop()
dskt.setDefaultRunLevel(3)
self.assertEqual(dskt.runlevel, 3)
dskt.setDefaultRunLevel(5)
self.assertEqual(dskt.runlevel, 5)
def get_default_run_level_test(self):
import pyanaconda.desktop
dskt = pyanaconda.desktop.Desktop()
self.assertEqual(dskt.getDefaultRunLevel(), dskt.runlevel)
def set_get_default_run_level_1_test(self):
import pyanaconda.desktop
dskt = pyanaconda.desktop.Desktop()
dskt.setDefaultRunLevel(3)
self.assertEqual(dskt.getDefaultRunLevel(), 3)
def set_get_default_run_level_2_test(self):
import pyanaconda.desktop
dskt = pyanaconda.desktop.Desktop()
dskt.setDefaultRunLevel(5)
self.assertEqual(dskt.getDefaultRunLevel(), 5)
def set_default_desktop_test(self):
import pyanaconda.desktop
dskt = pyanaconda.desktop.Desktop()
dskt.setDefaultDesktop('desktop')
self.assertEqual(dskt.info['DESKTOP'], 'desktop')
def get_default_desktop_test(self):
import pyanaconda.desktop
dskt = pyanaconda.desktop.Desktop()
dskt.info['DESKTOP'] = 'foobar'
ret = dskt.getDefaultDesktop()
self.assertEqual(ret, 'foobar')
def set_get_default_desktop_test(self):
import pyanaconda.desktop
dskt = pyanaconda.desktop.Desktop()
dskt.setDefaultDesktop('foo')
ret = dskt.getDefaultDesktop()
self.assertEqual(ret, 'foo')
def write_1_test(self):
import pyanaconda.desktop
dskt = pyanaconda.desktop.Desktop()
dskt.write()
self.assertEqual(self.fs['/mnt/sysimage/etc/inittab'],
'id:3:initdefault:')
def write_2_test(self):
import pyanaconda.desktop
dskt = pyanaconda.desktop.Desktop()
dskt.setDefaultRunLevel(5)
dskt.write()
self.assertEqual(self.fs['/mnt/sysimage/etc/inittab'],
'id:5:initdefault:')
def write_3_test(self):
import pyanaconda.desktop
pyanaconda.desktop.os = mock.Mock()
pyanaconda.desktop.os.path.isdir.return_value = True
dskt = pyanaconda.desktop.Desktop()
dskt.setDefaultDesktop('foo')
dskt.write()
self.assertEqual(self.fs['/mnt/sysimage/etc/inittab'],
'id:3:initdefault:')
self.assertEqual(self.fs['/mnt/sysimage/etc/sysconfig/desktop'],
'DESKTOP="foo"\n')

@ -1,132 +0,0 @@
#!/usr/bin/python
# Test Bug 500198
import mock
import sys
class FlagsTest(mock.TestCase):
"""Simulate /proc/cmdline parameters parsing (#500198)"""
def setUp(self):
self.setupModules(["_isys", "block", "ConfigParser"])
self.fs = mock.DiskIO()
import pyanaconda.flags
self.mock2 = mock.Mock()
pyanaconda.flags.open = mock.Mock(return_value=self.mock2)
def tearDown(self):
self.tearDownModules()
def createcmdlinedict_1_test(self):
"""/proc/cmdline without BOOT_IMAGE param"""
import pyanaconda.flags
self.cmd = 'vmlinuz initrd=initrd.img stage2=hd:LABEL="Fedora" xdriver=vesa nomodeset'
self.mock2.read = mock.Mock(return_value=self.cmd)
cmddict = pyanaconda.flags.flags.createCmdlineDict()
self.assertEqual(set(cmddict.keys()),
set(['vmlinuz', 'initrd', 'stage2', 'xdriver', 'nomodeset']))
def createcmdlinedict_2_test(self):
"""/proc/cmdline param: quotes at end"""
import pyanaconda.flags
self.cmd = 'vmlinuz BOOT_IMAGE=/boot/img initrd=initrd.img stage2=hd:LABEL="Fedora"'
self.mock2.read = mock.Mock(return_value=self.cmd)
try:
cmddict = pyanaconda.flags.flags.createCmdlineDict()
except (ValueError):
self.assertTrue(False, "ValueError exception was raised.")
self.assertEqual(set(cmddict.keys()),
set(['vmlinuz', 'BOOT_IMAGE', 'initrd', 'stage2']))
def createcmdlinedict_3_test(self):
"""/proc/cmdline param BOOT_IMAGE with quotes (no quotes at end)"""
import pyanaconda.flags
self.cmd = 'vmlinuz BOOT_IMAGE="img img" initrd=initrd.img'
self.mock2.read = mock.Mock(return_value=self.cmd)
cmddict = pyanaconda.flags.flags.createCmdlineDict()
self.assertEqual(set(cmddict.keys()),
set(['vmlinuz', 'BOOT_IMAGE', 'initrd']))
def createcmdlinedict_4_test(self):
"""/proc/cmdline param BOOT_IMAGE with quotes (no quotes at end) v2"""
import pyanaconda.flags
self.cmd = 'vmlinuz BOOT_IMAGE="/boot/img" stage2=hd:LABEL="Fedora" initrd=initrd.img'
self.mock2.read = mock.Mock(return_value=self.cmd)
cmddict = pyanaconda.flags.flags.createCmdlineDict()
self.assertEqual(set(cmddict.keys()),
set(['vmlinuz', 'BOOT_IMAGE', 'initrd', 'stage2']))
def createcmdlinedict_5_test(self):
"""/proc/cmdline param: BOOT_IMAGE with quotes (+ quotes at end)"""
import pyanaconda.flags
self.cmd = 'vmlinuz BOOT_IMAGE="/boot/img img" initrd=initrd.img stage2=hd:LABEL="Fedora"'
self.mock2.read = mock.Mock(return_value=self.cmd)
try:
cmddict = pyanaconda.flags.flags.createCmdlineDict()
except (ValueError):
self.assertTrue(False, "ValueError exception was raised.")
self.assertEqual(set(cmddict.keys()),
set(['vmlinuz', 'BOOT_IMAGE', 'initrd', 'stage2']))
def setattr_getattr_1_test(self):
import pyanaconda.flags
RET = 1
self.cmd = 'vmlinuz initrd=initrd.img xdriver=vesa nomodeset'
self.mock2.read = mock.Mock(return_value=self.cmd)
pyanaconda.flags.flags.sshd = RET
self.assertEqual(RET, pyanaconda.flags.flags.sshd)
def setattr_getattr_2_test(self):
import pyanaconda.flags
RET = 0
self.cmd = 'vmlinuz initrd=initrd.img xdriver=vesa nomodeset'
self.mock2.read = mock.Mock(return_value=self.cmd)
pyanaconda.flags.flags.sshd = RET
self.assertEqual(RET, pyanaconda.flags.flags.sshd)
def setattr_getattr_3_test(self):
import pyanaconda.flags
self.cmd = 'vmlinuz initrd=initrd.img xdriver=vesa nomodeset'
self.mock2.read = mock.Mock(return_value=self.cmd)
def f(): return pyanaconda.flags.flags.fooattr
self.assertRaises(AttributeError, f)
def setattr_getattr_4_test(self):
import pyanaconda.flags
self.cmd = 'vmlinuz initrd=initrd.img xdriver=vesa nomodeset'
self.mock2.read = mock.Mock(return_value=self.cmd)
def f(): pyanaconda.flags.flags.fooattr = 1
self.assertRaises(AttributeError, f)
def get_1_test(self):
import pyanaconda.flags
RET = 'text'
self.cmd = 'vmlinuz initrd=initrd.img xdriver=vesa nomodeset'
self.mock2.read = mock.Mock(return_value=self.cmd)
ret = pyanaconda.flags.flags.get('foobar', RET)
self.assertEqual(RET, ret)
def get_2_test(self):
import pyanaconda.flags
RET = 'text'
self.cmd = 'vmlinuz initrd=initrd.img xdriver=vesa nomodeset'
self.mock2.read = mock.Mock(return_value=self.cmd)
ret = pyanaconda.flags.flags.get('sshd', RET)
self.assertNotEqual(RET, ret)

@ -1,147 +0,0 @@
#!/usr/bin/python
import mock
import sys
IMAGENAME = 'Fedora-13-i386-DVD.iso'
class ImageTest(mock.TestCase):
def setUp(self):
self.setupModules(["_isys", "block", "ConfigParser"])
self.fs = mock.DiskIO()
DISCINFO = "1273712438.740122\n"
DISCINFO += "Fedora 13\n"
DISCINFO += "i386\n"
DISCINFO += "ALL\n"
DISCINFO2 = "1273712438.740122\n"
DISCINFO2 += "Fedora 13\n"
DISCINFO2 += "i386\n"
DISCINFO2 += "1,2\n"
self.fs.open('/mnt/install/cdimage/.discinfo', 'w').write(DISCINFO)
self.fs.open('/tmp/.discinfo', 'w').write(DISCINFO2)
import pyanaconda.image
pyanaconda.image.gettext = mock.Mock()
pyanaconda.image.log = mock.Mock()
pyanaconda.image.open = self.fs.open
pyanaconda.image.isys = mock.Mock()
pyanaconda.image._arch = 'i386'
pyanaconda.image.stat = mock.Mock()
pyanaconda.image.stat.ST_SIZE = 0
pyanaconda.image.os = mock.Mock()
pyanaconda.image.os.R_OK = 0
pyanaconda.image.os.stat.return_value = [2048]
pyanaconda.image.os.listdir.return_value=[IMAGENAME]
def tearDown(self):
self.tearDownModules()
def get_media_id_1_test(self):
import pyanaconda.image
ret = pyanaconda.image.getMediaId('/mnt/install/cdimage')
self.assertEqual(ret, '1273712438.740122')
def get_media_id_2_test(self):
import pyanaconda.image
pyanaconda.image.os.access = mock.Mock(return_value=False)
ret = pyanaconda.image.getMediaId('/')
self.assertEqual(ret, None)
def mount_directory_1_test(self):
import pyanaconda.image
pyanaconda.image.os.path.ismount = mock.Mock(return_value=False)
pyanaconda.image.mountDirectory('hd:/dev/sda1:/', mock.Mock())
self.assertEqual(pyanaconda.image.isys.method_calls,
[('mount', ('/dev/sda1', '/mnt/install/isodir'), {'fstype': 'auto', 'options':''})])
def mount_directory_2_test(self):
import pyanaconda.image
pyanaconda.image.os.path.ismount = mock.Mock(return_value=False)
pyanaconda.image.mountDirectory('hd:sda1:/', mock.Mock())
self.assertEqual(pyanaconda.image.isys.method_calls,
[('mount', ('/dev/sda1', '/mnt/install/isodir'), {'fstype': 'auto', 'options':''})])
def mount_directory_3_test(self):
import pyanaconda.image
pyanaconda.image.os.path.ismount = mock.Mock(return_value=True)
pyanaconda.image.mountDirectory('hd:sda1:/', mock.Mock())
self.assertEqual(pyanaconda.image.isys.method_calls, [])
def mount_image_1_test(self):
import pyanaconda.image
self.assertRaises(SystemError, pyanaconda.image.mountImage, '', '/mnt/install/cdimage', mock.Mock())
def mount_image_2_test(self):
import pyanaconda.image
pyanaconda.image.os.path.ismount = mock.Mock(return_value=False)
ret = pyanaconda.image.mountImage('', '/mnt/install/cdimage', mock.Mock())
self.assertEqual(pyanaconda.image.isys.method_calls,
[('isIsoImage', ('/Fedora-13-i386-DVD.iso',), {}),
('mount', ('/Fedora-13-i386-DVD.iso', '/mnt/install/cdimage'),
{'readOnly': True, 'fstype': 'iso9660'}),
('umount', ('/mnt/install/cdimage',), {'removeDir': False}),
('mount', ('/Fedora-13-i386-DVD.iso', '/mnt/install/cdimage'),
{'readOnly': True, 'fstype': 'iso9660'}),
])
def scan_for_media_1_test(self):
import pyanaconda.image
storage = mock.Mock()
storage.devicetree.devices = []
ret = pyanaconda.image.scanForMedia(mock.Mock(), storage)
self.assertEqual(ret, None)
def scan_for_media_2_test(self):
import pyanaconda.image
device = mock.Mock()
device.type = 'cdrom'
device.name = 'deviceName'
storage = mock.Mock()
storage.devicetree.devices = [device]
ret = pyanaconda.image.scanForMedia('/tmp', storage)
self.assertEqual(ret, 'deviceName')
self.assertEqual(device.method_calls,
[('format.mount', (), {'mountpoint': '/tmp'})])
def umount_image_1_test(self):
import pyanaconda.image
pyanaconda.image.umountImage('/tmp')
self.assertEqual(pyanaconda.image.isys.method_calls,
[('umount', ('/tmp',), {'removeDir': False})])
def unmount_cd_1_test(self):
import pyanaconda.image
window = mock.Mock()
pyanaconda.image.unmountCD(None, window)
self.assertEqual(window.method_calls, [])
def unmount_cd_2_test(self):
import pyanaconda.image
window = mock.Mock()
device = mock.Mock()
pyanaconda.image.unmountCD(device, window)
self.assertEqual(window.method_calls, [])
self.assertEqual(device.method_calls,
[('format.unmount', (), {})])
def verify_media_1_test(self):
import pyanaconda.image
ret = pyanaconda.image.verifyMedia('/tmp')
self.assertTrue(ret)
def verify_media_2_test(self):
import pyanaconda.image
ret = pyanaconda.image.verifyMedia('/tmp', timestamp=1337)
self.assertFalse(ret)
def verify_media_3_test(self):
import pyanaconda.image
pyanaconda.image._arch = 'x86_64'
ret = pyanaconda.image.verifyMedia('/tmp', 1)
self.assertFalse(ret)

@ -1,49 +0,0 @@
import mock
class IndexedDictTest(mock.TestCase):
def setUp(self):
self.setupModules(['_isys'])
def tearDown(self):
self.tearDownModules()
def instantiation_test(self):
from pyanaconda.indexed_dict import IndexedDict
d = IndexedDict()
self.assertIsInstance(d, IndexedDict)
def append_test(self):
from pyanaconda.indexed_dict import IndexedDict
d = IndexedDict()
stored_data = [1, 2, 3]
d["some_step"] = stored_data
self.assertIs(d["some_step"], stored_data)
def cant_append_test(self):
from pyanaconda.indexed_dict import IndexedDict
def assign_int(indexed_dict):
indexed_dict[3] = [1, 2, 3]
d = IndexedDict()
self.assertRaises(TypeError, d.__setitem__, 3, [1, 2, 3])
def referencing_test(self):
from pyanaconda.indexed_dict import IndexedDict
d = IndexedDict()
d["first"] = 10
d["second"] = 20
d["third"] = 30
self.assertEqual(d[0], 10)
self.assertEqual(d[1], 20)
self.assertEqual(d[2], 30)
self.assertRaises(IndexError, d.__getitem__, 3)
def index_test(self):
from pyanaconda.indexed_dict import IndexedDict
d = IndexedDict()
d["first"] = 10
d["second"] = 20
d["third"] = 30
self.assertEqual(d.index("first"), 0)
self.assertEqual(d.index("second"), 1)
self.assertEqual(d.index("third"), 2)

@ -1,37 +0,0 @@
import mock
import sys
class IutilTest(mock.TestCase):
def setUp(self):
self.setupModules(
['_isys', 'logging', 'pyanaconda.anaconda_log', 'block'])
import pyanaconda
pyanaconda.anaconda_log = mock.Mock()
def tearDown(self):
self.tearDownModules()
def copy_to_sysimage_test(self):
from pyanaconda import iutil
fs = mock.DiskIO()
self.take_over_io(fs, iutil)
self.assertEqual(iutil.copy_to_sysimage("/etc/securetty"), False)
fs["/etc/securetty"] = "tty1"
iutil.os.makedirs = mock.Mock()
iutil.shutil.copy = mock.Mock()
self.assertEqual(iutil.copy_to_sysimage("/etc/securetty"), True)
iutil.os.makedirs.assert_called_with("/mnt/sysimage/etc")
iutil.shutil.copy.assert_called_with("/etc/securetty",
"/mnt/sysimage/etc/securetty")
def testExecCaptureNonZeroFatal (self):
import iutil
try:
argv = ["-c", "import sys; sys.exit(3);"]
iutil.execWithCapture(sys.executable, argv, root=None, fatal=True)
except RuntimeError, ex:
self.assertIn("return code: 3", str(ex))
else:
self.fail("RuntimeError not raised")

@ -1,389 +0,0 @@
#!/usr/bin/python
import mock
import os
class NetworkTest(mock.TestCase):
def setUp(self):
self.setupModules(['_isys', 'block', 'logging', 'ConfigParser'])
self.fs = mock.DiskIO()
self.OK = 22
self.SYSCONFIGDIR = "/tmp/etc/sysconfig"
self.NETSCRIPTSDIR = "%s/network-scripts" % (self.SYSCONFIGDIR)
self.NETWORKCONFFILE = '%s/network' % self.SYSCONFIGDIR
self.IFCFGLOG = '/tmp/ifcfg.log'
self.DEFAULT_HOSTNAME = 'localhost.localdomain'
self.CONT = "DEVICE=eth0\nHWADDR=00:11:22:50:55:50\nTYPE=Ethernet\nBOOTPROTO=dhcp\n"
self.DEVICE = 'eth0'
self.DEV_FILE = self.NETSCRIPTSDIR + '/ifcfg-eth0'
self.DEV_KEY_FILE = self.NETSCRIPTSDIR + '/keys-eth0'
self.fs.open(self.DEV_FILE, 'w').write(self.CONT)
import pyanaconda
pyanaconda.anaconda_log = mock.Mock()
import pyanaconda.network
pyanaconda.network.socket = mock.Mock()
pyanaconda.network.socket.gethostname.return_value = self.DEFAULT_HOSTNAME
pyanaconda.network.open = self.fs.open
pyanaconda.simpleconfig.open = self.fs.open
pyanaconda.network.sysconfigDir = self.SYSCONFIGDIR
pyanaconda.network.netscriptsDir = self.NETSCRIPTSDIR
pyanaconda.network.networkConfFile = self.NETWORKCONFFILE
pyanaconda.network.ifcfgLogFile = self.IFCFGLOG
self.fs.open(self.IFCFGLOG, 'w')
# Network mock
pyanaconda.network.Network.update = mock.Mock()
self.setNMControlledDevices_backup = pyanaconda.network.Network.setNMControlledDevices
pyanaconda.network.Network.setNMControlledDevices = mock.Mock()
pyanaconda.network.Network.netdevices = {}
def tearDown(self):
self.tearDownModules()
def sanity_check_hostname_1_test(self):
import pyanaconda.network
(valid, err) = pyanaconda.network.sanityCheckHostname('desktop')
self.assertTrue(valid)
def sanity_check_hostname_2_test(self):
import pyanaconda.network
(valid, err) = pyanaconda.network.sanityCheckHostname('')
self.assertFalse(valid)
def sanity_check_hostname_3_test(self):
import pyanaconda.network
(valid, err) = pyanaconda.network.sanityCheckHostname('c'*256)
self.assertFalse(valid)
def sanity_check_hostname_4_test(self):
import pyanaconda.network
(valid, err) = pyanaconda.network.sanityCheckHostname('_asf')
self.assertFalse(valid)
def sanity_check_hostname_5_test(self):
import pyanaconda.network
(valid, err) = pyanaconda.network.sanityCheckHostname('a?f')
self.assertFalse(valid)
def get_default_hostname_1_test(self):
import pyanaconda.network
HOSTNAME = 'host1'
pyanaconda.network.getActiveNetDevs = mock.Mock(return_value=['dev'])
pyanaconda.network.isys = mock.Mock()
pyanaconda.network.isys.getIPAddresses.return_value = ['10.0.0.1']
pyanaconda.network.socket = mock.Mock()
pyanaconda.network.socket.gethostbyaddr.return_value = [HOSTNAME, '', '']
ret = pyanaconda.network.getDefaultHostname(mock.Mock())
self.assertEqual(ret, HOSTNAME)
def get_default_hostname_2_test(self):
import pyanaconda.network
HOSTNAME = 'host2'
pyanaconda.network.getActiveNetDevs = mock.Mock(return_value=[])
pyanaconda.network.isys = mock.Mock()
pyanaconda.network.socket = mock.Mock()
anaconda = mock.Mock()
anaconda.network.hostname = HOSTNAME
ret = pyanaconda.network.getDefaultHostname(anaconda)
self.assertEqual(ret, HOSTNAME)
def get_default_hostname_3_test(self):
import pyanaconda.network
HOSTNAME = 'host3'
pyanaconda.network.getActiveNetDevs = mock.Mock(return_value=[])
pyanaconda.network.isys = mock.Mock()
pyanaconda.network.socket = mock.Mock()
pyanaconda.network.socket.gethostname.return_value = HOSTNAME
anaconda = mock.Mock()
anaconda.network.hostname = ''
ret = pyanaconda.network.getDefaultHostname(anaconda)
self.assertEqual(ret, HOSTNAME)
def get_default_hostname_4_test(self):
import pyanaconda.network
pyanaconda.network.getActiveNetDevs = mock.Mock(return_value=[])
pyanaconda.network.isys = mock.Mock()
pyanaconda.network.socket = mock.Mock()
pyanaconda.network.socket.gethostname.return_value = ''
anaconda = mock.Mock()
anaconda.network.hostname = ''
ret = pyanaconda.network.getDefaultHostname(anaconda)
self.assertEqual(ret, self.DEFAULT_HOSTNAME)
def sanity_check_ip_string_1_test(self):
import pyanaconda.network
IPADDR = '10.0.0.5'
pyanaconda.network.sanityCheckIPString(IPADDR)
def sanity_check_ip_string_2_test(self):
import pyanaconda.network
IPADDR = "ff06:0:0:0:0:0:0:c3"
pyanaconda.network.sanityCheckIPString(IPADDR)
def sanity_check_ip_string_3_test(self):
import pyanaconda.network
IPADDR = "ff06:.:.:0:0:0:0:c3"
self.assertRaises(pyanaconda.network.IPError,
pyanaconda.network.sanityCheckIPString, IPADDR)
def sanity_check_ip_string_4_test(self):
import pyanaconda.network
import socket
pyanaconda.network.socket.error = socket.error
pyanaconda.network.socket.inet_pton = mock.Mock(side_effect=socket.error)
IPADDR = "1.8.64.512"
self.assertRaises(pyanaconda.network.IPError,
pyanaconda.network.sanityCheckIPString, IPADDR)
def sanity_check_ip_string_5_test(self):
import pyanaconda.network
import socket
pyanaconda.network.socket.error = socket.error
pyanaconda.network.socket.inet_pton = mock.Mock(side_effect=socket.error)
IPADDR = "top.secret.address"
self.assertRaises(pyanaconda.network.IPError,
pyanaconda.network.sanityCheckIPString, IPADDR)
def has_active_net_dev_1_test(self):
import pyanaconda.network
pyanaconda.network.dbus = mock.Mock()
pyanaconda.network.dbus.Interface().Get.return_value = \
pyanaconda.network.isys.NM_STATE_CONNECTED_GLOBAL
ret = pyanaconda.network.hasActiveNetDev()
self.assertTrue(ret)
self.assertTrue(pyanaconda.network.dbus.Interface().Get.called)
def has_active_net_dev_2_test(self):
import pyanaconda.network
pyanaconda.network.dbus = mock.Mock(side_effect=Exception)
ret = pyanaconda.network.hasActiveNetDev()
self.assertFalse(ret)
def has_active_net_dev_3_test(self):
import pyanaconda.network
pyanaconda.network.dbus = mock.Mock()
pyanaconda.network.dbus.Interface().Get.return_value = self.OK
pyanaconda.network.isys.NM_STATE_CONNECTED = (self.OK - 5)
ret = pyanaconda.network.hasActiveNetDev()
self.assertFalse(ret)
def networkdevice_read_test(self):
import pyanaconda.network
nd = pyanaconda.network.NetworkDevice(self.NETSCRIPTSDIR, self.DEVICE)
ret = nd.read()
self.assertEqual(ret, 4)
self.assertEqual(nd.info,
{'DEVICE': 'eth0', 'HWADDR': '00:11:22:50:55:50',
'BOOTPROTO': 'dhcp', 'TYPE': 'Ethernet'})
def networkdevice_clear_test(self):
import pyanaconda.network
nd = pyanaconda.network.NetworkDevice(self.NETSCRIPTSDIR, self.DEVICE)
nd.info = {'DEVICE': 'eth0', 'HWADDR': '00:11:22:50:55:50', 'TYPE': 'Ethernet'}
nd.clear()
self.assertEqual(nd.info, {})
def networkdevice_str_test(self):
import pyanaconda.network
pyanaconda.network.arch = mock.Mock()
pyanaconda.network.arch.isS390.return_value = False
nd = pyanaconda.network.NetworkDevice(self.NETSCRIPTSDIR, self.DEVICE)
nd.info = {'HWADDR': '00:11:22:50:55:50', 'DEVICE': 'eth0', 'TYPE': 'Ethernet'}
self.assertIn('DEVICE="eth0"', str(nd))
self.assertIn('TYPE="Ethernet"', str(nd))
def networkdevice_load_ifcfg_file_test(self):
import pyanaconda.network
nd = pyanaconda.network.NetworkDevice(self.NETSCRIPTSDIR, self.DEVICE)
nd.loadIfcfgFile()
self.assertFalse(nd._dirty)
self.assertEqual(nd.info,
{'DEVICE': 'eth0', 'HWADDR': '00:11:22:50:55:50',
'TYPE': 'Ethernet', 'BOOTPROTO': 'dhcp'})
def networkdevice_write_ifcfg_file_test(self):
import pyanaconda.network
nd = pyanaconda.network.NetworkDevice(self.NETSCRIPTSDIR, self.DEVICE)
nd.info = {'HWADDR': '66:55:44:33:22:11', 'DEVICE': 'eth1', 'TYPE': 'Ethernet'}
nd._dirty = True
nd.writeIfcfgFile()
self.assertIn('DEVICE="eth1"\n', self.fs[self.DEV_FILE])
self.assertIn('HWADDR="66:55:44:33:22:11"', self.fs[self.DEV_FILE])
self.assertIn('TYPE="Ethernet"', self.fs[self.DEV_FILE])
def networkdevice_set_1_test(self):
import pyanaconda.network
nd = pyanaconda.network.NetworkDevice(self.NETSCRIPTSDIR, self.DEVICE)
nd.set(('key', 'value'))
self.assertEqual(nd.info, {'KEY': 'value'})
self.assertTrue(nd._dirty)
def networkdevice_set_2_test(self):
import pyanaconda.network
nd = pyanaconda.network.NetworkDevice(self.NETSCRIPTSDIR, self.DEVICE)
nd.set(('key', 'value'))
nd.set(('key', 'other_value'))
self.assertEqual(nd.info, {'KEY': 'other_value'})
self.assertTrue(nd._dirty)
def networkdevice_set_3_test(self):
import pyanaconda.network
nd = pyanaconda.network.NetworkDevice(self.NETSCRIPTSDIR, self.DEVICE)
nd.set(('key', 'value'))
nd._dirty = False
nd.set(('key', 'other_value'))
self.assertEqual(nd.info, {'KEY': 'other_value'})
self.assertTrue(nd._dirty)
def networkdevice_set_gateway_test(self):
import pyanaconda.network
nd = pyanaconda.network.NetworkDevice(self.NETSCRIPTSDIR, self.DEVICE)
nd.setGateway('10.0.0.1')
self.assertEqual(nd.info, {'GATEWAY': '10.0.0.1'})
self.assertTrue(nd._dirty)
def networkdevice_set_gateway_ipv6_test(self):
import pyanaconda.network
nd = pyanaconda.network.NetworkDevice(self.NETSCRIPTSDIR, self.DEVICE)
nd.setGateway('fe80::5675:d0ff:feac:4d3f')
self.assertEqual(nd.info, {'IPV6_DEFAULTGW': 'fe80::5675:d0ff:feac:4d3f'})
self.assertTrue(nd._dirty)
def networkdevice_set_dns_test(self):
import pyanaconda.network
nd = pyanaconda.network.NetworkDevice(self.NETSCRIPTSDIR, self.DEVICE)
nd.setDNS('10.0.0.1, 10.0.0.2')
self.assertEqual(nd.info, {'DNS1': '10.0.0.1'})
self.assertEqual(nd.info, {'DNS2': '10.0.0.2'})
self.assertTrue(nd._dirty)
def networkdevice_keyfile_path_test(self):
import pyanaconda.network
nd = pyanaconda.network.NetworkDevice(self.NETSCRIPTSDIR, self.DEVICE)
ret = nd.keyfilePath
self.assertEqual(ret, self.DEV_KEY_FILE)
def networkdevice_write_wepkey_file_1_test(self):
import pyanaconda.network
nd = pyanaconda.network.NetworkDevice(self.NETSCRIPTSDIR, self.DEVICE)
nd.wepkey = False
ret = nd.writeWepkeyFile()
self.assertFalse(ret)
def networkdevice_write_wepkey_file_2_test(self):
import pyanaconda.network
TMP_FILE = '/tmp/wep.key'
TMP_DIR = '/tmp/wepkeyfiles'
pyanaconda.network.tempfile = mock.Mock()
pyanaconda.network.tempfile.mkstemp.return_value = (88, TMP_FILE)
pyanaconda.network.os = mock.Mock()
pyanaconda.network.os.path = os.path
pyanaconda.network.shutil = mock.Mock()
nd = pyanaconda.network.NetworkDevice(self.NETSCRIPTSDIR, self.DEVICE)
nd.iface = self.DEVICE
nd.wepkey = '12345'
nd.writeWepkeyFile(dir=TMP_DIR)
self.assertEqual(pyanaconda.network.os.write.call_args[0], (88, 'KEY1=12345\n'))
self.assertEqual(pyanaconda.network.shutil.move.call_args[0],
(TMP_FILE, '%s/keys-%s' % (TMP_DIR, self.DEVICE)))
def network_nm_controlled_devices_1_test(self):
import pyanaconda.network
nw = pyanaconda.network.Network()
nw.netdevices = {'dev': mock.Mock()}
pyanaconda.network.Network.setNMControlledDevices = self.setNMControlledDevices_backup
nw.setNMControlledDevices()
self.assertEqual(nw.netdevices['dev'].method_calls,
[('set', (('NM_CONTROLLED', 'yes'),), {})])
def network_nm_controlled_devices_2_test(self):
import pyanaconda.network
nw = pyanaconda.network.Network()
nw.netdevices = {'dev': mock.Mock()}
pyanaconda.network.Network.setNMControlledDevices = self.setNMControlledDevices_backup
nw.setNMControlledDevices([''])
self.assertEqual(nw.netdevices['dev'].method_calls,
[('set', (('NM_CONTROLLED', 'no'),), {})])
def network_write_ks_test(self):
import pyanaconda.network
TMPFILE = '/tmp/networkKS'
f = self.fs.open(TMPFILE, 'w')
nw = pyanaconda.network.Network()
nw.netdevices[self.DEVICE] = pyanaconda.network.NetworkDevice(
self.NETSCRIPTSDIR, self.DEVICE)
nw.netdevices[self.DEVICE].loadIfcfgFile()
nw.writeKS(f)
f.close()
self.assertEqual(self.fs[TMPFILE],
'network --device eth0 --bootproto dhcp --noipv6\n')
def network_wait_for_connection_1_test(self):
import pyanaconda.network
pyanaconda.network.dbus = mock.Mock()
pyanaconda.network.dbus.Interface().Get.return_value = \
pyanaconda.network.isys.NM_STATE_CONNECTED_GLOBAL
ret = pyanaconda.network.waitForConnection()
self.assertTrue(ret)
def network_wait_for_connection_2_test(self):
import pyanaconda.network
pyanaconda.network.dbus = mock.Mock()
pyanaconda.network.dbus.Interface().Get.return_value = self.OK-5
pyanaconda.network.isys = mock.Mock()
pyanaconda.network.isys.NM_STATE_CONNECTED = self.OK
pyanaconda.network.time.sleep = mock.Mock()
ret = pyanaconda.network.waitForConnection()
self.assertFalse(ret)
def network_bring_up_test(self):
import pyanaconda.network
pyanaconda.network.Network.write = mock.Mock()
pyanaconda.network.waitForConnection = mock.Mock()
nw = pyanaconda.network.Network()
nw.bringUp()
self.assertTrue(pyanaconda.network.Network.write.called)
self.assertTrue(pyanaconda.network.waitForConnection.called)
def iface_for_host_ip_test(self):
import pyanaconda.network
pyanaconda.network.arch = mock.Mock()
pyanaconda.network.arch.execWithCapture.return_value = \
"10.0.0.2 dev eth0 src 10.0.0.1"
ret = pyanaconda.network.ifaceForHostIP('10.0.0.2')
self.assertEqual(ret, 'eth0')

@ -1,25 +0,0 @@
#!/usr/bin/python
import mock
class PackagesTest(mock.TestCase):
def setUp(self):
self.setupModules(["_isys", "block", "logging", "parted", "storage",
"pyanaconda.storage.formats", "ConfigParser",
"pyanaconda.storage.storage_log"])
self.fs = mock.DiskIO()
import pyanaconda
pyanaconda.anaconda_log = mock.Mock()
import pyanaconda.packages
def tearDown(self):
self.tearDownModules()
def do_post_action_test(self):
import pyanaconda.packages
anaconda = mock.Mock()
pyanaconda.packages.doPostAction(anaconda)
self.assertTrue(anaconda.instClass.postAction.called)

@ -1,126 +0,0 @@
#!/usr/bin/python
import mock
class PackagingTest(mock.TestCase):
def setUp(self):
self.setupModules(["_isys", "logging"])
import pyanaconda
pyanaconda.anaconda_log = mock.Mock()
from pykickstart.version import makeVersion
from pyanaconda.flags import flags
# set some things specially since we're just testing
flags.testing = True
# set up ksdata
self.ksdata = makeVersion()
from pyanaconda.packaging import Payload
self.payload = Payload(self.ksdata)
def tearDown(self):
self.tearDownModules()
#os.system("rm -rf %s" % self.root)
def payload_abstract_test(self):
self.assertRaises(NotImplementedError, self.payload.setup, None)
self.assertRaises(NotImplementedError, self.payload.description, None)
def payload_repo_test(self):
# ksdata repo list initially empty
self.assertEqual(self.payload.data.repo.dataList(), [])
# create and add a new ksdata repo
repo_name = "test1"
repo = self.ksdata.RepoData(name=repo_name, baseurl="http://localhost/")
self.payload.addRepo(repo)
# verify the repo was added
self.assertEqual(self.payload.data.repo.dataList(), [repo])
self.assertEqual(self.payload.getAddOnRepo(repo_name), repo)
# remove the repo
self.payload.removeRepo(repo_name)
# verify the repo was removed
self.assertEqual(self.payload.getAddOnRepo(repo_name), None)
def payload_group_test(self):
import pykickstart.constants
from pykickstart.parser import Group
# verify that ksdata group lists are initially empty
self.assertEqual(self.payload.data.packages.groupList, [])
self.assertEqual(self.payload.data.packages.excludedGroupList, [])
self.payload.deselectGroup("core")
self.assertEqual(self.payload.groupSelected("core"), False)
# select a group and verify the selection is reflected afterward
self.payload.selectGroup("core", optional=True)
self.assertTrue(self.payload.groupSelected("core"))
# verify the group is not in the excluded group list
self.assertTrue(Group("core") not in self.payload.data.packages.excludedGroupList)
# verify the include (optional/all) is recorded
groups = self.payload.data.packages.groupList
group = groups[[g.name for g in groups].index("core")]
self.assertEqual(group.include, pykickstart.constants.GROUP_ALL)
# select more groups
self.payload.selectGroup("base")
self.payload.selectGroup("development", default=False)
# verify include types for newly selected groups
group = groups[[g.name for g in groups].index("development")]
self.assertEqual(group.include, pykickstart.constants.GROUP_REQUIRED)
group = groups[[g.name for g in groups].index("base")]
self.assertEqual(group.include, pykickstart.constants.GROUP_DEFAULT)
# deselect a group and verify the set of groups is correct afterward
self.payload.deselectGroup("base")
self.assertFalse(self.payload.groupSelected("base"))
self.assertTrue(self.payload.groupSelected("core"))
self.assertTrue(self.payload.groupSelected("development"))
def payload_package_test(self):
# verify that ksdata package lists are initially empty
self.assertEqual(self.payload.data.packages.packageList, [])
self.assertEqual(self.payload.data.packages.excludedList, [])
name = "vim-common"
# deselect a package
self.payload.deselectPackage(name)
self.assertEqual(self.payload.packageSelected(name), False)
# select the same package and verify it
self.payload.selectPackage(name)
self.assertEqual(self.payload.packageSelected(name), True)
self.assertTrue(name in self.payload.data.packages.packageList)
self.assertFalse(name in self.payload.data.packages.excludedList)
# select some other packages
self.payload.selectPackage("bash")
self.payload.selectPackage("gnote")
# deselect one of them and then verify the selection state of them all
self.payload.deselectPackage("bash")
self.assertFalse(self.payload.packageSelected("bash"))
self.assertTrue(self.payload.packageSelected("gnote"))
self.assertTrue(self.payload.packageSelected(name))
def payload_get_release_version_test(self):
# Given no URL, _getReleaseVersion should be able to get a releasever
# from pyanaconda.constants.productVersion. This trickery is due to the
# fact that pyanaconda/packaging/__init__.py will have already imported
# productVersion from pyanaconda.constants.
import pyanaconda.packaging
pyanaconda.packaging.productVersion = "17-Beta"
self.assertEqual(self.payload._getReleaseVersion(None), "17")

@ -1,480 +0,0 @@
#!/usr/bin/python
import mock
class PartIntfHelpersTest(mock.TestCase):
def setUp(self):
self.setupModules(["_isys", "block", 'parted', 'storage',
'pyanaconda.storage.formats', 'logging',
'ConfigParser', 'pyanaconda.storage.storage_log'])
self.fs = mock.DiskIO()
import pyanaconda
pyanaconda.anaconda_log = mock.Mock()
import pyanaconda.partIntfHelpers
def tearDown(self):
self.tearDownModules()
# sanityCheckVolumeGroupName tests
def sanitycheckvolumegroupname_right_hostname_1_test(self):
import pyanaconda.partIntfHelpers
HOSTNAME = "hostname"
ret = pyanaconda.partIntfHelpers.sanityCheckVolumeGroupName(HOSTNAME)
self.assertEqual(ret, None)
def sanitycheckvolumegroupname_right_hostname_2_test(self):
import pyanaconda.partIntfHelpers
HOSTNAME = "h"
ret = pyanaconda.partIntfHelpers.sanityCheckVolumeGroupName(HOSTNAME)
self.assertEqual(ret, None)
def sanitycheckvolumegroupname_right_hostname_3_test(self):
import pyanaconda.partIntfHelpers
HOSTNAME = "a" * 127
ret = pyanaconda.partIntfHelpers.sanityCheckVolumeGroupName(HOSTNAME)
self.assertEqual(ret, None)
def sanitycheckvolumegroupname_right_hostname_4_test(self):
import pyanaconda.partIntfHelpers
HOSTNAME = "h-o_s-t.name"
ret = pyanaconda.partIntfHelpers.sanityCheckVolumeGroupName(HOSTNAME)
self.assertEqual(ret, None)
def sanitycheckvolumegroupname_empty_hostname_test(self):
import pyanaconda.partIntfHelpers
HOSTNAME = ""
ret = pyanaconda.partIntfHelpers.sanityCheckVolumeGroupName(HOSTNAME)
self.assertNotEqual(ret, None)
def sanitycheckvolumegroupname_long_hostname_test(self):
import pyanaconda.partIntfHelpers
HOSTNAME = "asdfasdfas" * 13
ret = pyanaconda.partIntfHelpers.sanityCheckVolumeGroupName(HOSTNAME)
self.assertNotEqual(ret, None)
def sanitycheckvolumegroupname_bad_hostname_1_test(self):
import pyanaconda.partIntfHelpers
HOSTNAME = 'lvm'
ret = pyanaconda.partIntfHelpers.sanityCheckVolumeGroupName(HOSTNAME)
self.assertNotEqual(ret, None)
def sanitycheckvolumegroupname_bad_hostname_2_test(self):
import pyanaconda.partIntfHelpers
HOSTNAME = 'root'
ret = pyanaconda.partIntfHelpers.sanityCheckVolumeGroupName(HOSTNAME)
self.assertNotEqual(ret, None)
def sanitycheckvolumegroupname_bad_hostname_3_test(self):
import pyanaconda.partIntfHelpers
HOSTNAME = '.'
ret = pyanaconda.partIntfHelpers.sanityCheckVolumeGroupName(HOSTNAME)
self.assertNotEqual(ret, None)
def sanitycheckvolumegroupname_bad_hostname_4_test(self):
import pyanaconda.partIntfHelpers
HOSTNAME = '..'
ret = pyanaconda.partIntfHelpers.sanityCheckVolumeGroupName(HOSTNAME)
self.assertNotEqual(ret, None)
def sanitycheckvolumegroupname_bad_hostname_5_test(self):
import pyanaconda.partIntfHelpers
HOSTNAME = 'foo bar'
ret = pyanaconda.partIntfHelpers.sanityCheckVolumeGroupName(HOSTNAME)
self.assertNotEqual(ret, None)
def sanitycheckvolumegroupname_bad_hostname_6_test(self):
import pyanaconda.partIntfHelpers
HOSTNAME = 'foob@r'
ret = pyanaconda.partIntfHelpers.sanityCheckVolumeGroupName(HOSTNAME)
self.assertNotEqual(ret, None)
# sanityCheckLogicalVolumeName test
def sanitychecklogicalvolumename_right_name_1_test(self):
import pyanaconda.partIntfHelpers
LOGVOLNAME = "name"
ret = pyanaconda.partIntfHelpers.sanityCheckLogicalVolumeName(LOGVOLNAME)
self.assertEqual(ret, None)
def sanitychecklogicalvolumename_right_name_2_test(self):
import pyanaconda.partIntfHelpers
LOGVOLNAME = "name_00.9"
ret = pyanaconda.partIntfHelpers.sanityCheckLogicalVolumeName(LOGVOLNAME)
self.assertEqual(ret, None)
def sanitychecklogicalvolumename_right_name_3_test(self):
import pyanaconda.partIntfHelpers
LOGVOLNAME = "a"
ret = pyanaconda.partIntfHelpers.sanityCheckLogicalVolumeName(LOGVOLNAME)
self.assertEqual(ret, None)
def sanitychecklogicalvolumename_empty_name_test(self):
import pyanaconda.partIntfHelpers
LOGVOLNAME = ""
ret = pyanaconda.partIntfHelpers.sanityCheckLogicalVolumeName(LOGVOLNAME)
self.assertNotEqual(ret, None)
def sanitychecklogicalvolumename_long_name_test(self):
import pyanaconda.partIntfHelpers
LOGVOLNAME = "b" * 129
ret = pyanaconda.partIntfHelpers.sanityCheckLogicalVolumeName(LOGVOLNAME)
self.assertNotEqual(ret, None)
def sanitychecklogicalvolumename_bad_name_1_test(self):
import pyanaconda.partIntfHelpers
LOGVOLNAME = "group"
ret = pyanaconda.partIntfHelpers.sanityCheckLogicalVolumeName(LOGVOLNAME)
self.assertNotEqual(ret, None)
def sanitychecklogicalvolumename_bad_name_2_test(self):
import pyanaconda.partIntfHelpers
LOGVOLNAME = "."
ret = pyanaconda.partIntfHelpers.sanityCheckLogicalVolumeName(LOGVOLNAME)
self.assertNotEqual(ret, None)
def sanitychecklogicalvolumename_bad_name_3_test(self):
import pyanaconda.partIntfHelpers
LOGVOLNAME = ".."
ret = pyanaconda.partIntfHelpers.sanityCheckLogicalVolumeName(LOGVOLNAME)
self.assertNotEqual(ret, None)
def sanitychecklogicalvolumename_bad_name_4_test(self):
import pyanaconda.partIntfHelpers
LOGVOLNAME = 'foo bar'
ret = pyanaconda.partIntfHelpers.sanityCheckLogicalVolumeName(LOGVOLNAME)
self.assertNotEqual(ret, None)
def sanitychecklogicalvolumename_bad_name_5_test(self):
import pyanaconda.partIntfHelpers
LOGVOLNAME = 'foob@r'
ret = pyanaconda.partIntfHelpers.sanityCheckLogicalVolumeName(LOGVOLNAME)
self.assertNotEqual(ret, None)
# sanityCheckMountPoint test
def sanitycheckmountpoint_right_name_1_test(self):
import pyanaconda.partIntfHelpers
MNTPT = '/foob@r'
ret = pyanaconda.partIntfHelpers.sanityCheckMountPoint(MNTPT)
self.assertEqual(ret, None)
def sanitycheckmountpoint_right_name_2_test(self):
import pyanaconda.partIntfHelpers
MNTPT = '/var'
ret = pyanaconda.partIntfHelpers.sanityCheckMountPoint(MNTPT)
self.assertEqual(ret, None)
def sanitycheckmountpoint_right_name_3_test(self):
import pyanaconda.partIntfHelpers
MNTPT = '/'
ret = pyanaconda.partIntfHelpers.sanityCheckMountPoint(MNTPT)
self.assertEqual(ret, None)
def sanitycheckmountpoint_bad_name_1_test(self):
import pyanaconda.partIntfHelpers
MNTPT = '//'
ret = pyanaconda.partIntfHelpers.sanityCheckMountPoint(MNTPT)
self.assertNotEqual(ret, None)
def sanitycheckmountpoint_bad_name_2_test(self):
import pyanaconda.partIntfHelpers
MNTPT = '/foo bar'
ret = pyanaconda.partIntfHelpers.sanityCheckMountPoint(MNTPT)
self.assertNotEqual(ret, None)
def sanitycheckmountpoint_bad_name_3_test(self):
import pyanaconda.partIntfHelpers
MNTPT = '/./'
ret = pyanaconda.partIntfHelpers.sanityCheckMountPoint(MNTPT)
self.assertNotEqual(ret, None)
def sanitycheckmountpoint_bad_name_4_test(self):
import pyanaconda.partIntfHelpers
MNTPT = '/../'
ret = pyanaconda.partIntfHelpers.sanityCheckMountPoint(MNTPT)
self.assertNotEqual(ret, None)
def sanitycheckmountpoint_bad_name_5_test(self):
import pyanaconda.partIntfHelpers
MNTPT = '/..'
ret = pyanaconda.partIntfHelpers.sanityCheckMountPoint(MNTPT)
self.assertNotEqual(ret, None)
def sanitycheckmountpoint_bad_name_6_test(self):
import pyanaconda.partIntfHelpers
MNTPT = '/.'
ret = pyanaconda.partIntfHelpers.sanityCheckMountPoint(MNTPT)
self.assertNotEqual(ret, None)
def dodeletedevice_1_test(self):
import pyanaconda.partIntfHelpers
INTF = mock.Mock()
STORAGE = mock.Mock()
DEVICE = None
ret = pyanaconda.partIntfHelpers.doDeleteDevice(INTF, STORAGE, DEVICE)
self.assertFalse(ret)
def dodeletedevice_2_test(self):
import pyanaconda.partIntfHelpers
INTF = mock.Mock()
STORAGE = mock.Mock()
STORAGE.deviceImmutable.return_value = True
DEVICE = mock.Mock()
ret = pyanaconda.partIntfHelpers.doDeleteDevice(INTF, STORAGE, DEVICE)
self.assertFalse(ret)
def dodeletedevice_3_test(self):
import pyanaconda.partIntfHelpers
pyanaconda.partIntfHelpers.confirmDelete = mock.Mock(return_value=False)
INTF = mock.Mock()
STORAGE = mock.Mock()
STORAGE.deviceImmutable.return_value = False
DEVICE = mock.Mock()
ret = pyanaconda.partIntfHelpers.doDeleteDevice(INTF, STORAGE, DEVICE)
self.assertFalse(ret)
def dodeletedevice_4_test(self):
import pyanaconda.partIntfHelpers
pyanaconda.partIntfHelpers.confirmDelete = mock.Mock(return_value=False)
INTF = mock.Mock()
STORAGE = mock.Mock()
STORAGE.deviceImmutable.return_value = False
STORAGE.deviceDeps.return_value = []
DEVICE = mock.Mock()
ret = pyanaconda.partIntfHelpers.doDeleteDevice(INTF, STORAGE, DEVICE,
confirm=0)
self.assertTrue(ret)
self.assertTrue(STORAGE.destroyDevice.called)
def dodeletedevice_5_test(self):
import pyanaconda.partIntfHelpers
pyanaconda.partIntfHelpers.confirmDelete = mock.Mock(return_value=True)
INTF = mock.Mock()
STORAGE = mock.Mock()
STORAGE.deviceImmutable.return_value = False
STORAGE.deviceDeps.return_value = []
DEVICE = mock.Mock()
ret = pyanaconda.partIntfHelpers.doDeleteDevice(INTF, STORAGE, DEVICE)
self.assertTrue(ret)
self.assertTrue(STORAGE.destroyDevice.called)
def doclearpartitioneddevice_1_test(self):
import pyanaconda.partIntfHelpers
INTF = mock.Mock()
INTF.messageWindow.return_value = 0
STORAGE = mock.Mock()
DEVICE = mock.Mock()
ret = pyanaconda.partIntfHelpers.doClearPartitionedDevice(INTF, STORAGE,
DEVICE)
self.assertFalse(ret)
def doclearpartitioneddevice_2_test(self):
import pyanaconda.partIntfHelpers
INTF = mock.Mock()
INTF.messageWindow.return_value = 1
STORAGE = mock.Mock()
STORAGE.partitions = []
DEVICE = mock.Mock()
ret = pyanaconda.partIntfHelpers.doClearPartitionedDevice(INTF, STORAGE,
DEVICE)
self.assertFalse(ret)
def doclearpartitioneddevice_3_test(self):
import pyanaconda.partIntfHelpers
INTF = mock.Mock()
INTF.messageWindow.return_value = 1
DEVICE = mock.Mock()
p = mock.Mock()
p.disk = DEVICE
p.partedPartition.number = 0
STORAGE = mock.Mock()
STORAGE.partitions = [p]
STORAGE.deviceImmutable.return_value = False
STORAGE.deviceDeps.return_value = []
ret = pyanaconda.partIntfHelpers.doClearPartitionedDevice(INTF, STORAGE,
DEVICE)
self.assertTrue(ret)
def checkforswapnomatch_test(self):
import pyanaconda.partIntfHelpers
pyanaconda.partIntfHelpers.parted.PARTITION_SWAP = 5
device = mock.Mock()
device.exists.return_value = True
device.getFlag.return_value = True
device.format.type == "swap"
ANACONDA = mock.Mock()
ANACONDA.storage.partitions = [device]
ANACONDA.intf.messageWindow.return_value = 1
pyanaconda.partIntfHelpers.checkForSwapNoMatch(ANACONDA)
self.assertTrue(ANACONDA.storage.formatDevice.called)
def musthaveselecteddrive_test(self):
import pyanaconda.partIntfHelpers
INTF = mock.Mock()
pyanaconda.partIntfHelpers.mustHaveSelectedDrive(INTF)
self.assertTrue(INTF.messageWindow.called)
def querynoformatpreexisting_test(self):
import pyanaconda.partIntfHelpers
RET = 22
INTF = mock.Mock()
ret = INTF.messageWindow.return_value = RET
self.assertEqual(RET, ret)
def partitionsanityerrors_1_test(self):
import pyanaconda.partIntfHelpers
INTF = mock.Mock()
ERRORS = []
ret = pyanaconda.partIntfHelpers.partitionSanityErrors(INTF, ERRORS)
self.assertEqual(1, ret)
def partitionsanityerrors_2_test(self):
import pyanaconda.partIntfHelpers
RET = 5
INTF = mock.Mock()
INTF.messageWindow.return_value = RET
ERRORS = ['err string', 'foo string']
ret = pyanaconda.partIntfHelpers.partitionSanityErrors(INTF, ERRORS)
self.assertEqual(RET, ret)
self.assertTrue(ERRORS[0] in INTF.messageWindow.call_args[0][1])
self.assertTrue(ERRORS[1] in INTF.messageWindow.call_args[0][1])
def partitionsanitywarnings_1_test(self):
import pyanaconda.partIntfHelpers
INTF = mock.Mock()
WARNINGS = []
ret = pyanaconda.partIntfHelpers.partitionSanityWarnings(INTF, WARNINGS)
self.assertEqual(1, ret)
def partitionsanitywarnings_2_test(self):
import pyanaconda.partIntfHelpers
RET = 5
INTF = mock.Mock()
INTF.messageWindow.return_value = RET
WARNINGS = ['warning string', 'foo string']
ret = pyanaconda.partIntfHelpers.partitionSanityWarnings(INTF, WARNINGS)
self.assertEqual(RET, ret)
self.assertTrue(WARNINGS[0] in INTF.messageWindow.call_args[0][1])
self.assertTrue(WARNINGS[1] in INTF.messageWindow.call_args[0][1])
def partitionpreexistformatwarnings_1_test(self):
import pyanaconda.partIntfHelpers
INTF = mock.Mock()
WARNINGS = []
ret = pyanaconda.partIntfHelpers.partitionPreExistFormatWarnings(INTF, WARNINGS)
self.assertEqual(1, ret)
def partitionpreexistformatwarnings_2_test(self):
import pyanaconda.partIntfHelpers
RET = 10
INTF = mock.Mock()
INTF.messageWindow.return_value = RET
WARNINGS = [('foo', 'foobar', '/foodir')]
ret = pyanaconda.partIntfHelpers.partitionPreExistFormatWarnings(INTF, WARNINGS)
self.assertEqual(RET, ret)
self.assertTrue(WARNINGS[0][0] in INTF.messageWindow.call_args[0][1])
def getpreexistformatwarnings_1_test(self):
import pyanaconda.partIntfHelpers
STORAGE = mock.Mock()
STORAGE.devicetree.devices = []
ret = pyanaconda.partIntfHelpers.getPreExistFormatWarnings(STORAGE)
self.assertEqual([], ret)
def getpreexistformatwarnings_2_test(self):
import pyanaconda.partIntfHelpers
STORAGE = mock.Mock()
device = mock.Mock()
device.exists = True
device.name = 'foodev'
device.path = '/foodevdir'
device.format.name = 'fffoodev'
device.format.mountpoint = '/mnt/foo'
device.format.exists = False
device.format.hidden = False
STORAGE.devicetree.devices = [device]
ret = pyanaconda.partIntfHelpers.getPreExistFormatWarnings(STORAGE)
self.assertEqual([('/foodevdir', 'fffoodev', '/mnt/foo')], ret)
def confirmdelete_1_test(self):
import pyanaconda.partIntfHelpers
INTF = mock.Mock()
DEVICE = False
ret = pyanaconda.partIntfHelpers.confirmDelete(INTF, DEVICE)
self.assertEqual(None, ret)
def confirmdelete_2_test(self):
import pyanaconda.partIntfHelpers
RET = 51
INTF = mock.Mock()
INTF.messageWindow.return_value = RET
DEVICE = mock.Mock()
DEVICE.type = "lvmvg"
DEVICE.name = "devname"
ret = pyanaconda.partIntfHelpers.confirmDelete(INTF, DEVICE)
self.assertEqual(RET, ret)
self.assertTrue(DEVICE.name in INTF.messageWindow.call_args[0][1])
def confirmdelete_3_test(self):
import pyanaconda.partIntfHelpers
RET = 52
INTF = mock.Mock()
INTF.messageWindow.return_value = RET
DEVICE = mock.Mock()
DEVICE.type = "lvmlv"
DEVICE.name = "devname"
ret = pyanaconda.partIntfHelpers.confirmDelete(INTF, DEVICE)
self.assertEqual(RET, ret)
self.assertTrue(DEVICE.name in INTF.messageWindow.call_args[0][1])
def confirmdelete_4_test(self):
import pyanaconda.partIntfHelpers
RET = 53
INTF = mock.Mock()
INTF.messageWindow.return_value = RET
DEVICE = mock.Mock()
DEVICE.type = "mdarray"
DEVICE.name = "devname"
ret = pyanaconda.partIntfHelpers.confirmDelete(INTF, DEVICE)
self.assertEqual(RET, ret)
def confirmdelete_5_test(self):
import pyanaconda.partIntfHelpers
RET = 54
INTF = mock.Mock()
INTF.messageWindow.return_value = RET
DEVICE = mock.Mock()
DEVICE.type = "partition"
DEVICE.name = "devname"
DEVICE.path = "/dev/devname"
ret = pyanaconda.partIntfHelpers.confirmDelete(INTF, DEVICE)
self.assertEqual(RET, ret)
self.assertTrue(DEVICE.path in INTF.messageWindow.call_args[0][1])
def confirmdelete_6_test(self):
import pyanaconda.partIntfHelpers
RET = 55
INTF = mock.Mock()
INTF.messageWindow.return_value = RET
DEVICE = mock.Mock()
DEVICE.type = "other"
DEVICE.name = "devname"
ret = pyanaconda.partIntfHelpers.confirmDelete(INTF, DEVICE)
self.assertEqual(RET, ret)
self.assertTrue(DEVICE.type in INTF.messageWindow.call_args[0][1])
self.assertTrue(DEVICE.name in INTF.messageWindow.call_args[0][1])
def confirmresetpartitionstate_test(self):
import pyanaconda.partIntfHelpers
RET = 61
INTF = mock.Mock()
INTF.messageWindow.return_value = RET
ret = pyanaconda.partIntfHelpers.confirmResetPartitionState(INTF)
self.assertEqual(RET, ret)

@ -1,82 +0,0 @@
#!/usr/bin/python
import mock
import sys
import __builtin__
import ConfigParser
class ProductTest(mock.TestCase):
def setUp(self):
self.setupModules(['_isys', 'block', 'os'])
self.fs = mock.DiskIO()
# os module global mock
self.modifiedModule("os")
os = sys.modules['os']
os.access = mock.Mock(return_value=False)
os.uname.return_value = ('', '', '', '', 'i386')
os.environ = {}
# fake /tmp/product/.buildstamp file
self.BUGURL = 'http://bug.url'
self.FINAL = 'false'
self.ARCH = 'i386'
self.NAME = '__anaconda'
self.UUID = '123456.%s' % self.ARCH
self.VERSION = '14'
self.FILENAME = '/tmp/product/.buildstamp'
self.FILE = \
"[Main]\n"\
"BugURL: %s\n"\
"IsFinal: %s\n"\
"Arch: %s\n"\
"Product: %s\n"\
"UUID: %s\n"\
"Version: %s\n" % \
(self.BUGURL, self.FINAL, self.ARCH, self.NAME, self.UUID, self.VERSION)
self.fs.open(self.FILENAME, 'w').write(self.FILE)
# mock builtin open function
self.open = __builtin__.open
__builtin__.open = self.fs.open
if 'pyanaconda.product' in sys.modules:
del(sys.modules["pyanaconda.product"])
def tearDown(self):
__builtin__.open = self.open
self.tearDownModules()
def bug_url_test(self):
sys.modules['os'].access = mock.Mock(return_value=True)
import pyanaconda.product
self.assertEqual(pyanaconda.product.bugUrl, self.BUGURL)
def is_final_test(self):
sys.modules['os'].access = mock.Mock(return_value=True)
import pyanaconda.product
self.assertFalse(pyanaconda.product.isFinal)
def product_arch_test(self):
sys.modules['os'].access = mock.Mock(return_value=True)
import pyanaconda.product
self.assertEqual(pyanaconda.product.productArch, self.ARCH)
def product_name_test(self):
sys.modules['os'].access = mock.Mock(return_value=True)
import pyanaconda.product
self.assertEqual(pyanaconda.product.productName, self.NAME)
def product_stamp_test(self):
sys.modules['os'].access = mock.Mock(return_value=True)
import pyanaconda.product
self.assertEqual(pyanaconda.product.productStamp, self.UUID)
def product_version_test(self):
sys.modules['os'].access = mock.Mock(return_value=True)
import pyanaconda.product
self.assertEqual(pyanaconda.product.productVersion, self.VERSION)

@ -1,297 +0,0 @@
#!/usr/bin/python
import mock
class RescueTest(mock.TestCase):
def setUp(self):
self.setupModules(
['_isys', 'block', 'parted', 'storage', 'pyanaconda.storage.formats',
'logging', 'add_drive_text', 'ConfigParser',
'pyanaconda.storage.storage_log', 'pyanaconda.anaconda_log', 'snack'
])
self.fs = mock.DiskIO()
import pyanaconda
pyanaconda.anaconda_log = mock.Mock()
import snack
snack.SnackScreen = mock.Mock()
import pyanaconda.rescue
pyanaconda.rescue.open = self.fs.open
def tearDown(self):
self.tearDownModules()
#
# RescueInterface class tests
#
def rescueinterface_waitwindow_test(self):
import pyanaconda.rescue
RET = 'foo1'
pyanaconda.rescue.WaitWindow = mock.Mock(return_value=RET)
TITLE = 'title'
TEXT = 'text'
ri = pyanaconda.rescue.RescueInterface()
ret = ri.waitWindow(TITLE, TEXT)
self.assertEqual(ret, RET)
def rescueinterface_progresswindow_test(self):
import pyanaconda.rescue
RET = 'foo2'
pyanaconda.rescue.ProgressWindow = mock.Mock(return_value=RET)
TITLE = 'title'
TEXT = 'text'
TOTAL = 100
ri = pyanaconda.rescue.RescueInterface()
ret = ri.progressWindow(TITLE, TEXT, TOTAL)
self.assertEqual(ret, RET)
def rescueinterface_detailedmessagewindow_test(self):
import pyanaconda.rescue
RET = 'foo3'
pyanaconda.rescue.RescueInterface.messageWindow = mock.Mock(return_value=RET)
TITLE = 'title'
TEXT = 'text'
ri = pyanaconda.rescue.RescueInterface()
ret = ri.detailedMessageWindow(TITLE, TEXT)
self.assertEqual(ret, RET)
def rescueinterface_messagewindow_1_test(self):
import pyanaconda.rescue
pyanaconda.rescue.ButtonChoiceWindow = mock.Mock()
TITLE = 'title'
TEXT = 'text'
TYPE = 'ok'
ri = pyanaconda.rescue.RescueInterface()
ri.detailedMessageWindow(TITLE, TEXT, TYPE)
self.assertTrue(pyanaconda.rescue.ButtonChoiceWindow.called)
def rescueinterface_messagewindow_2_test(self):
import pyanaconda.rescue
RET='yes'
pyanaconda.rescue.ButtonChoiceWindow = mock.Mock(return_value=RET)
TITLE = 'title'
TEXT = 'text'
TYPE = 'yesno'
ri = pyanaconda.rescue.RescueInterface()
ret = ri.messageWindow(TITLE, TEXT, TYPE)
self.assertEqual(ret, 1)
def rescueinterface_messagewindow_3_test(self):
import pyanaconda.rescue
RET = 'barfoo'
pyanaconda.rescue.ButtonChoiceWindow = mock.Mock(return_value=RET)
TITLE = 'title'
TEXT = 'text'
TYPE = 'custom'
CUSTOM_BUTT = ['foo_bar', 'bar_foo']
ri = pyanaconda.rescue.RescueInterface()
ret = ri.messageWindow(TITLE, TEXT, TYPE, custom_buttons=CUSTOM_BUTT)
self.assertEqual(ret, 1)
def rescueinterface_messagewindow_4_test(self):
import pyanaconda.rescue
RET = 'foo4'
pyanaconda.rescue.OkCancelWindow = mock.Mock(return_value=RET)
TITLE = 'title'
TEXT = 'text'
TYPE = 'otherfoo'
ri = pyanaconda.rescue.RescueInterface()
ret = ri.messageWindow(TITLE, TEXT, TYPE)
self.assertEqual(ret, RET)
def rescueinterface_enablenetwork_1_test(self):
import pyanaconda.rescue
anaconda = mock.Mock()
anaconda.network.netdevices = {}
ri = pyanaconda.rescue.RescueInterface()
ret = ri.enableNetwork(anaconda)
self.assertFalse(ret)
def rescueinterface_passphraseentrywindow_test(self):
import pyanaconda.rescue
RET = ('secret', False)
pyanaconda.rescue.PassphraseEntryWindow = mock.Mock()
pyanaconda.rescue.PassphraseEntryWindow().run.return_value = RET
DEVICE = 'dev'
ri = pyanaconda.rescue.RescueInterface()
ret = ri.passphraseEntryWindow(DEVICE)
self.assertEqual(ret, RET)
self.assertTrue(pyanaconda.rescue.PassphraseEntryWindow().pop.called)
def rescueinterface_resetinitializediskquestion_test(self):
import pyanaconda.rescue
ri = pyanaconda.rescue.RescueInterface()
ri._initLabelAnswers = {'foo': 'bar'}
ri.resetInitializeDiskQuestion()
def rescueinterface_resetreinitinconsistentlvmquestion_test(self):
import pyanaconda.rescue
ri = pyanaconda.rescue.RescueInterface()
ri._inconsistentLVMAnswers = {'foo': 'bar'}
ri.resetReinitInconsistentLVMQuestion()
self.assertEqual(ri._inconsistentLVMAnswers, {})
def rescueinterface_questioninitializedisk_test(self):
import pyanaconda.rescue
ri = pyanaconda.rescue.RescueInterface()
ret = ri.questionInitializeDisk('/', '', 0)
self.assertFalse(ret)
def rescueinterface_questionreinitinconsistentlvm_test(self):
import pyanaconda.rescue
ri = pyanaconda.rescue.RescueInterface()
ret = ri.questionReinitInconsistentLVM()
self.assertFalse(ret)
def rescueinterface_questioninitializedasd_test(self):
import pyanaconda.rescue
ri = pyanaconda.rescue.RescueInterface()
ret = ri.questionInitializeDASD('', '')
self.assertEqual(ret, 1)
#
# module function tests
#
def makefstab_test(self):
import pyanaconda.rescue
INSTPATH = '/tmp'
FSTAB = 'rootfs / rootfs rw 0 0'
pyanaconda.rescue.os = mock.Mock()
pyanaconda.rescue.os.access.return_value = True
self.fs.open('/proc/mounts', 'w').write(FSTAB)
self.fs.open('%s/etc/fstab' % INSTPATH, 'w')
ret = pyanaconda.rescue.makeFStab(INSTPATH)
self.assertEqual(self.fs['%s/etc/fstab' % INSTPATH], FSTAB)
def makeresolvconf_1_test(self):
import pyanaconda.rescue
INSTPATH = '/tmp'
RESOLV = "nameserver 10.0.0.1"
pyanaconda.rescue.os = mock.Mock()
pyanaconda.rescue.os.access.return_value = False
pyanaconda.rescue.shutil = mock.Mock()
pyanaconda.rescue.makeResolvConf(INSTPATH)
self.assertFalse(pyanaconda.rescue.shutil.copyfile.called)
def makeresolvconf_2_test(self):
import pyanaconda.rescue
INSTPATH = '/tmp'
RESOLV = "nameserver 10.0.0.1"
pyanaconda.rescue.os = mock.Mock()
pyanaconda.rescue.os.access.return_value = True
pyanaconda.rescue.shutil = mock.Mock()
self.fs.open('%s/etc/resolv.conf' % INSTPATH, 'w').write(RESOLV)
pyanaconda.rescue.makeResolvConf(INSTPATH)
self.assertFalse(pyanaconda.rescue.shutil.copyfile.called)
def makeresolvconf_3_test(self):
import pyanaconda.rescue
INSTPATH = '/tmp'
RESOLV = "nameserver 10.0.0.1"
pyanaconda.rescue.os = mock.Mock()
pyanaconda.rescue.os.access.return_value = True
pyanaconda.rescue.shutil = mock.Mock()
self.fs.open('%s/etc/resolv.conf' % INSTPATH, 'w').write('')
self.fs.open('/etc/resolv.conf', 'w').write('')
pyanaconda.rescue.makeResolvConf(INSTPATH)
self.assertFalse(pyanaconda.rescue.shutil.copyfile.called)
self.assertEqual(self.fs['%s/etc/resolv.conf' % INSTPATH], '')
def makeresolvconf_4_test(self):
import pyanaconda.rescue
INSTPATH = '/tmp'
RESOLV = "nameserver 10.0.0.1"
pyanaconda.rescue.os = mock.Mock()
pyanaconda.rescue.os.access.return_value = True
pyanaconda.rescue.shutil = mock.Mock()
self.fs.open('%s/etc/resolv.conf' % INSTPATH, 'w').write('')
self.fs.open('/etc/resolv.conf', 'w').write(RESOLV)
pyanaconda.rescue.makeResolvConf(INSTPATH)
self.assertTrue(pyanaconda.rescue.shutil.copyfile.called)
self.assertEqual(self.fs['%s/etc/resolv.conf' % INSTPATH],
'nameserver 10.0.0.1')
def startnetworking_test(self):
import pyanaconda.rescue
NETWORK = mock.Mock()
pyanaconda.rescue.os = mock.Mock()
pyanaconda.rescue.startNetworking(NETWORK, '')
self.assertEqual(pyanaconda.rescue.os.system.call_args,
(('/usr/sbin/ifconfig lo 127.0.0.1',), {}))
self.assertTrue(NETWORK.bringUp.called)
def runshell_1_test(self):
import pyanaconda.rescue
import sys
TMPFILE = '/tmp/abc'
MSG = "foo bar"
pyanaconda.rescue.os = mock.Mock()
pyanaconda.rescue.os.path.exists.return_value = True
pyanaconda.rescue.subprocess = mock.Mock()
proc = mock.Mock()
proc.returncode = 0
pyanaconda.rescue.subprocess.Popen.return_value = proc
stdout = sys.stdout
sys.stdout = self.fs.open(TMPFILE, 'w')
pyanaconda.rescue.runShell(msg=MSG)
sys.stdout.close()
sys.stdout = stdout
self.assertTrue(MSG in self.fs[TMPFILE])
self.assertEqual(pyanaconda.rescue.subprocess.Popen.call_args,
((['/usr/bin/firstaidkit-qs'],), {}))
def runshell_2_test(self):
import pyanaconda.rescue
import sys
TMPFILE = '/tmp/abc'
MSG = "foo bar"
def fake_f(filename, _=""):
return filename == "/bin/bash"
pyanaconda.rescue.os = mock.Mock()
pyanaconda.rescue.os.path.exists = fake_f
pyanaconda.rescue.iutil = mock.Mock()
proc = mock.Mock()
proc.returncode = 0
pyanaconda.rescue.subprocess.Popen.return_value = proc
stdout = sys.stdout
sys.stdout = self.fs.open(TMPFILE, 'w')
pyanaconda.rescue.runShell(msg=MSG)
sys.stdout.close()
sys.stdout = stdout
self.assertTrue(MSG in self.fs[TMPFILE])
self.assertTrue(pyanaconda.rescue.iutil.execConsole.called)
def runshell_3_test(self):
import pyanaconda.rescue
import sys
TMPFILE = '/tmp/abc'
SCREEN = mock.Mock()
pyanaconda.rescue.os = mock.Mock()
pyanaconda.rescue.os.path.exists.return_value = True
pyanaconda.rescue.subprocess = mock.Mock()
proc = mock.Mock()
proc.returncode = 0
pyanaconda.rescue.subprocess.Popen.return_value = proc
pyanaconda.rescue.runShell(screen=SCREEN)
self.assertTrue(SCREEN.suspend.called)
self.assertTrue(SCREEN.finish.called)

@ -1,62 +0,0 @@
#!/usr/bin/python
import mock
import sys
class SecurityTest(mock.TestCase):
def setUp(self):
self.setupModules(["_isys", "block", "ConfigParser"])
self.fs = mock.DiskIO()
import pyanaconda.security
pyanaconda.security.log = mock.Mock()
pyanaconda.security.open = self.fs.open
pyanaconda.security.iutil = mock.Mock()
import pyanaconda.flags
pyanaconda.flags.flags.selinux = 1
def tearDown(self):
self.tearDownModules()
def set_get_selinux_test(self):
import pyanaconda.security
states = pyanaconda.security.selinux_states
scrt = pyanaconda.security.Security()
for state in states:
scrt.setSELinux(state)
self.assertEqual(scrt.getSELinux(), state)
def set_get_selinux_bad_sate_test(self):
import pyanaconda.security
states = pyanaconda.security.selinux_states
scrt = pyanaconda.security.Security()
scrt.setSELinux('bad_state')
self.assertTrue(scrt.getSELinux() in states)
def write_test(self):
"""Simulate writing security (simulate executing lokkit and authconfig)"""
import pyanaconda.security
scrt = pyanaconda.security.Security()
pyanaconda.security.ROOT_PATH = "/tmp/security"
scrt.write()
self.assertEqual(pyanaconda.security.iutil.method_calls,
[('execWithRedirect',
('/usr/sbin/lokkit', ['--selinux=enforcing']),
{'root': '/tmp/security', 'stderr': '/dev/null', 'stdout': '/dev/null'}
),
('resetRpmDb', (), {}),
('execWithRedirect',
('/usr/sbin/authconfig',
['--update', '--nostart', '--enableshadow', '--passalgo=sha512']
),
{'root': '/tmp/security', 'stderr': '/dev/tty5', 'stdout': '/dev/tty5'}
)
]
)

@ -1,154 +0,0 @@
#!/usr/bin/python
import mock
import os
import sys
class SimpleconfigTest(mock.TestCase):
def setUp(self):
self.setupModules(["_isys", "block", "ConfigParser"])
import pyanaconda.simpleconfig
# Stuff for IfcfgFile class tests
self.DIR = '/tmp/'
self.IFACE = 'eth0'
self.PATH = "%sifcfg-%s" % (self.DIR, self.IFACE)
self.CONTENT = '# Broadcom Corporation NetXtreme BCM5761 Gigabit Ethernet\n'
self.CONTENT += 'DEVICE=eth0\n'
self.CONTENT += 'HWADDR=00:10:18:61:35:98\n'
self.CONTENT += 'ONBOOT=no\n'
open(self.PATH, 'w').write(self.CONTENT)
def tearDown(self):
self.tearDownModules()
def uppercase_ascii_string_letters_test(self):
"""Converting to uppercase (letters)"""
import pyanaconda.simpleconfig
ret = pyanaconda.simpleconfig.uppercase_ASCII_string('abcd')
self.assertEqual(ret, 'ABCD')
ret = pyanaconda.simpleconfig.uppercase_ASCII_string('aBCD')
self.assertEqual(ret, 'ABCD')
ret = pyanaconda.simpleconfig.uppercase_ASCII_string('ABCD')
self.assertEqual(ret, 'ABCD')
def uppercase_ascii_string_numbers_test(self):
"""Converting to uppercase (numbers)"""
import pyanaconda.simpleconfig
ret = pyanaconda.simpleconfig.uppercase_ASCII_string('123')
self.assertEqual(ret, '123')
def uppercase_ascii_string_others_test(self):
"""Converting to uppercase (special chars)"""
import pyanaconda.simpleconfig
ret = pyanaconda.simpleconfig.uppercase_ASCII_string('--')
self.assertEqual(ret, '--')
ret = pyanaconda.simpleconfig.uppercase_ASCII_string(' ')
self.assertEqual(ret, ' ')
ret = pyanaconda.simpleconfig.uppercase_ASCII_string('')
self.assertEqual(ret, '')
def unquote_test(self):
from pyanaconda.simpleconfig import unquote
self.assertEqual(unquote("plain string"), "plain string")
self.assertEqual(unquote('"double quote"'), "double quote")
self.assertEqual(unquote("'single quote'"), "single quote")
def quote_test(self):
from pyanaconda.simpleconfig import quote
self.assertEqual(quote("nospaces"), "nospaces")
self.assertEqual(quote("plain string"), '"plain string"')
self.assertEqual(quote("alwaysquote", always=True), '"alwaysquote"')
def set_and_get_test(self):
"""Setting and getting values"""
import pyanaconda.simpleconfig
scf = pyanaconda.simpleconfig.SimpleConfigFile()
scf.set(('key1', 'value1'))
self.assertEqual(scf.get('key1'), 'value1')
scf.set(('KEY2', 'value2'))
self.assertEqual(scf.get('key2'), 'value2')
scf.set(('KEY3', 'value3'))
self.assertEqual(scf.get('KEY3'), 'value3')
scf.set(('key4', 'value4'))
self.assertEqual(scf.get('KEY4'), 'value4')
def unset_test(self):
import pyanaconda.simpleconfig
scf = pyanaconda.simpleconfig.SimpleConfigFile()
scf.set(('key1', 'value1'))
scf.unset(('key1'))
self.assertEqual(scf.get('key1'), '')
def write_test(self):
import pyanaconda.simpleconfig
scf = pyanaconda.simpleconfig.SimpleConfigFile()
scf.set(('key1', 'value1'))
scf.write('/tmp/file')
self.assertEqual(open('/tmp/file').read(), 'KEY1=value1\n')
def read_test(self):
import pyanaconda.simpleconfig
scf = pyanaconda.simpleconfig.SimpleConfigFile()
open('/tmp/file', 'w').write('KEY1="value1"\n')
scf.read('/tmp/file')
self.assertEqual(scf.get('key1'), 'value1')
def read_write_test(self):
from pyanaconda.simpleconfig import SimpleConfigFile
scf = SimpleConfigFile()
scf.read(self.PATH)
scf.write("/tmp/file")
self.assertEqual(open("/tmp/file").read(), self.CONTENT)
def write_new_keys_test(self):
from pyanaconda.simpleconfig import SimpleConfigFile
scf = SimpleConfigFile()
scf.read(self.PATH)
scf.set(("key1", "value1"))
scf.write("/tmp/file")
self.assertEqual(open("/tmp/file").read(),
self.CONTENT+"KEY1=value1\n")
def remove_key_test(self):
from pyanaconda.simpleconfig import SimpleConfigFile
scf = SimpleConfigFile()
scf.read(self.PATH)
scf.unset("BOOT")
scf.write("/tmp/file")
scf.reset()
scf.read("/tmp/file")
self.assertEqual(scf.get("BOOT"), "")
def ifcfgfile_path_property_test(self):
import pyanaconda.simpleconfig
scf = pyanaconda.simpleconfig.IfcfgFile(self.DIR, self.IFACE)
self.assertEqual(scf.path, self.PATH)
def ifcfgfile_read_test(self):
import pyanaconda.simpleconfig
scf = pyanaconda.simpleconfig.IfcfgFile(self.DIR, self.IFACE)
scf.read()
self.assertEqual(scf.get('device'), 'eth0')
self.assertEqual(scf.get('hwaddr'), '00:10:18:61:35:98')
self.assertEqual(scf.get('onboot'), 'no')
def ifcfgfile_read_and_clear_test(self):
import pyanaconda.simpleconfig
scf = pyanaconda.simpleconfig.IfcfgFile(self.DIR, self.IFACE)
scf.read()
scf.clear()
self.assertEqual(scf.get('device'), '')
self.assertEqual(scf.get('hwaddr'), '')
self.assertEqual(scf.get('onboot'), '')
def ifcfgfile_write_test(self):
import pyanaconda.simpleconfig
scf = pyanaconda.simpleconfig.IfcfgFile(self.DIR, self.IFACE)
scf.set(('device', 'eth0'))
scf.set(('hwaddr', '00:11:22:33:44:55'))
scf.set(('onboot', 'no'))
scf.write()
self.assertEqual(open(self.PATH).read(),
'DEVICE="eth0"\nHWADDR="00:11:22:33:44:55"\nONBOOT="no"\n')

@ -1,56 +0,0 @@
#!/usr/bin/python
import mock
import sys
ZONE = 'Europe/Prague'
UTC = 2
class TimeZoneTest(mock.TestCase):
def setUp(self):
self.setupModules(["_isys", "block", "ConfigParser"])
self.fs = mock.DiskIO()
import pyanaconda.timezone
pyanaconda.timezone.log = mock.Mock()
pyanaconda.timezone.open = self.fs.open
pyanaconda.timezone.os.access = mock.Mock(return_value = True)
pyanaconda.timezone.shutil.copyfile = mock.Mock()
pyanaconda.timezone.os = mock.Mock()
pyanaconda.timezone.os.access.return_value = True
pyanaconda.timezone.shutil = mock.Mock()
#pyanaconda.timezone.shutil.copyfile = mock.Mock()
def tearDown(self):
self.tearDownModules()
def get_timezone_info_test(self):
import pyanaconda.timezone
tz = pyanaconda.timezone.Timezone()
info = tz.getTimezoneInfo()
self.assertEqual( (tz.tz, tz.utc), info )
def set_timezone_info_test(self):
import pyanaconda.timezone
tz = pyanaconda.timezone.Timezone()
tz.setTimezoneInfo(ZONE, UTC)
self.assertEqual((ZONE, UTC), (tz.tz, tz.utc))
def write_test(self):
import pyanaconda.timezone
tz = pyanaconda.timezone.Timezone()
tz.tz = ZONE
tz.utc = True
PATH = ''
ADJTIME = '0.013782 1279118821 0.000000\n1279118821\nUTC\n'
f = self.fs.open('/mnt/sysimage/etc/adjtime', 'w')
f.write(ADJTIME)
f.close()
tz.write()
self.assertEqual(self.fs['/mnt/sysimage/etc/adjtime'], ADJTIME)

@ -1,142 +0,0 @@
#!/usr/bin/python
import mock
import sys
GIDNUMBER = 'pw_gid'
HOMEDIRECTORY = 'pw_dir'
class UsersTest(mock.TestCase):
def setUp(self):
self.setupModules(["_isys", "block", "ConfigParser"])
self.fs = mock.DiskIO()
self.anaconda = mock.Mock()
self.anaconda.security.auth.find.return_value = -1
import pyanaconda.users
pyanaconda.users.log = mock.Mock()
pyanaconda.users.iutil = mock.Mock()
pyanaconda.users.iutil.mkdirChain = mock.Mock()
pyanaconda.users.os = mock.Mock()
pyanaconda.users.os.fork.return_value=False
pyanaconda.users.os.waitpid.return_value=(1, 1)
pyanaconda.users.os.WEXITSTATUS.return_value=0
pyanaconda.users.libuser.admin = mock.Mock()
pyanaconda.users.libuser.GIDNUMBER = GIDNUMBER
pyanaconda.users.libuser.HOMEDIRECTORY = HOMEDIRECTORY
pyanaconda.users.libuser.admin().lookupGroupByName.return_value = False
pyanaconda.users.libuser.admin().lookupUserByName.return_value = False
pyanaconda.users.libuser.admin().initGroup().get.return_value = ['']
pyanaconda.users.libuser.admin().initGroup().reset_mock()
pyanaconda.users.libuser.admin().reset_mock()
def tearDown(self):
self.tearDownModules()
def create_group_test(self):
import pyanaconda.users
GROUP = 'Group'
GID = 100
group_dict = { "name" : GROUP,
"gid" : GID,
"root" : ""
}
usr = pyanaconda.users.Users(self.anaconda)
self.assertTrue(usr.createGroup(GROUP, **group_dict))
methods = pyanaconda.users.libuser.admin().method_calls[:]
try:
if methods[2][0] == 'addGroup':
methods.pop()
except:
pass
self.assertEqual(methods,
[('lookupGroupByName', (GROUP,), {}), ('initGroup', (GROUP,), {}),])
self.assertEqual(
pyanaconda.users.libuser.admin().initGroup().method_calls,
[('set', (GIDNUMBER, GID), {})])
def create_user_test(self):
import pyanaconda.users
USER = 'TestUser'
PASS = 'abcde'
user_dict = { "name" : USER,
"password" : PASS,
"groups" : [],
"homedir" : "",
"isCrypted" : False,
"shell" : "",
"uid" : None,
"root" : ""
}
usr = pyanaconda.users.Users(self.anaconda)
self.assertTrue(usr.createUser(USER, **user_dict))
self.assertTrue(pyanaconda.users.iutil.mkdirChain.called)
methods = [x[0] for x in pyanaconda.users.libuser.admin().method_calls]
self.assertEqual(methods,
['lookupUserByName', 'initUser', 'initGroup', 'addUser','addGroup',
'setpassUser', 'lookupGroupByName'])
self.assertEqual(pyanaconda.users.libuser.admin().initUser.call_args_list,
[((USER,), {})])
self.assertEqual(pyanaconda.users.libuser.admin().initGroup.call_args_list,
[((USER,), {})])
self.assertEqual(pyanaconda.users.libuser.admin().initUser().method_calls,
[('set', (GIDNUMBER, ['']), {}),
('set', (HOMEDIRECTORY, '/home/%s' % USER), {})]
)
self.assertEqual(pyanaconda.users.libuser.admin().initGroup().method_calls,
[('get', (GIDNUMBER,), {})])
def check_user_exists_test(self):
import pyanaconda.users
USER = 'TestUser'
usr = pyanaconda.users.Users(self.anaconda)
self.assertTrue(usr.checkUserExists(USER, root=''))
self.assertEqual(pyanaconda.users.libuser.admin().method_calls,
[('lookupUserByName', (USER,), {})])
def get_pass_algo_md5_test(self):
import pyanaconda.users
usr = pyanaconda.users.Users(self.anaconda)
self.assertEqual(usr.getPassAlgo(), None)
def set_user_password_test(self):
import pyanaconda.users
USER = 'TestUser'
PASS = 'abcde'
CRYPTED = False
LOCK = False
usr = pyanaconda.users.Users(self.anaconda)
usr.setUserPassword(USER, PASS, CRYPTED, LOCK)
methods = [x[0] for x in pyanaconda.users.libuser.admin().method_calls]
self.assertEqual(methods,
['lookupUserByName', 'setpassUser', 'modifyUser'])
def set_root_password_test(self):
import pyanaconda.users
usr = pyanaconda.users.Users(self.anaconda)
usr.setRootPassword()
methods = [x[0] for x in pyanaconda.users.libuser.admin().method_calls]
self.assertEqual(methods,
['lookupUserByName', 'setpassUser', 'modifyUser'])

@ -1,116 +0,0 @@
#!/usr/bin/python
import mock
import os
class VncTest(mock.TestCase):
def setUp(self):
self.setupModules(["_isys", "block", "logging", "ConfigParser"])
self.fs = mock.DiskIO()
self.anaconda = mock.Mock()
self.anaconda.ksdata.vnc.password = ''
import pyanaconda
pyanaconda.anaconda_log = mock.Mock()
self.OK = 22
import pyanaconda.vnc
pyanaconda.vnc.log = mock.Mock()
pyanaconda.vnc.os = mock.Mock()
pyanaconda.vnc.subprocess = mock.Mock()
pyanaconda.vnc.subprocess.Popen().communicate.return_value = (1, 2)
pyanaconda.vnc.subprocess.Popen().returncode = self.OK
pyanaconda.vnc.open = self.fs.open
self.ROOT = '/'
self.DISPLAY = '2'
self.DESKTOP = 'Desktop'
self.PASS = ''
self.LOG_FILE = '/tmp/vnc.log'
self.PW_FILE = '/tmp/vncpassword'
self.VNCCONNECTHOST = 'host'
def tearDown(self):
self.tearDownModules()
def set_vnc_password_1_test(self):
import pyanaconda.vnc
server = pyanaconda.vnc.VncServer()
server.anaconda = self.anaconda
pyanaconda.vnc.iutil = mock.Mock()
pyanaconda.vnc.os.pipe.return_value = (1, 2)
server.setVNCPassword()
self.assertEqual(
pyanaconda.vnc.iutil.execWithRedirect.call_args_list,
[(('vncpasswd', ['-f']), {'stdin': 1, 'stdout': '/tmp/vncpassword'})])
def initialize_test(self):
import pyanaconda.vnc
IP = '192.168.0.21'
HOSTNAME = 'desktop'
dev = mock.Mock()
dev.get.return_value = 'eth0'
pyanaconda.vnc.network = mock.Mock()
pyanaconda.vnc.network.Network().netdevices = [dev]
pyanaconda.vnc.network.getActiveNetDevs.return_value = [0]
pyanaconda.vnc.network.getDefaultHostname.return_value = HOSTNAME
pyanaconda.vnc.isys = mock.Mock()
pyanaconda.vnc.isys.getIPAddresses = mock.Mock(return_value=[IP])
server = pyanaconda.vnc.VncServer(display=self.DISPLAY)
server.initialize()
expected = "%s:%s (%s)" % (HOSTNAME, self.DISPLAY, IP)
self.assertEqual(server.connxinfo, expected)
def openlogfile_test(self):
import pyanaconda.vnc
FILE = 'file'
pyanaconda.vnc.os.O_RDWR = os.O_RDWR
pyanaconda.vnc.os.O_CREAT = os.O_CREAT
pyanaconda.vnc.os.open.return_value = FILE
server = pyanaconda.vnc.VncServer(log_file=self.LOG_FILE)
ret = server.openlogfile()
self.assertEqual(ret, FILE)
self.assertEqual(pyanaconda.vnc.os.open.call_args,
((self.LOG_FILE, os.O_RDWR | os.O_CREAT), {})
)
def connect_to_view_test(self):
import pyanaconda.vnc
pyanaconda.vnc.subprocess.Popen().communicate.return_value = (self.OK, '')
server = pyanaconda.vnc.VncServer(vncconnecthost=self.VNCCONNECTHOST)
ret = server.connectToView()
self.assertTrue(ret)
params = pyanaconda.vnc.subprocess.Popen.call_args[0][0]
self.assertTrue(self.VNCCONNECTHOST in params)
self.assertTrue(params[params.index(self.VNCCONNECTHOST)-1] == "-connect")
def start_server_test(self):
import pyanaconda.vnc
pyanaconda.vnc.VncServer.initialize = mock.Mock()
pyanaconda.vnc.VncServer.setVNCPassword = mock.Mock()
pyanaconda.vnc.VncServer.VNCListen = mock.Mock()
pyanaconda.vnc.subprocess.Popen().poll.return_value = None
pyanaconda.vnc.os.environ = {}
pyanaconda.vnc.time.sleep = mock.Mock()
server = pyanaconda.vnc.VncServer(root=self.ROOT, display=self.DISPLAY,
desktop=self.DESKTOP, password=self.PASS, vncconnecthost="")
server.openlogfile = mock.Mock()
server.startServer()
params = pyanaconda.vnc.subprocess.Popen.call_args[0][0]
self.assertTrue('desktop=%s'%self.DESKTOP in params)
self.assertTrue(':%s'%self.DISPLAY in params)
self.assertTrue(pyanaconda.vnc.VncServer.VNCListen.called)
self.assertTrue("DISPLAY" in pyanaconda.vnc.os.environ)
self.assertEqual(pyanaconda.vnc.os.environ['DISPLAY'], ":%s" % self.DISPLAY)

@ -23,10 +23,6 @@ pyanaconda/text.py
pyanaconda/users.py
pyanaconda/vnc.py
# Install class definitions
pyanaconda/installclasses/fedora.py
pyanaconda/installclasses/rhel.py
# Packaging module source files
pyanaconda/packaging/__init__.py
pyanaconda/packaging/livepayload.py
@ -131,6 +127,7 @@ pyanaconda/ui/gui/spokes/custom.glade
pyanaconda/ui/gui/spokes/advstorage/fcoe.glade
pyanaconda/ui/gui/spokes/advstorage/iscsi.glade
pyanaconda/ui/gui/spokes/advstorage/dasd.glade
pyanaconda/ui/gui/spokes/advstorage/zfcp.glade
pyanaconda/ui/gui/spokes/lib/cart.glade
pyanaconda/ui/gui/spokes/lib/custom_storage_helpers.glade
pyanaconda/ui/gui/spokes/lib/dasdfmt.glade

File diff suppressed because it is too large Load Diff

@ -56,9 +56,9 @@ def collect_addon_paths(toplevel_addon_paths, ui_subdir="gui"):
if os.path.isdir(addon_spoke_path):
module_paths["spokes"].append(("%s.%s.spokes.%%s" % (addon_id, ui_subdir), addon_spoke_path))
addon_category_path = os.path.join(path, addon_id, ui_subdir, "categories")
if os.path.isdir(addon_spoke_path):
module_paths["categories"].append(("%s.%s.categories.%%s" % (addon_id, ui_subdir), addon_category_path))
addon_category_path = os.path.join(path, addon_id, "categories")
if os.path.isdir(addon_category_path):
module_paths["categories"].append(("%s.categories.%%s" % addon_id, addon_category_path))
return module_paths
@ -75,17 +75,17 @@ class AddonRegistry(object):
def __str__(self):
return functools.reduce(lambda acc, (id, addon): acc + str(addon),
self.__dict__.iteritems(), "")
self.__dict__.items(), "")
def execute(self, storage, ksdata, instClass, users):
"""This method calls execute on all the registered addons."""
for v in self.__dict__.itervalues():
for v in self.__dict__.values():
if hasattr(v, "execute"):
v.execute(storage, ksdata, instClass, users)
def setup(self, storage, ksdata, instClass):
"""This method calls setup on all the registered addons."""
for v in self.__dict__.itervalues():
for v in self.__dict__.values():
if hasattr(v, "setup"):
v.setup(storage, ksdata, instClass)

@ -188,7 +188,7 @@ class Anaconda(object):
# gather up info on the running threads
threads = "\nThreads\n-------\n"
for thread_id, frame in sys._current_frames().iteritems():
for thread_id, frame in sys._current_frames().items():
threads += "\nThread %s\n" % (thread_id,)
threads += "".join(format_stack(frame))
@ -211,8 +211,11 @@ class Anaconda(object):
if self.displayMode == 'g':
from pyanaconda.ui.gui import GraphicalUserInterface
# Run the GUI in non-fullscreen mode, so live installs can still
# use the window manager
self._intf = GraphicalUserInterface(self.storage, self.payload,
self.instClass, gui_lock=self.gui_initialized)
self.instClass, gui_lock=self.gui_initialized,
fullscreen=False)
# needs to be refreshed now we know if gui or tui will take place
addon_paths = addons.collect_addon_paths(constants.ADDON_PATHS,

@ -161,7 +161,7 @@ class AnacondaArgumentParser(ArgumentParser):
according to the option definitions set by add_argument.
boot_cmdline can be given as a string (to be parsed by BootArgs), or a
dict (or any object with .iteritems()) of {bootarg:value} pairs.
dict (or any object with .items()) of {bootarg:value} pairs.
If boot_cmdline is None, the boot_cmdline data will be whatever BootArgs reads
by default (/proc/cmdline, /run/initramfs/etc/cmdline, /etc/cmdline).
@ -186,7 +186,7 @@ class AnacondaArgumentParser(ArgumentParser):
# go over all options corresponding to current boot cmdline
# and do any modifications necessary
# NOTE: program cmdline overrides boot cmdline
for arg, val in bootargs.iteritems():
for arg, val in bootargs.items():
option = self._get_bootarg_option(arg)
if option is None:
# this boot option is unknown to Anaconda, skip it
@ -349,7 +349,7 @@ class HelpTextParser(object):
with open(self._path) as lines:
for parsed_option, parsed_text in self.read(lines):
self._help_text[parsed_option] = parsed_text
except StandardError:
except Exception: # pylint: disable=broad-except
log.error("error reading help text file %s", self._path)
return self._help_text.get(option, "")

@ -23,7 +23,7 @@
#
import logging
from logging.handlers import SysLogHandler, SYSLOG_UDP_PORT
from logging.handlers import SysLogHandler, SocketHandler, SYSLOG_UDP_PORT
import os
import sys
import types
@ -32,16 +32,13 @@ import warnings
from pyanaconda.flags import flags
from pyanaconda.constants import LOGLVL_LOCK
DEFAULT_TTY_LEVEL = logging.INFO
DEFAULT_LEVEL = logging.INFO
ENTRY_FORMAT = "%(asctime)s,%(msecs)03d %(levelname)s %(name)s: %(message)s"
TTY_FORMAT = "%(levelname)s %(name)s: %(message)s"
STDOUT_FORMAT = "%(asctime)s %(message)s"
DATE_FORMAT = "%H:%M:%S"
MAIN_LOG_FILE = "/tmp/anaconda.log"
MAIN_LOG_TTY = "/dev/tty3"
PROGRAM_LOG_FILE = "/tmp/program.log"
PROGRAM_LOG_TTY = "/dev/tty5"
STORAGE_LOG_FILE = "/tmp/storage.log"
PACKAGING_LOG_FILE = "/tmp/packaging.log"
SENSITIVE_INFO_LOG_FILE = "/tmp/sensitive-info.log"
@ -89,12 +86,16 @@ class AnacondaSyslogHandler(SysLogHandler):
"""Map the priority level to a syslog level """
return self.levelMap.get(level, SysLogHandler.mapPriority(self, level))
class AnacondaSocketHandler(SocketHandler):
def makePickle(self, record):
return self.formatter.format(record) + "\n"
class AnacondaLog:
SYSLOG_CFGFILE = "/etc/rsyslog.conf"
VIRTIO_PORT = "/dev/virtio-ports/org.fedoraproject.anaconda.log.0"
def __init__ (self):
self.tty_loglevel = DEFAULT_TTY_LEVEL
self.loglevel = DEFAULT_LEVEL
self.remote_syslog = None
# Rename the loglevels so they are the same as in syslog.
logging.addLevelName(logging.WARNING, "WARN")
@ -117,22 +118,12 @@ class AnacondaLog:
for logr in [self.anaconda_logger, storage_logger]:
logr.setLevel(logging.DEBUG)
self.forwardToSyslog(logr)
# Logging of basic stuff and storage to tty3.
# XXX Use os.uname here since it's too early to be importing the
# storage module.
if not os.uname()[4].startswith('s390') and os.access(MAIN_LOG_TTY, os.W_OK):
self.addFileHandler(MAIN_LOG_TTY, logr,
fmtStr=TTY_FORMAT,
autoLevel=True)
# External program output log
program_logger = logging.getLogger("program")
program_logger.setLevel(logging.DEBUG)
self.addFileHandler(PROGRAM_LOG_FILE, program_logger,
minLevel=logging.DEBUG)
self.addFileHandler(PROGRAM_LOG_TTY, program_logger,
fmtStr=TTY_FORMAT,
autoLevel=True)
self.forwardToSyslog(program_logger)
# Create the packaging logger.
@ -175,7 +166,7 @@ class AnacondaLog:
fmtStr=STDOUT_FORMAT, minLevel=logging.INFO)
# Add a simple handler - file or stream, depending on what we're given.
def addFileHandler (self, dest, addToLogger, minLevel=DEFAULT_TTY_LEVEL,
def addFileHandler (self, dest, addToLogger, minLevel=DEFAULT_LEVEL,
fmtStr=ENTRY_FORMAT,
autoLevel=False):
try:
@ -216,8 +207,16 @@ class AnacondaLog:
self.anaconda_logger.warning("%s", warnings.formatwarning(
message, category, filename, lineno, line))
def setup_remotelog(self, host, port):
remotelog = AnacondaSocketHandler(host, port)
remotelog.setFormatter(logging.Formatter(ENTRY_FORMAT, DATE_FORMAT))
remotelog.setLevel(logging.DEBUG)
logging.getLogger().addHandler(remotelog)
def restartSyslog(self):
os.system("systemctl restart rsyslog.service")
# Import here instead of at the module level to avoid an import loop
from pyanaconda.iutil import execWithRedirect
execWithRedirect("systemctl", ["restart", "rsyslog.service"])
def updateRemote(self, remote_syslog):
"""Updates the location of remote rsyslogd to forward to.
@ -237,12 +236,13 @@ class AnacondaLog:
"""
TEMPLATE = "*.* %s;anaconda_syslog\n"
if not os.path.exists(self.VIRTIO_PORT) \
or not os.access(self.VIRTIO_PORT, os.W_OK):
vport = flags.cmdline.get('virtiolog', self.VIRTIO_PORT)
if not os.access(vport, os.W_OK):
return
with open(self.SYSLOG_CFGFILE, 'a') as cfgfile:
cfgfile.write(TEMPLATE % (self.VIRTIO_PORT,))
cfgfile.write(TEMPLATE % (vport,))
self.restartSyslog()

@ -28,12 +28,13 @@ import shutil
import struct
import blivet
from parted import PARTITION_BIOS_GRUB
from glob import glob
from pyanaconda import iutil
from blivet.devicelibs import raid
from pyanaconda.isys import sync
from pyanaconda.product import productName
from pyanaconda.flags import flags
from pyanaconda.flags import flags, can_touch_runtime_system
from blivet.errors import StorageError
from blivet.fcoe import fcoe
import pyanaconda.network
@ -245,7 +246,7 @@ class BootLoader(object):
stage2_mountpoints = ["/boot", "/"]
stage2_bootable = False
stage2_must_be_primary = True
stage2_description = N_("/boot filesystem")
stage2_description = N_("/boot file system")
stage2_max_end = Size("2 TiB")
@property
@ -779,7 +780,7 @@ class BootLoader(object):
@update_only.setter
def update_only(self, value):
if value and not self.can_update:
raise ValueError("this bootloader does not support updates")
raise ValueError("this boot loader does not support updates")
elif self.can_update:
self._update_only = value
@ -818,6 +819,14 @@ class BootLoader(object):
if usr_device:
dracut_devices.extend([usr_device])
netdevs = storage.devicetree.getDevicesByInstance(NetworkStorageDevice)
rootdev = storage.rootDevice
if any(rootdev.dependsOn(netdev) for netdev in netdevs):
dracut_devices = set(dracut_devices)
for dev in storage.mountpoints.values():
if any(dev.dependsOn(netdev) for netdev in netdevs):
dracut_devices.add(dev)
done = []
for device in dracut_devices:
for dep in storage.devices:
@ -867,6 +876,11 @@ class BootLoader(object):
continue
self.boot_args.add("ifname=%s:%s" % (nic, hwaddr.lower()))
# Add iscsi_firmware to trigger dracut running iscsistart
# See rhbz#1099603 and rhbz#1185792
if len(glob("/sys/firmware/iscsi_boot*")) > 0:
self.boot_args.add("iscsi_firmware")
#
# preservation of most of our boot args
#
@ -927,7 +941,7 @@ class BootLoader(object):
def write_config(self):
""" Write the bootloader configuration. """
if not self.config_file:
raise BootLoaderError("no config file defined for this bootloader")
raise BootLoaderError("no config file defined for this boot loader")
config_path = os.path.normpath(iutil.getSysroot() + self.config_file)
if os.access(config_path, os.R_OK):
@ -1096,12 +1110,13 @@ class GRUB(BootLoader):
if not self.password:
raise BootLoaderError("cannot encrypt empty password")
import string
# Used for ascii_letters and digits constants
import string # pylint: disable=deprecated-module
import crypt
import random
salt = "$6$"
salt_len = 16
salt_chars = string.letters + string.digits + './'
salt_chars = string.ascii_letters + string.digits + './'
rand_gen = random.SystemRandom()
salt += "".join(rand_gen.choice(salt_chars) for i in range(salt_len))
@ -1272,16 +1287,28 @@ class GRUB(BootLoader):
# add the redundant targets if installing stage1 to a disk that is
# a member of the stage2 array.
# Look for both mdraid and btrfs raid
if self.stage2_device.type == "mdarray" and \
self.stage2_device.level == raid.RAID1 and \
self.stage2_device.level == raid.RAID1:
stage2_raid = True
# Set parents to the list of partitions in the RAID
stage2_parents = self.stage2_device.parents
elif self.stage2_device.type == "btrfs subvolume" and \
self.stage2_device.parents[0].dataLevel == raid.RAID1:
stage2_raid = True
# Set parents to the list of partitions in the parent volume
stage2_parents = self.stage2_device.parents[0].parents
else:
stage2_raid = False
if stage2_raid and \
self.stage1_device.isDisk and \
self.stage2_device.dependsOn(self.stage1_device):
for stage2dev in self.stage2_device.parents:
for stage2dev in stage2_parents:
# if target disk contains any of /boot array's member
# partitions, set up stage1 on each member's disk
# and stage2 on each member partition
stage1dev = stage2dev.disk
targets.append((stage1dev, stage2dev))
targets.append((stage1dev, self.stage2_device))
else:
targets.append((self.stage1_device, self.stage2_device))
@ -1290,7 +1317,7 @@ class GRUB(BootLoader):
def install(self, args=None):
rc = iutil.execInSysroot("grub-install", ["--just-copy"])
if rc:
raise BootLoaderError("bootloader install failed")
raise BootLoaderError("boot loader install failed")
for (stage1dev, stage2dev) in self.install_targets:
cmd = ("root %(stage2dev)s\n"
@ -1311,7 +1338,7 @@ class GRUB(BootLoader):
rc = iutil.execInSysroot("grub", args, stdin=pread)
iutil.eintr_retry_call(os.close, pread)
if rc:
raise BootLoaderError("bootloader install failed")
raise BootLoaderError("boot loader install failed")
def update(self):
self.install()
@ -1342,14 +1369,14 @@ class GRUB(BootLoader):
self.stage2_device.level == raid.RAID1 and \
self.stage1_device.type != "mdarray":
if not self.stage1_device.isDisk:
msg = _("bootloader stage2 device %(stage2dev)s is on a multi-disk array, but bootloader stage1 device %(stage1dev)s is not. " \
msg = _("boot loader stage2 device %(stage2dev)s is on a multi-disk array, but boot loader stage1 device %(stage1dev)s is not. " \
"A drive failure in %(stage2dev)s could render the system unbootable.") % \
{"stage1dev" : self.stage1_device.name,
"stage2dev" : self.stage2_device.name}
self.warnings.append(msg)
elif not self.stage2_device.dependsOn(self.stage1_device):
msg = _("bootloader stage2 device %(stage2dev)s is on a multi-disk array, but bootloader stage1 device %(stage1dev)s is not part of this array. " \
"The stage1 bootloader will only be installed to a single drive.") % \
msg = _("boot loader stage2 device %(stage2dev)s is on a multi-disk array, but boot loader stage1 device %(stage1dev)s is not part of this array. " \
"The stage1 boot loader will only be installed to a single drive.") % \
{"stage1dev" : self.stage1_device.name,
"stage2dev" : self.stage2_device.name}
self.warnings.append(msg)
@ -1373,7 +1400,7 @@ class GRUB2(GRUB):
- BIOS boot partition (GPT)
- parted /dev/sda set <partition_number> bios_grub on
- can't contain a filesystem
- can't contain a file system
- 31KiB min, 1MiB recommended
"""
@ -1381,15 +1408,11 @@ class GRUB2(GRUB):
packages = ["grub2"]
_config_file = "grub.cfg"
_config_dir = "grub2"
config_file_mode = 0o600
defaults_file = "/etc/default/grub"
can_dual_boot = True
can_update = True
terminal_type = "gfxterm"
# requirements for boot devices
stage2_device_types = ["partition", "mdarray", "lvmlv", "btrfs volume",
"btrfs subvolume"]
stage2_device_types = ["partition", "mdarray", "lvmlv"]
stage2_raid_levels = [raid.RAID0, raid.RAID1, raid.RAID4,
raid.RAID5, raid.RAID6, raid.RAID10]
stage2_raid_metadata = ["0", "0.90", "1.0", "1.2"]
@ -1511,7 +1534,7 @@ class GRUB2(GRUB):
iutil.eintr_retry_call(os.close, pread)
self.encrypted_password = buf.split()[-1].strip()
if not self.encrypted_password.startswith("grub.pbkdf2."):
raise BootLoaderError("failed to encrypt bootloader password")
raise BootLoaderError("failed to encrypt boot loader password")
def write_password_config(self):
if not self.password and not self.encrypted_password:
@ -1544,22 +1567,23 @@ class GRUB2(GRUB):
try:
self.write_password_config()
except (BootLoaderError, OSError, RuntimeError) as e:
log.error("bootloader password setup failed: %s", e)
log.error("boot loader password setup failed: %s", e)
# disable non-xen entries
os.chmod("%s/etc/grub.d/10_linux" % iutil.getSysroot(), 0644)
# make sure the default entry is the OS we are installing
entry_title = "0"
rc = iutil.execInSysroot("grub2-set-default", [entry_title])
if rc:
log.error("failed to set default menu entry to %s", productName)
if self.default is not None:
entry_title = "0"
rc = iutil.execInSysroot("grub2-set-default", [entry_title])
if rc:
log.error("failed to set default menu entry to %s", productName)
# now tell grub2 to generate the main configuration file
rc = iutil.execInSysroot("grub2-mkconfig",
["-o", self.config_file])
if rc:
raise BootLoaderError("failed to write bootloader configuration")
raise BootLoaderError("failed to write boot loader configuration")
#
# installation
@ -1587,7 +1611,7 @@ class GRUB2(GRUB):
root=iutil.getSysroot(),
env_prune=['MALLOC_PERTURB_'])
if rc:
raise BootLoaderError("bootloader install failed")
raise BootLoaderError("boot loader install failed")
def write(self):
""" Write the bootloader configuration and install the bootloader. """
@ -1679,6 +1703,10 @@ class EFIGRUB(GRUB2):
log.info("Skipping efibootmgr for image/directory install.")
return ""
if "noefi" in flags.cmdline:
log.info("Skipping efibootmgr for noefi")
return ""
if kwargs.pop("capture", False):
exec_func = iutil.execWithCapture
else:
@ -1724,7 +1752,7 @@ class EFIGRUB(GRUB2):
rc = self.efibootmgr("-b", slot_id, "-B")
if rc:
raise BootLoaderError("failed to remove old efi boot entry. This is most likely a kernel bug.")
raise BootLoaderError("failed to remove old efi boot entry. This is most likely a kernel or firmware bug.")
@property
def efi_dir_as_efifs_dir(self):
@ -1741,7 +1769,7 @@ class EFIGRUB(GRUB2):
self.efi_dir_as_efifs_dir + self._efi_binary,
root=iutil.getSysroot())
if rc:
raise BootLoaderError("failed to set new efi boot target. This is most likely a kernel bug.")
raise BootLoaderError("failed to set new efi boot target. This is most likely a kernel or firmware bug.")
def add_efi_boot_target(self):
if self.stage1_device.type == "partition":
@ -1780,6 +1808,7 @@ class EFIGRUB(GRUB2):
def check(self):
return True
class XenEFI(EFIGRUB):
packages = ["efibootmgr"]
_config_file = 'xen.cfg'
@ -1852,22 +1881,15 @@ class XenEFI(EFIGRUB):
write_config = BootLoader.write_config
# FIXME: We need to include grubby, and omit the shim package
# on aarch64 until we get all the EFI bits in place.
class Aarch64EFIGRUB(EFIGRUB):
packages = ["grub2-efi", "efibootmgr", "grubby"]
_serial_consoles = ["ttyAMA", "ttyS"]
_efi_binary = "\\grubaa64.efi"
class MacEFIGRUB(EFIGRUB):
def mactel_config(self):
if os.path.exists(iutil.getSysroot() + "/usr/libexec/mactel-boot-setup"):
rc = iutil.execInSysroot("/usr/libexec/mactel-boot-setup", [])
if rc:
log.error("failed to configure Mac bootloader")
log.error("failed to configure Mac boot loader")
def install(self, args=None):
super(MacEFIGRUB, self).install()
@ -2016,7 +2038,7 @@ class Yaboot(YabootBase):
args = ["-f", "-C", self.config_file]
rc = iutil.execInSysroot(self.prog, args)
if rc:
raise BootLoaderError("bootloader installation failed")
raise BootLoaderError("boot loader installation failed")
class IPSeriesYaboot(Yaboot):
@ -2040,6 +2062,8 @@ class IPSeriesYaboot(Yaboot):
super(IPSeriesYaboot, self).install()
def updatePowerPCBootList(self):
if not can_touch_runtime_system("updatePowerPCBootList", touch_live=True):
return
log.debug("updatePowerPCBootList: self.stage1_device.path = %s", self.stage1_device.path)
@ -2065,7 +2089,7 @@ class IPSeriesYaboot(Yaboot):
# Place the disk containing the PReP partition first.
# Remove all other occurances of it.
boot_list = [boot_disk] + filter(lambda x: x != boot_disk, boot_list)
boot_list = [boot_disk] + [x for x in boot_list if x != boot_disk]
log.debug("updatePowerPCBootList: updated boot_list = %s", boot_list)
@ -2100,6 +2124,8 @@ class IPSeriesGRUB2(GRUB2):
# This will update the PowerPC's (ppc) bios boot devive order list
def updateNVRAMBootList(self):
if not can_touch_runtime_system("updateNVRAMBootList", touch_live=True):
return
log.debug("updateNVRAMBootList: self.stage1_device.path = %s", self.stage1_device.path)
@ -2124,7 +2150,7 @@ class IPSeriesGRUB2(GRUB2):
# Place the disk containing the PReP partition first.
# Remove all other occurances of it.
boot_list = [boot_disk] + filter(lambda x: x != boot_disk, boot_list)
boot_list = [boot_disk] + [x for x in boot_list if x != boot_disk]
update_value = "boot-device=%s" % " ".join(boot_list)
@ -2357,7 +2383,7 @@ class EXTLINUX(BootLoader):
rc = iutil.execInSysroot("extlinux", args)
if rc:
raise BootLoaderError("bootloader install failed")
raise BootLoaderError("boot loader install failed")
# every platform that wants a bootloader needs to be in this dict
@ -2450,21 +2476,31 @@ def writeBootLoader(storage, payload, instClass, ksdata):
"""
if not storage.bootloader.skip_bootloader:
stage1_device = storage.bootloader.stage1_device
log.info("bootloader stage1 target device is %s", stage1_device.name)
log.info("boot loader stage1 target device is %s", stage1_device.name)
stage2_device = storage.bootloader.stage2_device
log.info("bootloader stage2 target device is %s", stage2_device.name)
log.info("boot loader stage2 target device is %s", stage2_device.name)
# Bridge storage EFI configuration to bootloader
if hasattr(storage.bootloader, 'efi_dir'):
storage.bootloader.efi_dir = instClass.efi_dir
if isinstance(payload, RPMOSTreePayload):
if storage.bootloader.skip_bootloader:
log.info("skipping bootloader install per user request")
log.info("skipping boot loader install per user request")
return
writeBootLoaderFinal(storage, payload, instClass, ksdata)
return
# get a list of installed kernel packages
kernel_versions = payload.kernelVersionList + payload.rescueKernelList
# add whatever rescue kernels we can find to the end
kernel_versions = list(payload.kernelVersionList)
rescue_versions = glob(iutil.getSysroot() + "/boot/vmlinuz-*-rescue-*")
rescue_versions += glob(iutil.getSysroot() + "/boot/efi/EFI/%s/vmlinuz-*-rescue-*" % instClass.efi_dir)
kernel_versions += (f.split("/")[-1][8:] for f in rescue_versions)
if not kernel_versions:
log.warning("no kernel was installed -- bootloader config unchanged")
log.warning("no kernel was installed -- boot loader config unchanged")
return
# all the linux images' labels are based on the default image's
@ -2480,14 +2516,12 @@ def writeBootLoader(storage, payload, instClass, ksdata):
short=base_short_label)
storage.bootloader.add_image(default_image)
storage.bootloader.default = default_image
if hasattr(storage.bootloader, 'efi_dir'):
storage.bootloader.efi_dir = instClass.efi_dir
# write out /etc/sysconfig/kernel
writeSysconfigKernel(storage, version, instClass)
if storage.bootloader.skip_bootloader:
log.info("skipping bootloader install per user request")
log.info("skipping boot loader install per user request")
return
# now add an image for each of the other kernels

@ -19,7 +19,8 @@
# Author(s): Erik Troan <ewt@redhat.com>
#
import string
# Used for digits, ascii_letters, punctuation constants
import string # pylint: disable=deprecated-module
from pyanaconda.i18n import N_
# Use -1 to indicate that the selinux configuration is unset
@ -51,7 +52,7 @@ DD_RPMS = "/tmp/DD-*"
TRANSLATIONS_UPDATE_DIR="/tmp/updates/po"
ANACONDA_CLEANUP = "anaconda-cleanup"
MOUNT_DIR = "/mnt/install"
MOUNT_DIR = "/run/install"
DRACUT_REPODIR = "/run/install/repo"
DRACUT_ISODIR = "/run/install/source"
ISO_DIR = MOUNT_DIR + "/isodir"
@ -62,7 +63,7 @@ BASE_REPO_NAME = "anaconda"
# NOTE: this should be LANG_TERRITORY.CODESET, e.g. en_US.UTF-8
DEFAULT_LANG = "en_US.UTF-8"
DEFAULT_VC_FONT = "latarcyrheb-sun16"
DEFAULT_VC_FONT = "eurlatgr"
DEFAULT_KEYBOARD = "us"
@ -133,15 +134,16 @@ FIRSTBOOT_ENVIRON = "firstboot"
UNSUPPORTED_HW = 1 << 28
# Password validation
PASSWORD_MIN_LEN = 6
PASSWORD_MIN_LEN = 8
PASSWORD_EMPTY_ERROR = N_("The password is empty.")
PASSWORD_CONFIRM_ERROR_GUI = N_("The passwords do not match.")
PASSWORD_CONFIRM_ERROR_TUI = N_("The passwords you entered were different. Please try again.")
PASSWORD_WEAK = N_("The password you have provided is weak. You will have to press Done twice to confirm it.")
PASSWORD_WEAK_WITH_ERROR = N_("The password you have provided is weak: %s. You will have to press Done twice to confirm it.")
PASSWORD_WEAK = N_("The password you have provided is weak. %s")
PASSWORD_WEAK_WITH_ERROR = N_("The password you have provided is weak: %s. %s")
PASSWORD_WEAK_CONFIRM = N_("You have provided a weak password. Press Done again to use anyway.")
PASSWORD_WEAK_CONFIRM_WITH_ERROR = N_("You have provided a weak password: %s. Press Done again to use anyway.")
PASSWORD_ASCII = N_("The password you have provided contains non-ASCII characters. You may not be able to switch between keyboard layouts to login. Press Done to continue.")
PASSWORD_DONE_TWICE = N_("You will have to press Done twice to confirm it.")
PASSWORD_STRENGTH_DESC = [N_("Empty"), N_("Weak"), N_("Fair"), N_("Good"), N_("Strong")]
@ -159,9 +161,22 @@ CMDLINE_APPEND = ["modprobe.blacklist"]
DEFAULT_AUTOPART_TYPE = AUTOPART_TYPE_LVM
# Default to these units when reading user input when no units given
SIZE_UNITS_DEFAULT = "MiB"
import logging
LOGLVL_LOCK = logging.DEBUG-1
# Constants for reporting status to IPMI. These are from the IPMI spec v2 rev1.1, page 512.
IPMI_STARTED = 0x7 # installation started
IPMI_FINISHED = 0x8 # installation finished successfully
IPMI_ABORTED = 0x9 # installation finished unsuccessfully, due to some non-exn error
IPMI_FAILED = 0xA # installation hit an exception
# for how long (in seconds) we try to wait for enough entropy for LUKS
# keep this a multiple of 60 (minutes)
MAX_ENTROPY_WAIT = 10 * 60
MAX_ENTROPY_WAIT = 10 * 60
# X display number to use
X_DISPLAY_NUMBER = 1

@ -55,6 +55,11 @@ class CmdlineError(Exception):
class RemovedModuleError(ImportError):
pass
class PasswordCryptError(Exception):
def __init__(self, algo):
Exception.__init__(self)
self.algo = algo
class ZIPLError(Exception):
pass
@ -122,20 +127,11 @@ class ErrorHandler(object):
self.ui.showError(message)
return ERROR_RAISE
def _dirtyFSHandler(self, exn):
message = _("The following file systems for your Linux system were "
"not unmounted cleanly. Would you like to mount them "
"anyway?\n%s") % exn.devices
if self.ui.showYesNoQuestion(message):
return ERROR_CONTINUE
else:
return ERROR_RAISE
def _fstabTypeMismatchHandler(self, exn):
# FIXME: include the two types in the message instead of including
# the raw exception text
message = _("There is an entry in your /etc/fstab file that contains "
"an invalid or incorrect filesystem type:\n\n")
"an invalid or incorrect file system type:\n\n")
message += " " + str(exn)
self.ui.showError(message)
@ -245,7 +241,7 @@ class ErrorHandler(object):
return ERROR_RAISE
def _bootLoaderErrorHandler(self, exn):
message = _("The following error occurred while installing the bootloader. "
message = _("The following error occurred while installing the boot loader. "
"The system will not be bootable. "
"Would you like to ignore this and continue with "
"installation?")
@ -256,6 +252,12 @@ class ErrorHandler(object):
else:
return ERROR_RAISE
def _passwordCryptErrorHandler(self, exn):
message = _("Unable to encrypt password: unsupported algorithm %s") % exn.algo
self.ui.showError(message)
return ERROR_RAISE
def _ziplErrorHandler(self, *args, **kwargs):
details = kwargs["exception"]
message = _("Installation was stopped due to an error installing the "
@ -283,7 +285,6 @@ class ErrorHandler(object):
_map = {"PartitioningError": self._partitionErrorHandler,
"FSResizeError": self._fsResizeHandler,
"NoDisksError": self._noDisksHandler,
"DirtyFSError": self._dirtyFSHandler,
"FSTabTypeMismatchError": self._fstabTypeMismatchHandler,
"InvalidImageSizeError": self._invalidImageSizeHandler,
"MissingImageError": self._missingImageHandler,
@ -295,6 +296,7 @@ class ErrorHandler(object):
"PayloadInstallError": self._payloadInstallHandler,
"DependencyError": self._dependencyErrorHandler,
"BootLoaderError": self._bootLoaderErrorHandler,
"PasswordCryptError": self._passwordCryptErrorHandler,
"ZIPLError": self._ziplErrorHandler}
if exn.__class__.__name__ in _map:

@ -28,7 +28,6 @@ from pyanaconda import iutil, kickstart
import sys
import os
import shutil
import signal
import time
import re
import errno
@ -37,7 +36,7 @@ import traceback
import blivet.errors
from pyanaconda.errors import CmdlineError
from pyanaconda.ui.communication import hubQ
from pyanaconda.constants import THREAD_EXCEPTION_HANDLING_TEST
from pyanaconda.constants import THREAD_EXCEPTION_HANDLING_TEST, IPMI_FAILED
from pyanaconda.threads import threadMgr
from pyanaconda.i18n import _
from pyanaconda import flags
@ -50,7 +49,7 @@ log = logging.getLogger("anaconda")
class AnacondaExceptionHandler(ExceptionHandler):
def __init__(self, confObj, intfClass, exnClass, tty_num, gui_lock):
def __init__(self, confObj, intfClass, exnClass, tty_num, gui_lock, interactive):
"""
:see: python-meh's ExceptionHandler
:param tty_num: the number of tty the interface is running on
@ -60,8 +59,9 @@ class AnacondaExceptionHandler(ExceptionHandler):
ExceptionHandler.__init__(self, confObj, intfClass, exnClass)
self._gui_lock = gui_lock
self._intf_tty_num = tty_num
self._interactive = interactive
def run_handleException(self, dump_info):
def _main_loop_handleException(self, dump_info):
"""
Helper method with one argument only so that it can be registered
with GLib.idle_add() to run on idle or called from a handler.
@ -70,8 +70,21 @@ class AnacondaExceptionHandler(ExceptionHandler):
"""
super(AnacondaExceptionHandler, self).handleException(dump_info)
return False
ty = dump_info.exc_info.type
value = dump_info.exc_info.value
if (issubclass(ty, blivet.errors.StorageError) and value.hardware_fault) \
or (issubclass(ty, OSError) and value.errno == errno.EIO):
# hardware fault or '[Errno 5] Input/Output error'
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.messageWindow(_("Hardware error occured"), hw_error_msg)
sys.exit(0)
else:
super(AnacondaExceptionHandler, self).handleException(dump_info)
return False
def handleException(self, dump_info):
"""
@ -90,78 +103,70 @@ class AnacondaExceptionHandler(ExceptionHandler):
ty = dump_info.exc_info.type
value = dump_info.exc_info.value
if (issubclass(ty, blivet.errors.StorageError) and value.hardware_fault) \
or (issubclass(ty, OSError) and value.errno == errno.EIO):
# hardware fault or '[Errno 5] Input/Output error'
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.messageWindow(_("Hardware error occured"), hw_error_msg)
sys.exit(0)
else:
try:
from gi.repository import Gtk
# XXX: Gtk stopped raising RuntimeError if it fails to
# initialize. Horay! But will it stay like this? Let's be
# cautious and raise the exception on our own to work in both
# cases
initialized = Gtk.init_check(None)[0]
if not initialized:
raise RuntimeError()
# Attempt to grab the GUI initializing lock, do not block
if not self._gui_lock.acquire(False):
# the graphical interface is running, don't crash it by
# running another one potentially from a different thread
log.debug("Gtk running, queuing exception handler to the "
"main loop")
GLib.idle_add(self.run_handleException, dump_info)
else:
log.debug("Gtk not running, starting Gtk and running "
"exception handler in it")
super(AnacondaExceptionHandler, self).handleException(
dump_info)
except (RuntimeError, ImportError):
log.debug("Gtk cannot be initialized")
# X not running (Gtk cannot be initialized)
if threadMgr.in_main_thread():
log.debug("In the main thread, running exception handler")
if (issubclass (ty, CmdlineError)):
try:
from gi.repository import Gtk
# XXX: Gtk stopped raising RuntimeError if it fails to
# initialize. Horay! But will it stay like this? Let's be
# cautious and raise the exception on our own to work in both
# cases
initialized = Gtk.init_check(None)[0]
if not initialized:
raise RuntimeError()
# Attempt to grab the GUI initializing lock, do not block
if not self._gui_lock.acquire(False):
# the graphical interface is running, don't crash it by
# running another one potentially from a different thread
log.debug("Gtk running, queuing exception handler to the "
"main loop")
GLib.idle_add(self._main_loop_handleException, dump_info)
else:
log.debug("Gtk not running, starting Gtk and running "
"exception handler in it")
self._main_loop_handleException(dump_info)
except (RuntimeError, ImportError):
log.debug("Gtk cannot be initialized")
# X not running (Gtk cannot be initialized)
if threadMgr.in_main_thread():
log.debug("In the main thread, running exception handler")
if issubclass(ty, CmdlineError) or not self._interactive:
if issubclass(ty, CmdlineError):
cmdline_error_msg = _("\nThe installation was stopped due to "
"incomplete spokes detected while running "
"in non-interactive cmdline mode. Since there "
"can not be any questions in cmdline mode, "
"cannot be any questions in cmdline mode, "
"edit your kickstart file and retry "
"installation.\nThe exact error message is: "
"\n\n%s.\n\nThe installer will now terminate.") % str(value)
# since there is no UI in cmdline mode and it is completely
# non-interactive, we can't show a message window asking the user
# to acknowledge the error; instead, print the error out and sleep
# for a few seconds before exiting the installer
print(cmdline_error_msg)
time.sleep(10)
sys.exit(0)
else:
print("\nAn unknown error has occured, look at the "
"/tmp/anaconda-tb* file(s) for more details")
# in the main thread, run exception handler
super(AnacondaExceptionHandler, self).handleException(
dump_info)
cmdline_error_msg = _("\nRunning in cmdline mode, no interactive debugging "
"allowed.\nThe exact error message is: "
"\n\n%s.\n\nThe installer will now terminate.") % str(value)
# since there is no UI in cmdline mode and it is completely
# non-interactive, we can't show a message window asking the user
# to acknowledge the error; instead, print the error out and sleep
# for a few seconds before exiting the installer
print(cmdline_error_msg)
time.sleep(10)
sys.exit(1)
else:
log.debug("In a non-main thread, sending a message with "
"exception data")
# not in the main thread, just send message with exception
# data and let message handler run the exception handler in
# the main thread
exc_info = dump_info.exc_info
hubQ.send_exception((exc_info.type,
exc_info.value,
exc_info.stack))
print("\nAn unknown error has occured, look at the "
"/tmp/anaconda-tb* file(s) for more details")
# in the main thread, run exception handler
self._main_loop_handleException(dump_info)
else:
log.debug("In a non-main thread, sending a message with "
"exception data")
# not in the main thread, just send message with exception
# data and let message handler run the exception handler in
# the main thread
exc_info = dump_info.exc_info
hubQ.send_exception((exc_info.type,
exc_info.value,
exc_info.stack))
def postWriteHook(self, dump_info):
anaconda = dump_info.object
@ -181,19 +186,13 @@ class AnacondaExceptionHandler(ExceptionHandler):
except:
pass
iutil.ipmi_report(IPMI_FAILED)
def runDebug(self, exc_info):
if flags.can_touch_runtime_system("switch console") \
and self._intf_tty_num != 1:
iutil.vtActivate(1)
pidfl = "/tmp/vncshell.pid"
if os.path.exists(pidfl) and os.path.isfile(pidfl):
pf = open(pidfl, "r")
for pid in pf.readlines():
if not int(pid) == os.getpid():
os.kill(int(pid), signal.SIGKILL)
pf.close()
iutil.eintr_retry_call(os.open, "/dev/console", os.O_RDWR) # reclaim stdin
iutil.eintr_retry_call(os.dup2, 0, 1) # reclaim stdout
iutil.eintr_retry_call(os.dup2, 0, 2) # reclaim stderr
@ -266,9 +265,10 @@ def initExceptionHandling(anaconda):
# anaconda-tb file
conf.register_callback("journalctl", journalctl_callback, attchmnt_only=False)
interactive = not anaconda.displayMode == 'c'
handler = AnacondaExceptionHandler(conf, anaconda.intf.meh_interface,
ReverseExceptionDump, anaconda.intf.tty_num,
anaconda.gui_initialized)
anaconda.gui_initialized, interactive)
handler.install(anaconda)
return conf

@ -65,11 +65,12 @@ class Flags(object):
self.gpt = False
self.leavebootorder = False
self.testing = False
self.dnf = False
self.dnf = True
self.mpathFriendlyNames = True
# ksprompt is whether or not to prompt for missing ksdata
self.ksprompt = True
self.rescue_mode = False
self.noefi = False
# parse the boot commandline
self.cmdline = BootArgs()
# Lock it down: no more creating new flags!
@ -79,7 +80,7 @@ class Flags(object):
def read_cmdline(self):
for f in ("selinux", "debug", "leavebootorder", "testing", "extlinux",
"nombr", "gpt", "dnf"):
"nombr", "gpt", "dnf", "noefi"):
self.set_cmdline_bool(f)
if not selinux.is_selinux_enabled():

@ -99,10 +99,8 @@ in a couple seconds
* cell tower geolocation
"""
import requests
import urllib
import urllib2
import json
import dbus
import threading
import time
@ -525,10 +523,9 @@ class FedoraGeoIPProvider(GeolocationBackend):
def _refresh(self):
try:
reply = urllib2.urlopen(self.API_URL, timeout=
constants.NETWORK_CONNECTION_TIMEOUT)
if reply:
json_reply = json.load(reply)
reply = requests.get(self.API_URL, timeout=constants.NETWORK_CONNECTION_TIMEOUT, verify=True)
if reply.status_code == requests.codes.ok:
json_reply = reply.json()
territory = json_reply.get("country_code", None)
timezone_source = "GeoIP"
timezone_code = json_reply.get("time_zone", None)
@ -549,10 +546,10 @@ class FedoraGeoIPProvider(GeolocationBackend):
territory_code=territory,
timezone=timezone_code,
timezone_source=timezone_source))
except urllib2.HTTPError as e:
log.debug("Geoloc: HTTPError for Fedora GeoIP API lookup:\n%s", e)
except urllib2.URLError as e:
log.debug("Geoloc: URLError for Fedora GeoIP API lookup:\n%s", e)
else:
log.error("Geoloc: Fedora GeoIP API lookup failed with status code: %s", reply.status_code)
except requests.exceptions.RequestException as e:
log.debug("Geoloc: RequestException for Fedora GeoIP API lookup:\n%s", e)
except ValueError as e:
log.debug("Geoloc: Unable to decode GeoIP JSON:\n%s", e)
@ -570,10 +567,9 @@ class HostipGeoIPProvider(GeolocationBackend):
def _refresh(self):
try:
reply = urllib2.urlopen(self.API_URL, timeout=
constants.NETWORK_CONNECTION_TIMEOUT)
if reply:
reply_dict = json.load(reply)
reply = requests.get(self.API_URL, timeout=constants.NETWORK_CONNECTION_TIMEOUT, verify=True)
if reply.status_code == requests.codes.ok:
reply_dict = reply.json()
territory = reply_dict.get("country_code", None)
# unless at least country_code is available,
@ -584,8 +580,13 @@ class HostipGeoIPProvider(GeolocationBackend):
public_ip_address=reply_dict.get("ip", None),
city=reply_dict.get("city", None)
))
except urllib2.URLError as e:
log.debug("Geoloc: URLError during Hostip lookup:\n%s", e)
else:
log.error("Geoloc: Hostip lookup failed with status code: %s", reply.status_code)
except requests.exceptions.RequestException as e:
log.debug("Geoloc: RequestException during Hostip lookup:\n%s", e)
except ValueError as e:
log.debug("Geoloc: Unable to decode Hostip JSON:\n%s", e)
class GoogleWiFiLocationProvider(GeolocationBackend):
@ -607,9 +608,8 @@ class GoogleWiFiLocationProvider(GeolocationBackend):
if access_points:
try:
url = self._get_url(access_points)
reply = urllib2.urlopen(url, timeout=
constants.NETWORK_CONNECTION_TIMEOUT)
result_dict = json.load(reply)
reply = requests.get(url, timeout=constants.NETWORK_CONNECTION_TIMEOUT, verify=True)
result_dict = reply.json()
status = result_dict.get('status', 'NOT OK')
if status == 'OK':
lat = result_dict['location']['lat']
@ -623,12 +623,13 @@ class GoogleWiFiLocationProvider(GeolocationBackend):
t_code = geocoding_result.territory_code
self._set_result(LocationResult(territory_code=t_code))
else:
log.info("Service couldn't find current location.")
except urllib2.URLError as e:
log.debug("Geoloc: URLError during Google"
" Wifi lookup:\n%s", e)
log.info("Geoloc: Service couldn't find current location.")
except requests.exceptions.RequestException as e:
log.debug("Geoloc: RequestException during Google Wifi lookup:\n%s", e)
except ValueError as e:
log.debug("Geoloc: Unable to decode Google Wifi JSON:\n%s", e)
else:
log.info("No WiFi access points found - can't detect location.")
log.info("Geoloc: No WiFi access points found - can't detect location.")
def _get_url(self, access_points):
"""Generate Google API URL for the given access points
@ -697,18 +698,19 @@ class Geocoder(object):
coordinates.latitude,
coordinates.longitude)
try:
reply = urllib2.urlopen(url, timeout=
constants.NETWORK_CONNECTION_TIMEOUT)
if reply:
reply_dict = json.load(reply)
reply = requests.get(url, timeout=constants.NETWORK_CONNECTION_TIMEOUT, verify=True)
if reply.status_code == requests.codes.ok:
reply_dict = reply.json()
territory_code = reply_dict['address']['country_code'].upper()
return GeocodingResult(coordinates=coordinates,
territory_code=territory_code)
else:
log.error("Geoloc: Nominatim reverse geocoding failed with status code: %s", reply.status_code)
return None
except urllib2.URLError as e:
log.debug("Geoloc: URLError during Nominatim reverse geocoding"
" :\n%s", e)
except requests.exceptions.RequestException as e:
log.debug("Geoloc: RequestException during Nominatim reverse geocoding:\n%s", e)
except ValueError as e:
log.debug("Geoloc: Unable to decode Nominatim reverse geocoding JSON:\n%s", e)
class GeocodingResult(object):

@ -23,9 +23,9 @@ __all__ = ["_", "N_", "P_", "C_", "CN_", "CP_"]
import gettext
_ = lambda x: gettext.ldgettext("anaconda", x) if x else ""
_ = lambda x: gettext.translation("anaconda", fallback=True).ugettext(x) if x != "" else u""
N_ = lambda x: x
P_ = lambda x, y, z: gettext.ldngettext("anaconda", x, y, z)
P_ = lambda x, y, z: gettext.translation("anaconda", fallback=True).ungettext(x, y, z)
# This is equivalent to "pgettext" in GNU gettext. The pgettext functions
# are not exported by Python, but all they really do is a stick a EOT

@ -21,11 +21,11 @@
Anaconda built-in help module
"""
import os
import subprocess
from pyanaconda.flags import flags
from pyanaconda.localization import find_best_locale_match
from pyanaconda.constants import DEFAULT_LANG
from pyanaconda.iutil import startProgram
import logging
log = logging.getLogger("anaconda")
@ -124,7 +124,7 @@ def start_yelp(help_path):
# under some extreme circumstances (placeholders missing)
# the help path can be None and we need to prevent Popen
# receiving None as an argument instead of a string
yelp_process = subprocess.Popen(["yelp", help_path or ""])
yelp_process = startProgram(["yelp", help_path or ""], reset_lang=False)
def kill_yelp():
"""Try to kill any existing yelp processes"""

@ -1,46 +0,0 @@
# indexed_dict.py
# Implements IndexedDictionary class.
#
# 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.
#
class IndexedDict(dict):
""" Indexed dictionary that remembers order of the inserted elements.
Values can be inserted with string keys only, but referenced by both
string keys or index.
There's a unit test for the class, please maintain it along.
"""
def __init__(self):
super(IndexedDict, self).__init__()
self._indexes = []
def __getitem__(self, key):
if type(key) is int:
key = self._indexes[key]
return super(IndexedDict, self).__getitem__(key)
def __setitem__(self, key, value):
if type(key) is int:
raise TypeError("IndexedDict only accepts strings as new keys")
assert(len(self) == len(self._indexes))
self._indexes.append(key)
return super(IndexedDict, self).__setitem__(key, value)
def index(self, string_key):
return self._indexes.index(string_key)

@ -20,13 +20,16 @@
# Red Hat Author(s): Chris Lumens <clumens@redhat.com>
#
from blivet import turnOnFilesystems, callbacks
from blivet import callbacks
from blivet.osinstall import turnOnFilesystems
from blivet.devices import BTRFSDevice
from pyanaconda.bootloader import writeBootLoader
from pyanaconda.progress import progress_report, progress_message, progress_step, progress_complete, progress_init
from pyanaconda.users import createLuserConf, getPassAlgo, Users
from pyanaconda import flags
from pyanaconda import iutil
from pyanaconda import timezone
from pyanaconda import network
from pyanaconda.i18n import _
from pyanaconda.threads import threadMgr
from pyanaconda.ui.lib.entropy import wait_for_entropy
@ -97,12 +100,20 @@ def doConfiguration(storage, payload, ksdata, instClass):
ksdata.rootpw.execute(storage, ksdata, instClass, u)
ksdata.group.execute(storage, ksdata, instClass, u)
ksdata.user.execute(storage, ksdata, instClass, u)
ksdata.sshkey.execute(storage, ksdata, instClass, u)
with progress_report(_("Configuring addons")):
ksdata.addons.execute(storage, ksdata, instClass, u)
with progress_report(_("Generating initramfs")):
payload.recreateInitrds(force=True)
payload.recreateInitrds()
# Work around rhbz#1200539, grubby doesn't handle grub2 missing initrd with /boot on btrfs
# So rerun writing the bootloader if this is live and /boot is on btrfs
boot_on_btrfs = isinstance(storage.mountpoints.get("/boot", storage.mountpoints.get("/")), BTRFSDevice)
if flags.flags.livecdInstall and boot_on_btrfs \
and (not ksdata.bootloader.disabled and ksdata.bootloader != "none"):
writeBootLoader(storage, payload, instClass, ksdata)
if willRunRealmd:
with progress_report(_("Joining realm: %s") % ksdata.realm.discovered):
@ -117,17 +128,6 @@ def doConfiguration(storage, payload, ksdata, instClass):
progress_complete()
def moveBootMntToPhysical(storage):
"""Move the /boot mount to /mnt/sysimage/boot."""
if iutil.getSysroot() == iutil.getTargetPhysicalRoot():
return
bootmnt = storage.mountpoints.get('/boot')
if bootmnt is None:
return
bootmnt.format.teardown()
bootmnt.teardown()
bootmnt.format.setup(options=bootmnt.format.options, chroot=iutil.getTargetPhysicalRoot())
def doInstall(storage, payload, ksdata, instClass):
"""Perform an installation. This method takes the ksdata as prepared by
the UI (the first hub, in graphical mode) and applies it to the disk.
@ -147,6 +147,11 @@ def doInstall(storage, payload, ksdata, instClass):
steps = len(storage.devicetree.findActions(action_type="create", object_type="format")) + \
len(storage.devicetree.findActions(action_type="resize", object_type="format"))
# Update every 10% of packages installed. We don't know how many packages
# we are installing until it's too late (see realmd later on) so this is
# the best we can do.
steps += 10
# pre setup phase, post install
steps += 2
@ -190,9 +195,8 @@ def doInstall(storage, payload, ksdata, instClass):
turnOnFilesystems(storage, mountOnly=flags.flags.dirInstall, callbacks=callbacks_reg)
write_storage_late = (flags.flags.livecdInstall or ksdata.ostreesetup.seen
or ksdata.method.method == "liveimg"
and not flags.flags.dirInstall)
if not write_storage_late:
or ksdata.method.method == "liveimg")
if not write_storage_late and not flags.flags.dirInstall:
storage.write()
# Do packaging.
@ -207,6 +211,10 @@ def doInstall(storage, payload, ksdata, instClass):
ksdata.authconfig.setup()
ksdata.firewall.setup()
# make name resolution work for rpm scripts in chroot
if flags.can_touch_runtime_system("copy /etc/resolv.conf to sysroot"):
network.copyFileToPath("/etc/resolv.conf", iutil.getSysroot())
# anaconda requires storage packages in order to make sure the target
# system is bootable and configurable, and some other packages in order
# to finish setting up the system.
@ -216,6 +224,9 @@ def doInstall(storage, payload, ksdata, instClass):
if willInstallBootloader:
packages += storage.bootloader.packages
if network.is_using_team_device():
packages.append("teamd")
# don't try to install packages from the install class' ignored list and the
# explicitly excluded ones (user takes the responsibility)
packages = [p for p in packages
@ -223,7 +234,7 @@ def doInstall(storage, payload, ksdata, instClass):
payload.preInstall(packages=packages, groups=payload.languageGroups())
payload.install()
if write_storage_late:
if write_storage_late and not flags.flags.dirInstall:
if iutil.getSysroot() != iutil.getTargetPhysicalRoot():
blivet.setSysroot(iutil.getTargetPhysicalRoot(),
iutil.getSysroot())
@ -251,13 +262,10 @@ def doInstall(storage, payload, ksdata, instClass):
# Do bootloader.
if willInstallBootloader:
with progress_report(_("Installing bootloader")):
with progress_report(_("Installing boot loader")):
writeBootLoader(storage, payload, instClass, ksdata)
with progress_report(_("Performing post-installation setup tasks")):
# Now, let's reset the state here so that the payload has
# /boot in the system root.
moveBootMntToPhysical(storage)
payload.postInstall()
progress_complete()

@ -26,7 +26,7 @@ import os, sys
import imputil
from blivet.partspec import PartSpec
from blivet.devicelibs import swap
from blivet.autopart import swapSuggestion
from blivet.platform import platform
from blivet.size import Size
@ -37,7 +37,7 @@ from pyanaconda.kickstart import getAvailableDiskSpace
class BaseInstallClass(object):
# default to not being hidden
hidden = 0
hidden = False
name = "base"
bootloaderTimeoutDefault = None
bootloaderExtraArgs = []
@ -58,15 +58,18 @@ class BaseInstallClass(object):
# Blivet uses by default.
defaultFS = None
# don't select this class by default
default = 0
# help
help_folder = "/usr/share/anaconda/help"
help_main_page = "Installation_Guide.xml"
help_placeholder = None
help_placeholder_with_links = None
# path to the installclass stylesheet, if any
stylesheet = None
# comps environment id to select by default
defaultPackageEnvironment = None
@property
def l10n_domain(self):
if self._l10n_domain is None:
@ -101,7 +104,7 @@ class BaseInstallClass(object):
disk_space = getAvailableDiskSpace(storage)
swp = swap.swapSuggestion(disk_space=disk_space)
swp = swapSuggestion(disk_space=disk_space)
autorequests.append(PartSpec(fstype="swap", size=swp, grow=False,
lv=True, encrypted=True))
@ -128,8 +131,8 @@ class BaseInstallClass(object):
allClasses = []
allClasses_hidden = []
# returns ( className, classObject, classLogo ) tuples
def availableClasses(showHidden=0):
# returns ( className, classObject ) tuples
def availableClasses(showHidden=False):
global allClasses
global allClasses_hidden
@ -194,7 +197,7 @@ def availableClasses(showHidden=0):
try:
found = imputil.imp.find_module(mainName)
except ImportError:
log.warning ("module import of %s failed: %s", mainName, sys.exc_type)
log.warning ("module import of %s failed: %s", mainName, sys.exc_info()[0])
continue
try:
@ -204,10 +207,10 @@ def availableClasses(showHidden=0):
# If it's got these two methods, it's an InstallClass.
if hasattr(obj, "setDefaultPartitioning") and hasattr(obj, "setPackageSelection"):
sortOrder = getattr(obj, "sortPriority", 0)
if obj.hidden == 0 or showHidden == 1:
if not obj.hidden or showHidden:
lst.append(((obj.name, obj), sortOrder))
except (ImportError, AttributeError):
log.warning ("module import of %s failed: %s", mainName, sys.exc_type)
log.warning ("module import of %s failed: %s", mainName, sys.exc_info()[0])
lst.sort(_ordering)
for (item, _) in lst:
@ -223,8 +226,8 @@ def availableClasses(showHidden=0):
def getBaseInstallClass():
# figure out what installclass we should base on.
allavail = availableClasses(showHidden = 1)
avail = availableClasses(showHidden = 0)
allavail = availableClasses(showHidden=True)
avail = availableClasses(showHidden=False)
if len(avail) == 1:
(cname, cobject) = avail[0]
@ -240,8 +243,6 @@ def getBaseInstallClass():
elif len(allavail) > 1:
(cname, cobject) = allavail.pop()
log.info('%s is the highest priority installclass, using it', cname)
# Default to the base installclass if nothing else is found.
else:
raise RuntimeError("Unable to find an install class to use!!!")

@ -33,7 +33,7 @@ _isys_la_SOURCES = isys.c
auditddir = $(libexecdir)/$(PACKAGE_NAME)
auditd_PROGRAMS = auditd
auditd_SOURCES = auditd.c
auditd_CFLAGS = -DSTANDALONE $(SELINUX_CFLAGS)
auditd_LDADD = $(SELINUX_LIBS) $(LIBNL_LIBS)
auditd_CFLAGS = $(SELINUX_CFLAGS)
auditd_LDFLAGS = -laudit
MAINTAINERCLEANFILES = Makefile.in

@ -29,18 +29,10 @@ except ImportError:
# up PYTHONPATH and just do this basic import.
import _isys
import os
import os.path
import socket
import stat
import sys
from pyanaconda import iutil
import blivet.arch
import re
import struct
import dbus
import time
import datetime
import pytz
import logging
log = logging.getLogger("anaconda")
@ -117,34 +109,39 @@ def set_system_time(secs):
"""
_isys.set_system_time(secs)
log.info("System time set to %s", time.ctime(secs))
log.info("System time set to %s UTC", time.asctime(time.gmtime(secs)))
def set_system_date_time(year=None, month=None, day=None, hour=None, minute=None,
second=None, utc=False):
second=None, tz=None):
"""
Set system date and time given by the parameters as numbers. If some
parameter is missing or None, the current system date/time field is used
instead (i.e. the value is not changed by this function).
:type year, month, ..., second: int
:param utc: wheter the other parameters specify UTC or local time
:type utc: bool
"""
# If no timezone is set, use UTC
if not tz:
tz = pytz.UTC
# get the right values
local = 0 if utc else 1
now = datetime.datetime.now()
year = year or now.year
month = month or now.month
day = day or now.day
hour = hour or now.hour
minute = minute or now.minute
second = second or now.second
# struct fields -> year, month, day, hour, minute, second, week_day, year_day, local
time_struct = time.struct_time((year, month, day, hour, minute, second, 0, 0, local))
set_system_time(int(time.mktime(time_struct)))
now = datetime.datetime.now(tz)
year = year if year is not None else now.year
month = month if month is not None else now.month
day = day if day is not None else now.day
hour = hour if hour is not None else now.hour
minute = minute if minute is not None else now.minute
second = second if second is not None else now.second
set_date = datetime.datetime(year, month, day, hour, minute, second, tzinfo=tz)
# Calculate the number of seconds between this time and timestamp 0
epoch = datetime.datetime.fromtimestamp(0, pytz.UTC)
timestamp = (set_date - epoch).total_seconds()
set_system_time(timestamp)
def total_memory():
"""Returns total system memory in kB (given to us by /proc/meminfo)"""
@ -180,4 +177,4 @@ def total_memory():
log.error("MemTotal: line not found in /proc/meminfo")
raise RuntimeError("MemTotal: line not found in /proc/meminfo")
handleSegv = _isys.handleSegv
installSyncSignalHandlers = _isys.installSyncSignalHandlers

@ -38,7 +38,6 @@
#include "auditd.h"
#ifdef USESELINUX
static int done;
static void sig_done(int sig)
@ -92,43 +91,38 @@ static void do_auditd(int fd) {
}
return;
}
#endif /* USESELINUX */
int audit_daemonize(void) {
#ifdef USESELINUX
int fd;
pid_t child;
/* I guess we should actually do something with the output of AC_FUNC_FORK */
#ifndef HAVE_WORKING_FORK
#error "Autoconf could not find a working fork. Please fix this."
#endif
if ((child = fork()) > 0)
return 0;
if (child < 0)
return -1;
#ifndef STANDALONE
for (fd = 0; fd < getdtablesize(); fd++)
close(fd);
signal(SIGTTOU, SIG_IGN);
signal(SIGTTIN, SIG_IGN);
signal(SIGTSTP, SIG_IGN);
#endif /* !defined(STANDALONE) */
/* Close stdin and friends */
close(STDIN_FILENO);
close(STDOUT_FILENO);
close(STDERR_FILENO);
if ((fd = open("/proc/self/oom_adj", O_RDWR)) >= 0) {
write(fd, "-17", 3);
if ((fd = open("/proc/self/oom_score_adj", O_RDWR)) >= 0) {
write(fd, "-1000", 5);
close(fd);
}
fd = audit_open();
do_auditd(fd);
audit_close(fd);
#ifndef STANDALONE
exit(0);
#endif /* !defined(STANDALONE) */
#endif /* USESELINUX */
return 0;
}
#ifdef STANDALONE
int main(void) {
if (audit_daemonize() < 0)
{
@ -138,7 +132,6 @@ int main(void) {
return 0;
}
#endif /* STANDALONE */
/*
* vim:ts=8:sw=4:sts=4:et

@ -28,14 +28,15 @@
#include <signal.h>
#include <execinfo.h>
#include <stdlib.h>
#include <string.h>
static PyObject * doSync(PyObject * s, PyObject * args);
static PyObject * doSegvHandler(PyObject *s, PyObject *args);
static PyObject * doSignalHandlers(PyObject *s, PyObject *args);
static PyObject * doSetSystemTime(PyObject *s, PyObject *args);
static PyMethodDef isysModuleMethods[] = {
{ "sync", (PyCFunction) doSync, METH_VARARGS, NULL},
{ "handleSegv", (PyCFunction) doSegvHandler, METH_VARARGS, NULL },
{ "sync", (PyCFunction) doSync, METH_NOARGS, NULL},
{ "installSyncSignalHandlers", (PyCFunction) doSignalHandlers, METH_NOARGS, NULL},
{ "set_system_time", (PyCFunction) doSetSystemTime, METH_VARARGS, NULL},
{ NULL, NULL, 0, NULL }
} ;
@ -46,27 +47,22 @@ void init_isys(void) {
}
static PyObject * doSync(PyObject * s, PyObject * args) {
int fd;
if (!PyArg_ParseTuple(args, "", &fd)) return NULL;
sync();
Py_INCREF(Py_None);
return Py_None;
}
static PyObject * doSegvHandler(PyObject *s, PyObject *args) {
static void sync_signal_handler(int signum) {
void *array[20];
size_t size;
char **strings;
size_t i;
signal(SIGSEGV, SIG_DFL); /* back to default */
size = backtrace (array, 20);
strings = backtrace_symbols (array, size);
printf ("Anaconda received SIGSEGV!. Backtrace:\n");
printf ("Anaconda received signal %d!. Backtrace:\n", signum);
for (i = 0; i < size; i++)
printf ("%s\n", strings[i]);
@ -74,6 +70,43 @@ static PyObject * doSegvHandler(PyObject *s, PyObject *args) {
exit(1);
}
static PyObject * doSignalHandlers(PyObject *s, PyObject *args) {
/* Install a signal handler for all synchronous signals */
struct sigaction sa;
memset(&sa, 0, sizeof(struct sigaction));
sa.sa_handler = sync_signal_handler;
/* Use these flags to ensure that a crash within the signal handler will
* just crash anaconda and not get stuck in a loop. RESETHAND resets the
* handler to SIG_DFL when the handler is entered, so that further signals
* will exit the program, and NODEFER ensures that the signal is not blocked
* during the signal handler, so a SIGSEGV triggered by handling a SIGSEGV will
* be processed and will use the default handler. The Linux kernel forces
* both of these things during a signal handler crash, but this makes it
* explicit.
*
* These flags also mean that a SIGSEGV from a second thread could abort
* the processing of a SIGSEGV from a first, but too bad.
*/
sa.sa_flags = SA_RESETHAND | SA_NODEFER;
if (sigaction(SIGILL, &sa, NULL) != 0) {
return PyErr_SetFromErrno(PyExc_SystemError);
}
if (sigaction(SIGFPE, &sa, NULL) != 0) {
return PyErr_SetFromErrno(PyExc_SystemError);
}
if (sigaction(SIGSEGV, &sa, NULL) != 0) {
return PyErr_SetFromErrno(PyExc_SystemError);
}
Py_INCREF(Py_None);
return Py_None;
}
static PyObject * doSetSystemTime(PyObject *s, PyObject *args) {
struct timeval tv;
tv.tv_usec = 0;
@ -82,7 +115,7 @@ static PyObject * doSetSystemTime(PyObject *s, PyObject *args) {
return NULL;
if (settimeofday(&tv, NULL) != 0)
PyErr_SetFromErrno(PyExc_SystemError);
return PyErr_SetFromErrno(PyExc_SystemError);
Py_INCREF(Py_None);
return Py_None;

@ -1,7 +1,7 @@
#
# iutil.py - generic install utility functions
#
# Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007
# Copyright (C) 1999-2014
# Red Hat, Inc. All rights reserved.
#
# This program is free software; you can redistribute it and/or modify
@ -27,12 +27,16 @@ import os.path
import errno
import subprocess
import unicodedata
import string
# Used for ascii_lowercase, ascii_uppercase constants
import string # pylint: disable=deprecated-module
import tempfile
import types
import re
from threading import Thread
from Queue import Queue, Empty
from urllib import quote, unquote
import gettext
import signal
from gi.repository import GLib
from pyanaconda.flags import flags
from pyanaconda.constants import DRACUT_SHUTDOWN_EJECT, TRANSLATIONS_UPDATE_DIR, UNSUPPORTED_HW
@ -46,11 +50,25 @@ program_log = logging.getLogger("program")
from pyanaconda.anaconda_log import program_log_lock
_child_env = {}
def setenv(name, value):
""" Set an environment variable to be used by child processes.
This method does not modify os.environ for the running process, which
is not thread-safe. If setenv has already been called for a particular
variable name, the old value is overwritten.
:param str name: The name of the environment variable
:param str value: The value of the environment variable
"""
_child_env[name] = value
def augmentEnv():
env = os.environ.copy()
env.update({"LC_ALL": "C",
"ANA_INSTALL_PATH": getSysroot()
})
env.update({"ANA_INSTALL_PATH": getSysroot()})
env.update(_child_env)
return env
_root_path = "/mnt/sysimage"
@ -97,16 +115,26 @@ def setSysroot(path):
global _sysroot
_sysroot = path
def _run_program(argv, root='/', stdin=None, stdout=None, env_prune=None, log_output=True, binary_output=False):
""" Run an external program, log the output and return it to the caller
def startProgram(argv, root='/', stdin=None, stdout=subprocess.PIPE, stderr=subprocess.STDOUT,
env_prune=None, env_add=None, reset_handlers=True, reset_lang=True, **kwargs):
""" Start an external program and return the Popen object.
The root and reset_handlers arguments are handled by passing a
preexec_fn argument to subprocess.Popen, but an additional preexec_fn
can still be specified and will be run. The user preexec_fn will be run
last.
:param argv: The command to run and argument
:param root: The directory to chroot to before running command.
:param stdin: The file object to read stdin from.
:param stdout: Optional file object to write stdout and stderr to.
:param env_prune: environment variable to remove before execution
:param log_output: whether to log the output of command
:param binary_output: whether to treat the output of command as binary data
:return: The return code of the command and the output
:param stdout: The file object to write stdout to.
:param stderr: The file object to write stderr to.
:param env_prune: environment variables to remove before execution
:param env_add: environment variables to add before execution
:param reset_handlers: whether to reset to SIG_DFL any signal handlers set to SIG_IGN
:param reset_lang: whether to set the locale of the child process to C
:param kwargs: Additional parameters to pass to subprocess.Popen
:return: A Popen object for the running command.
"""
if env_prune is None:
env_prune = []
@ -117,45 +145,152 @@ def _run_program(argv, root='/', stdin=None, stdout=None, env_prune=None, log_ou
if target_root == _root_path:
target_root = getSysroot()
def chroot():
# Check for and save a preexec_fn argument
preexec_fn = kwargs.pop("preexec_fn", None)
def preexec():
# If a target root was specificed, chroot into it
if target_root and target_root != '/':
os.chroot(target_root)
os.chdir("/")
# Signal handlers set to SIG_IGN persist across exec. Reset
# these to SIG_DFL if requested. In particular this will include the
# SIGPIPE handler set by python.
if reset_handlers:
for signum in range(1, signal.NSIG):
if signal.getsignal(signum) == signal.SIG_IGN:
signal.signal(signum, signal.SIG_DFL)
# If the user specified an additional preexec_fn argument, run it
if preexec_fn is not None:
preexec_fn()
with program_log_lock:
program_log.info("Running... %s", " ".join(argv))
env = augmentEnv()
for var in env_prune:
env.pop(var, None)
env = augmentEnv()
for var in env_prune:
env.pop(var, None)
try:
proc = subprocess.Popen(argv,
stdin=stdin,
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT,
preexec_fn=chroot, cwd=root, env=env)
output_string = proc.communicate()[0]
if output_string:
if binary_output:
output_lines = [output_string]
else:
if output_string[-1] != "\n":
output_string = output_string + "\n"
output_lines = output_string.splitlines(True)
for line in output_lines:
if log_output:
if reset_lang:
env.update({"LC_ALL": "C"})
if env_add:
env.update(env_add)
return subprocess.Popen(argv,
stdin=stdin,
stdout=stdout,
stderr=stderr,
close_fds=True,
preexec_fn=preexec, cwd=root, env=env, **kwargs)
def startX(argv, output_redirect=None):
""" Start X and return once X is ready to accept connections.
X11, if SIGUSR1 is set to SIG_IGN, will send SIGUSR1 to the parent
process once it is ready to accept client connections. This method
sets that up and waits for the signal or bombs out if nothing happens
for a minute. The process will also be added to the list of watched
processes.
:param argv: The command line to run, as a list
:param output_redirect: file or file descriptor to redirect stdout and stderr to
"""
# Use a list so the value can be modified from the handler function
x11_started = [False]
def sigusr1_handler(num, frame):
log.debug("X server has signalled a successful start.")
x11_started[0] = True
# Fail after, let's say a minute, in case something weird happens
# and we don't receive SIGUSR1
def sigalrm_handler(num, frame):
# Check that it didn't make it under the wire
if x11_started[0]:
return
log.error("Timeout trying to start %s", argv[0])
raise ExitError("Timeout trying to start %s" % argv[0])
# preexec_fn to add the SIGUSR1 handler in the child
def sigusr1_preexec():
signal.signal(signal.SIGUSR1, signal.SIG_IGN)
try:
old_sigusr1_handler = signal.signal(signal.SIGUSR1, sigusr1_handler)
old_sigalrm_handler = signal.signal(signal.SIGALRM, sigalrm_handler)
# Start the timer
signal.alarm(60)
childproc = startProgram(argv, stdout=output_redirect, stderr=output_redirect,
preexec_fn=sigusr1_preexec)
watchProcess(childproc, argv[0])
# Wait for SIGUSR1
while not x11_started[0]:
signal.pause()
finally:
# Put everything back where it was
signal.alarm(0)
signal.signal(signal.SIGUSR1, old_sigusr1_handler)
signal.signal(signal.SIGALRM, old_sigalrm_handler)
def _run_program(argv, root='/', stdin=None, stdout=None, env_prune=None, log_output=True,
binary_output=False, filter_stderr=False):
""" Run an external program, log the output and return it to the caller
:param argv: The command to run and argument
:param root: The directory to chroot to before running command.
:param stdin: The file object to read stdin from.
:param stdout: Optional file object to write the output to.
:param env_prune: environment variable to remove before execution
:param log_output: whether to log the output of command
:param binary_output: whether to treat the output of command as binary data
:param filter_stderr: whether to exclude the contents of stderr from the returned output
:return: The return code of the command and the output
"""
try:
if filter_stderr:
stderr = subprocess.PIPE
else:
stderr = subprocess.STDOUT
proc = startProgram(argv, root=root, stdin=stdin, stdout=subprocess.PIPE, stderr=stderr,
env_prune=env_prune)
(output_string, err_string) = proc.communicate()
if output_string:
if binary_output:
output_lines = [output_string]
else:
if output_string[-1] != "\n":
output_string = output_string + "\n"
output_lines = output_string.splitlines(True)
if log_output:
with program_log_lock:
for line in output_lines:
program_log.info(line.strip())
if stdout:
stdout.write(line)
if stdout:
stdout.write(output_string)
except OSError as e:
# If stderr was filtered, log it separately
if filter_stderr and err_string and log_output:
err_lines = err_string.splitlines(True)
with program_log_lock:
for line in err_lines:
program_log.info(line.strip())
except OSError as e:
with program_log_lock:
program_log.error("Error running %s: %s", argv[0], e.strerror)
raise
raise
with program_log_lock:
program_log.debug("Return code: %d", proc.returncode)
return (proc.returncode, output_string)
@ -192,13 +327,14 @@ def execWithRedirect(command, argv, stdin=None, stdout=None,
return _run_program(argv, stdin=stdin, stdout=stdout, root=root, env_prune=env_prune,
log_output=log_output, binary_output=binary_output)[0]
def execWithCapture(command, argv, stdin=None, root='/', log_output=True):
def execWithCapture(command, argv, stdin=None, root='/', log_output=True, filter_stderr=False):
""" Run an external program and capture standard out and err.
:param command: The command to run
:param argv: The argument list
:param stdin: The file object to read stdin from.
:param root: The directory to chroot to before running command.
:param log_output: Whether to log the output of command
:param filter_stderr: Whether stderr should be excluded from the returned output
:return: The output of the command
"""
if flags.testing:
@ -207,12 +343,17 @@ def execWithCapture(command, argv, stdin=None, root='/', log_output=True):
return ""
argv = [command] + argv
return _run_program(argv, stdin=stdin, root=root, log_output=log_output)[1]
return _run_program(argv, stdin=stdin, root=root, log_output=log_output,
filter_stderr=filter_stderr)[1]
def execReadlines(command, argv, stdin=None, root='/', env_prune=None):
""" Execute an external command and return the line output of the command
in real-time.
This method assumes that there is a reasonably low delay between the
end of output and the process exiting. If the child process closes
stdout and then keeps on truckin' there will be problems.
:param command: The command to run
:param argv: The argument list
:param stdin: The file object to read stdin from.
@ -222,66 +363,210 @@ def execReadlines(command, argv, stdin=None, root='/', env_prune=None):
:param env_prune: environment variable to remove before execution
Output from the file is not logged to program.log
This returns a generator with the lines from the command until it has finished
This returns an iterator with the lines from the command until it has finished
"""
if env_prune is None:
env_prune = []
# Return the lines from stdout via a Queue
def queue_lines(out, queue):
for line in iter(out.readline, b''):
queue.put(line.strip())
out.close()
class ExecLineReader(object):
"""Iterator class for returning lines from a process and cleaning
up the process when the output is no longer needed.
"""
def chroot():
if root and root != '/':
os.chroot(root)
os.chdir("/")
def __init__(self, proc, argv):
self._proc = proc
self._argv = argv
def __iter__(self):
return self
def __del__(self):
# See if the process is still running
if self._proc.poll() is None:
# Stop the process and ignore any problems that might arise
try:
self._proc.terminate()
except OSError:
pass
def next(self):
# Read the next line, blocking if a line is not yet available
line = self._proc.stdout.readline()
if line == '':
# Output finished, wait for the process to end
self._proc.communicate()
# Check for successful exit
if self._proc.returncode < 0:
raise OSError("process '%s' was killed by signal %s" %
(self._argv, -self._proc.returncode))
elif self._proc.returncode > 0:
raise OSError("process '%s' exited with status %s" %
(self._argv, self._proc.returncode))
raise StopIteration
return line.strip()
argv = [command] + argv
with program_log_lock:
program_log.info("Running... %s", " ".join(argv))
env = augmentEnv()
for var in env_prune:
env.pop(var, None)
try:
proc = subprocess.Popen(argv,
stdin=stdin,
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT,
bufsize=1,
preexec_fn=chroot, cwd=root, env=env)
proc = startProgram(argv, root=root, stdin=stdin, env_prune=env_prune, bufsize=1)
except OSError as e:
program_log.error("Error running %s: %s", argv[0], e.strerror)
with program_log_lock:
program_log.error("Error running %s: %s", argv[0], e.strerror)
raise
q = Queue()
t = Thread(target=queue_lines, args=(proc.stdout, q))
t.daemon = True # thread dies with the program
t.start()
while True:
try:
line = q.get(timeout=.1)
yield line
q.task_done()
except Empty:
if proc.poll() is not None:
if os.WIFSIGNALED(proc.returncode):
raise OSError("process '%s' was killed" % argv)
break
q.join()
return ExecLineReader(proc, argv)
## Run a shell.
def execConsole():
try:
proc = subprocess.Popen(["/bin/sh"])
proc = startProgram(["/bin/sh"], stdout=None, stderr=None, reset_lang=False)
proc.wait()
except OSError as e:
raise RuntimeError("Error running /bin/sh: " + e.strerror)
# Dictionary of processes to watch in the form {pid: [name, GLib event source id], ...}
_forever_pids = {}
# Set to True if process watching is handled by GLib
_watch_process_glib = False
_watch_process_handler_set = False
class ExitError(RuntimeError):
pass
# Raise an error on process exit. The argument is a list of tuples
# of the form [(name, status), ...] with statuses in the subprocess
# format (>=0 is return codes, <0 is signal)
def _raise_exit_error(statuses):
exn_message = []
for proc_name, status in statuses:
if status >= 0:
status_str = "with status %s" % status
else:
status_str = "on signal %s" % -status
exn_message.append("%s exited %s" % (proc_name, status_str))
raise ExitError(", ".join(exn_message))
# Signal handler used with watchProcess
def _sigchld_handler(num=None, frame=None):
# Check whether anything in the list of processes being watched has
# exited. We don't want to call waitpid(-1), since that would break
# anything else using wait/waitpid (like the subprocess module).
exited_pids = []
exit_statuses = []
for child_pid in _forever_pids:
try:
pid_result, status = eintr_retry_call(os.waitpid, child_pid, os.WNOHANG)
except OSError as e:
if e.errno == errno.ECHILD:
continue
if pid_result:
proc_name = _forever_pids[child_pid][0]
exited_pids.append(child_pid)
# Convert the wait-encoded status to the format used by subprocess
if os.WIFEXITED(status):
sub_status = os.WEXITSTATUS(status)
else:
# subprocess uses negative return codes to indicate signal exit
sub_status = -os.WTERMSIG(status)
exit_statuses.append((proc_name, sub_status))
for child_pid in exited_pids:
if _forever_pids[child_pid][1]:
GLib.source_remove(_forever_pids[child_pid][1])
del _forever_pids[child_pid]
if exit_statuses:
_raise_exit_error(exit_statuses)
# GLib callback used with watchProcess
def _watch_process_cb(pid, status, proc_name):
# Convert the wait-encoded status to the format used by subprocess
if os.WIFEXITED(status):
sub_status = os.WEXITSTATUS(status)
else:
# subprocess uses negative return codes to indicate signal exit
sub_status = -os.WTERMSIG(status)
_raise_exit_error([(proc_name, sub_status)])
def watchProcess(proc, name):
"""Watch for a process exit, and raise a ExitError when it does.
This method installs a SIGCHLD signal handler and thus interferes
the child_watch_add methods in GLib. Use watchProcessGLib to convert
to GLib mode if using a GLib main loop.
Since the SIGCHLD handler calls wait() on the watched process, this call
cannot be combined with Popen.wait() or Popen.communicate, and also
doing so wouldn't make a whole lot of sense.
:param proc: The Popen object for the process
:param name: The name of the process
"""
global _watch_process_handler_set
if not _watch_process_glib and not _watch_process_handler_set:
signal.signal(signal.SIGCHLD, _sigchld_handler)
_watch_process_handler_set = True
# Add the PID to the dictionary
# The second item in the list is for the GLib event source id and will be
# replaced with the id once we have one.
_forever_pids[proc.pid] = [name, None]
# If GLib is watching processes, add a watcher. child_watch_add checks if
# the process has already exited.
if _watch_process_glib:
_forever_pids[proc.id][1] = GLib.child_watch_add(proc.pid, _watch_process_cb, name)
else:
# Check that the process didn't already exit
if proc.poll() is not None:
del _forever_pids[proc.pid]
_raise_exit_error([(name, proc.returncode)])
def watchProcessGLib():
"""Convert process watching to GLib mode.
This allows anaconda modes that use GLib main loops to use
GLib.child_watch_add and continue to watch processes started before the
main loop.
"""
global _watch_process_glib
# The first call to child_watch_add will replace our SIGCHLD handler, and
# child_watch_add checks if the process has already exited before it returns,
# which will handle processes that exit while we're in the loop.
_watch_process_glib = True
for child_pid in _forever_pids:
_forever_pids[child_pid][1] = GLib.child_watch_add(child_pid, _watch_process_cb,
_forever_pids[child_pid])
def unwatchProcess(proc):
"""Unwatch a process watched by watchProcess.
:param proc: The Popen object for the process.
"""
if _forever_pids[proc.pid][1]:
GLib.source_remove(_forever_pids[proc.pid][1])
del _forever_pids[proc.pid]
def unwatchAllProcesses():
"""Clear the watched process list."""
global _forever_pids
for child_pid in _forever_pids:
if _forever_pids[child_pid][1]:
GLib.source_remove(_forever_pids[child_pid][1])
_forever_pids = {}
def getDirSize(directory):
""" Get the size of a directory and all its subdirectories.
:param dir: The name of the directory to find the size of.
@ -396,7 +681,7 @@ def parseNfsUrl(nfsurl):
return (options, host, path)
def add_po_path(module, directory):
def add_po_path(directory):
""" Looks to see what translations are under a given path and tells
the gettext module to use that path as the base dir """
for d in os.listdir(directory):
@ -408,27 +693,12 @@ def add_po_path(module, directory):
if not basename.endswith(".mo"):
continue
log.info("setting %s as translation source for %s", directory, basename[:-3])
module.bindtextdomain(basename[:-3], directory)
gettext.bindtextdomain(basename[:-3], directory)
def setup_translations(module):
def setup_translations():
if os.path.isdir(TRANSLATIONS_UPDATE_DIR):
add_po_path(module, TRANSLATIONS_UPDATE_DIR)
module.textdomain("anaconda")
def fork_orphan():
"""Forks an orphan.
Returns 1 in the parent and 0 in the orphaned child.
"""
intermediate = os.fork()
if not intermediate:
if os.fork():
# the intermediate child dies
os._exit(0)
return 0
# the original process waits for the intermediate child
eintr_retry_call(os.waitpid, intermediate, 0)
return 1
add_po_path(TRANSLATIONS_UPDATE_DIR)
gettext.textdomain("anaconda")
def _run_systemctl(command, service):
"""
@ -756,9 +1026,9 @@ def is_unsupported_hw():
:rtype: bool
"""
try:
tainted = long(open("/proc/sys/kernel/tainted").read())
tainted = int(open("/proc/sys/kernel/tainted").read())
except (IOError, ValueError):
tainted = 0L
tainted = 0
status = bool(tainted & UNSUPPORTED_HW)
if status:
@ -888,6 +1158,25 @@ def xprogressive_delay():
yield 0.25*(2**counter)
counter += 1
def get_platform_groupid():
""" Return a platform group id string
This runs systemd-detect-virt and if the result is not 'none' it
prefixes the lower case result with "platform-" for use as a group id.
:returns: Empty string or a group id for the detected platform
:rtype: str
"""
try:
platform = execWithCapture("systemd-detect-virt", []).strip()
except (IOError, AttributeError):
return ""
if platform == "none":
return ""
return "platform-" + platform.lower()
def persistent_root_image():
""":returns: whether we are running from a persistent (not in RAM) root.img"""
@ -901,6 +1190,30 @@ def persistent_root_image():
return True
_supports_ipmi = None
def ipmi_report(event):
global _supports_ipmi
if _supports_ipmi is None:
_supports_ipmi = os.path.exists("/dev/ipmi0") and os.path.exists("/usr/bin/ipmitool")
if not _supports_ipmi:
return
(fd, path) = tempfile.mkstemp()
# EVM revision - always 0x4
# Sensor type - always 0x1F for Base OS Boot/Installation Status
# Sensor num - passed in event
# Event dir & type - always 0x0 for anaconda's purposes
# Event data 1, 2, 3 - 0x0 for now
eintr_retry_call(os.write, fd, "0x4 0x1F %#x 0x0 0x0 0x0 0x0\n" % event)
eintr_retry_call(os.close, fd)
execWithCapture("ipmitool", ["sel", "add", path])
os.remove(path)
# Copied from python's subprocess.py
def eintr_retry_call(func, *args):
"""Retry an interruptible system call if interrupted."""
@ -911,3 +1224,7 @@ def eintr_retry_call(func, *args):
if e.errno == errno.EINTR:
continue
raise
def parent_dir(directory):
"""Return the parent's path"""
return "/".join(os.path.normpath(directory).split("/")[:-1])

@ -395,12 +395,7 @@ class LocaledWrapper(object):
# if there are more layouts than variants, empty strings should be appended
diff = len(layouts) - len(variants)
variants.extend(diff * [""])
# if there are more variants than layouts, throw the trailing ones away
variants = variants[:len(layouts)]
# map can be used with multiple lists and works like zipWith (Haskell)
return map(join_layout_variant, layouts, variants)
return [join_layout_variant(layout, variant) for layout, variant in zip(layouts, variants)]
@property
def options(self):

@ -21,15 +21,16 @@
from pyanaconda.errors import ScriptError, errorHandler
from blivet.deviceaction import ActionCreateFormat, ActionDestroyFormat, ActionResizeDevice, ActionResizeFormat
from blivet.devices import LUKSDevice
from blivet.devicelibs.lvm import getPossiblePhysicalExtents, LVM_PE_SIZE, KNOWN_THPOOL_PROFILES
from blivet.devices.lvm import LVMVolumeGroupDevice
from blivet.devicelibs.lvm import LVM_PE_SIZE, KNOWN_THPOOL_PROFILES
from blivet.devicelibs.crypto import MIN_CREATE_ENTROPY
from blivet.devicelibs import swap as swap_lib
from blivet.formats import getFormat
from blivet.partitioning import doPartitioning
from blivet.partitioning import growLVM
from blivet.errors import PartitioningError
from blivet.size import Size
from blivet.errors import PartitioningError, StorageError, BTRFSValueError
from blivet.size import Size, KiB
from blivet import udev
from blivet import autopart
from blivet.platform import platform
import blivet.iscsi
import blivet.fcoe
@ -41,9 +42,8 @@ from pyanaconda import iutil
import os
import os.path
import tempfile
import subprocess
from pyanaconda.flags import flags, can_touch_runtime_system
from pyanaconda.constants import ADDON_PATHS
from pyanaconda.constants import ADDON_PATHS, IPMI_ABORTED
import shlex
import sys
import urlgrabber
@ -62,12 +62,15 @@ from pyanaconda.i18n import _
from pyanaconda.ui.common import collect
from pyanaconda.addons import AddonSection, AddonData, AddonRegistry, collect_addon_paths
from pyanaconda.bootloader import GRUB2, get_bootloader
from pyanaconda.pwpolicy import F22_PwPolicy, F22_PwPolicyData
from pykickstart.constants import CLEARPART_TYPE_NONE, FIRSTBOOT_SKIP, FIRSTBOOT_RECONFIG, KS_SCRIPT_POST, KS_SCRIPT_PRE, \
KS_SCRIPT_TRACEBACK, SELINUX_DISABLED, SELINUX_ENFORCING, SELINUX_PERMISSIVE
from pykickstart.base import BaseHandler
from pykickstart.errors import formatErrorMsg, KickstartError, KickstartValueError
from pykickstart.parser import KickstartParser
from pykickstart.parser import Script as KSScript
from pykickstart.sections import Section
from pykickstart.sections import NullSection, PackageSection, PostScriptSection, PreScriptSection, TracebackScriptSection
from pykickstart.version import returnClassForVersion
@ -76,8 +79,7 @@ log = logging.getLogger("anaconda")
stderrLog = logging.getLogger("anaconda.stderr")
storage_log = logging.getLogger("blivet")
stdoutLog = logging.getLogger("anaconda.stdout")
from pyanaconda.anaconda_log import logger, logLevelMap, setHandlersLevel,\
DEFAULT_TTY_LEVEL
from pyanaconda.anaconda_log import logger, logLevelMap, setHandlersLevel, DEFAULT_LEVEL
class AnacondaKSScript(KSScript):
""" Execute a kickstart script
@ -96,6 +98,9 @@ class AnacondaKSScript(KSScript):
else:
scriptRoot = "/"
# Environment variables that cause problems for %post scripts
env_prune = ["LIBUSER_CONF"]
(fd, path) = tempfile.mkstemp("", "ks-script-", scriptRoot + "/tmp")
iutil.eintr_retry_call(os.write, fd, self.script)
@ -122,7 +127,8 @@ class AnacondaKSScript(KSScript):
with open(messages, "w") as fp:
rc = iutil.execWithRedirect(self.interp, ["/tmp/%s" % os.path.basename(path)],
stdout=fp,
root = scriptRoot)
root = scriptRoot,
env_prune = env_prune)
if rc != 0:
log.error("Error code %s running the kickstart script at line %s", rc, self.lineno)
@ -132,6 +138,7 @@ class AnacondaKSScript(KSScript):
err = "".join(fp.readlines())
errorHandler.cb(ScriptError(self.lineno, err))
iutil.ipmi_report(IPMI_ABORTED)
sys.exit(0)
class AnacondaInternalScript(AnacondaKSScript):
@ -251,7 +258,7 @@ def refreshAutoSwapSize(storage):
for request in storage.autoPartitionRequests:
if request.fstype == "swap":
disk_space = getAvailableDiskSpace(storage)
request.size = swap_lib.swapSuggestion(disk_space=disk_space)
request.size = autopart.swapSuggestion(disk_space=disk_space)
break
###
@ -270,7 +277,7 @@ class Authconfig(commands.authconfig.FC3_Authconfig):
def execute(self, *args):
cmd = "/usr/sbin/authconfig"
if not os.path.lexists(iutil.getSysroot()+cmd):
if self.seen:
if flags.automatedInstall and self.seen:
msg = _("%s is missing. Cannot setup authentication.") % cmd
raise KickstartError(msg)
else:
@ -306,7 +313,7 @@ class AutoPart(commands.autopart.F21_AutoPart):
return retval
def execute(self, storage, ksdata, instClass):
from blivet.partitioning import doAutoPartition
from blivet.autopart import doAutoPartition
from pyanaconda.storage_utils import sanity_check
if not self.autopart:
@ -351,6 +358,11 @@ class Bootloader(commands.bootloader.F21_Bootloader):
raise KickstartValueError(formatErrorMsg(self.lineno,
msg=_("GRUB2 does not support installation to a partition.")))
if self.isCrypted and isinstance(get_bootloader(), GRUB2):
if not self.password.startswith("grub.pbkdf2."):
raise KickstartValueError(formatErrorMsg(self.lineno,
msg="GRUB2 encrypted password must be in grub.pbkdf2 format."))
return self
def execute(self, storage, ksdata, instClass):
@ -449,12 +461,12 @@ class BTRFSData(commands.btrfs.F17_BTRFSData):
if dev and dev.format.type != "btrfs":
raise KickstartValueError(formatErrorMsg(self.lineno,
msg=_("BTRFS partition \"%(device)s\" has a format of \"%(format)s\", but should have a format of \"btrfs\".") %
msg=_("Btrfs partition \"%(device)s\" has a format of \"%(format)s\", but should have a format of \"btrfs\".") %
{"device": member, "format": dev.format.type}))
if not dev:
raise KickstartValueError(formatErrorMsg(self.lineno,
msg=_("Tried to use undefined partition \"%s\" in BTRFS volume specification.") % member))
msg=_("Tried to use undefined partition \"%s\" in Btrfs volume specification.") % member))
members.append(dev)
@ -467,7 +479,7 @@ class BTRFSData(commands.btrfs.F17_BTRFSData):
if len(members) == 0 and not self.preexist:
raise KickstartValueError(formatErrorMsg(self.lineno,
msg=_("BTRFS volume defined without any member devices. Either specify member devices or use --useexisting.")))
msg=_("Btrfs volume defined without any member devices. Either specify member devices or use --useexisting.")))
# allow creating btrfs vols/subvols without specifying mountpoint
if self.mountpoint in ("none", "None"):
@ -491,16 +503,19 @@ class BTRFSData(commands.btrfs.F17_BTRFSData):
device = devicetree.resolveDevice(self.name)
if not device:
raise KickstartValueError(formatErrorMsg(self.lineno,
msg=_("BTRFS volume \"%s\" specified with --useexisting does not exist.") % self.name))
msg=_("Btrfs volume \"%s\" specified with --useexisting does not exist.") % self.name))
device.format.mountpoint = self.mountpoint
else:
request = storage.newBTRFS(name=name,
try:
request = storage.newBTRFS(name=name,
subvol=self.subvol,
mountpoint=self.mountpoint,
metaDataLevel=self.metaDataLevel,
dataLevel=self.dataLevel,
parents=members)
except BTRFSValueError as e:
raise KickstartValueError(formatErrorMsg(self.lineno, msg=e.message))
storage.createDevice(request)
@ -515,17 +530,13 @@ class Realm(commands.realm.F19_Realm):
return
try:
argv = ["realm", "discover", "--verbose"] + \
argv = ["discover", "--verbose"] + \
self.discover_options + [self.join_realm]
proc = subprocess.Popen(argv, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
output, stderr = proc.communicate()
# might contain useful information for users who use
# use the realm kickstart command
log.info("Realm discover stderr:\n%s", stderr)
except OSError as msg:
output = iutil.execWithCapture("realm", argv, filter_stderr=True)
except OSError:
# TODO: A lousy way of propagating what will usually be
# 'no such realm'
log.error("Error running realm %s: %s", argv, msg)
# The error message is logged by iutil
return
# Now parse the output for the required software. First line is the
@ -557,25 +568,16 @@ class Realm(commands.realm.F19_Realm):
# no explicit password arg using implicit --no-password
pw_args = ["--no-password"]
argv = ["realm", "join", "--install", iutil.getSysroot(), "--verbose"] + \
argv = ["join", "--install", iutil.getSysroot(), "--verbose"] + \
pw_args + self.join_args
rc = -1
try:
proc = subprocess.Popen(argv, stdout=subprocess.PIPE,
stderr=subprocess.PIPE)
stderr = proc.communicate()[1]
# might contain useful information for users who use
# use the realm kickstart command
log.info("Realm join stderr:\n%s", stderr)
rc = proc.returncode
except OSError as msg:
log.error("Error running %s: %s", argv, msg)
if rc != 0:
log.error("Command failure: %s: %d", argv, rc)
return
rc = iutil.execWithRedirect("realm", argv)
except OSError:
pass
log.info("Joined realm %s", self.join_realm)
if rc == 0:
log.info("Joined realm %s", self.join_realm)
class ClearPart(commands.clearpart.F21_ClearPart):
@ -820,6 +822,11 @@ class LogVolData(commands.logvol.F21_LogVolData):
storage.doAutoPart = False
# FIXME: we should be running sanityCheck on partitioning that is not ks
# autopart, but that's likely too invasive for #873135 at this moment
if self.mountpoint == "/boot" and blivet.arch.isS390():
raise KickstartValueError(formatErrorMsg(self.lineno, msg="/boot can not be of type 'lvmlv' on s390x"))
# we might have truncated or otherwise changed the specified vg name
vgname = ksdata.onPart.get(self.vgname, self.vgname)
@ -833,7 +840,7 @@ class LogVolData(commands.logvol.F21_LogVolData):
self.mountpoint = ""
if self.recommended or self.hibernation:
disk_space = getAvailableDiskSpace(storage)
size = swap_lib.swapSuggestion(hibernation=self.hibernation, disk_space=disk_space)
size = autopart.swapSuggestion(hibernation=self.hibernation, disk_space=disk_space)
self.grow = False
else:
if self.fstype != "":
@ -841,7 +848,7 @@ class LogVolData(commands.logvol.F21_LogVolData):
else:
ty = storage.defaultFSType
if size is None:
if size is None and not self.preexist:
if not self.size:
raise KickstartValueError(formatErrorMsg(self.lineno,
msg="Size can not be decided on from kickstart nor obtained from device."))
@ -921,18 +928,10 @@ class LogVolData(commands.logvol.F21_LogVolData):
msg=_("Logical volume name \"%(logvol)s\" is already in use in volume group \"%(volgroup)s\".") %
{"logvol": self.name, "volgroup": vg.name}))
# Size specification checks
if not self.percent:
if not size:
raise KickstartValueError(formatErrorMsg(self.lineno,
msg=_("No size given for logical volume \"%s\". Use one of --useexisting, --size, or --percent.") % self.name))
elif not self.grow and size < vg.peSize:
raise KickstartValueError(formatErrorMsg(self.lineno,
msg=_("Logical volume size \"%(logvolSize)s\" must be larger than the volume group extent size of \"%(extentSize)s\".") %
{"logvolSize": size, "extentSize": vg.peSize}))
elif self.percent <= 0 or self.percent > 100:
if not self.percent and size and not self.grow and size < vg.peSize:
raise KickstartValueError(formatErrorMsg(self.lineno,
msg=_("Percentage must be between 0 and 100.")))
msg=_("Logical volume size \"%(logvolSize)s\" must be larger than the volume group extent size of \"%(extentSize)s\".") %
{"logvolSize": size, "extentSize": vg.peSize}))
# Now get a format to hold a lot of these extra values.
fmt = getFormat(ty,
@ -942,8 +941,9 @@ class LogVolData(commands.logvol.F21_LogVolData):
mountopts=self.fsopts)
if not fmt.type and not self.thin_pool:
raise KickstartValueError(formatErrorMsg(self.lineno,
msg=_("The \"%s\" filesystem type is not supported.") % ty))
msg=_("The \"%s\" file system type is not supported.") % ty))
add_fstab_swap = None
# If we were given a pre-existing LV to create a filesystem on, we need
# to verify it and its VG exists and then schedule a new format action
# to take place there. Also, we only support a subset of all the
@ -967,7 +967,7 @@ class LogVolData(commands.logvol.F21_LogVolData):
devicetree.registerAction(ActionCreateFormat(device, fmt))
if ty == "swap":
storage.addFstabSwap(device)
add_fstab_swap = device
else:
# If a previous device has claimed this mount point, delete the
# old one.
@ -1006,7 +1006,8 @@ class LogVolData(commands.logvol.F21_LogVolData):
else:
maxsize = None
request = storage.newLV(fmt=fmt,
try:
request = storage.newLV(fmt=fmt,
name=self.name,
parents=parents,
size=size,
@ -1016,15 +1017,26 @@ class LogVolData(commands.logvol.F21_LogVolData):
maxsize=maxsize,
percent=self.percent,
**pool_args)
except (StorageError, ValueError) as e:
raise KickstartValueError(formatErrorMsg(self.lineno, msg=e.message))
storage.createDevice(request)
if ty == "swap":
storage.addFstabSwap(request)
add_fstab_swap = request
if self.encrypted:
if self.passphrase and not storage.encryptionPassphrase:
storage.encryptionPassphrase = self.passphrase
# try to use the global passphrase if available
# XXX: we require the LV/part with --passphrase to be processed
# before this one to setup the storage.encryptionPassphrase
self.passphrase = self.passphrase or storage.encryptionPassphrase
if not self.passphrase:
raise KickstartValueError(formatErrorMsg(self.lineno,
msg=_("No passphrase given for encrypted LV")))
cert = getEscrowCertificate(storage.escrowCertificates, self.escrowcert)
if self.preexist:
luksformat = fmt
@ -1034,8 +1046,7 @@ class LogVolData(commands.logvol.F21_LogVolData):
add_backup_passphrase=self.backuppassphrase)
luksdev = LUKSDevice("luks%d" % storage.nextID,
fmt=luksformat,
parents=device,
min_luks_entropy=MIN_CREATE_ENTROPY)
parents=device)
else:
luksformat = request.format
request.format = getFormat("luks", passphrase=self.passphrase,
@ -1046,14 +1057,22 @@ class LogVolData(commands.logvol.F21_LogVolData):
luksdev = LUKSDevice("luks%d" % storage.nextID,
fmt=luksformat,
parents=request)
if ty == "swap":
# swap is on the LUKS device not on the LUKS' parent device,
# override the info here
add_fstab_swap = luksdev
storage.createDevice(luksdev)
if add_fstab_swap:
storage.addFstabSwap(add_fstab_swap)
class Logging(commands.logging.FC6_Logging):
def execute(self, *args):
if logger.tty_loglevel == DEFAULT_TTY_LEVEL:
if logger.loglevel == DEFAULT_LEVEL:
# not set from the command line
level = logLevelMap[self.level]
logger.tty_loglevel = level
logger.loglevel = level
setHandlersLevel(log, level)
setHandlersLevel(storage_log, level)
@ -1064,7 +1083,7 @@ class Logging(commands.logging.FC6_Logging):
remote_server = "%s:%s" %(self.host, self.port)
logger.updateRemote(remote_server)
class Network(commands.network.F21_Network):
class Network(commands.network.F22_Network):
def execute(self, storage, ksdata, instClass):
network.write_network_config(storage, ksdata, instClass, iutil.getSysroot())
@ -1092,7 +1111,7 @@ class PartitionData(commands.partition.F18_PartData):
storage.doAutoPart = False
if self.onbiosdisk != "":
for (disk, biosdisk) in storage.eddDict.iteritems():
for (disk, biosdisk) in storage.eddDict.items():
if "%x" % biosdisk == self.onbiosdisk:
self.disk = disk
break
@ -1108,7 +1127,7 @@ class PartitionData(commands.partition.F18_PartData):
self.mountpoint = ""
if self.recommended or self.hibernation:
disk_space = getAvailableDiskSpace(storage)
size = swap_lib.swapSuggestion(hibernation=self.hibernation, disk_space=disk_space)
size = autopart.swapSuggestion(hibernation=self.hibernation, disk_space=disk_space)
self.grow = False
# if people want to specify no mountpoint for some reason, let them
# this is really needed for pSeries boot partitions :(
@ -1156,7 +1175,7 @@ class PartitionData(commands.partition.F18_PartData):
if devicetree.getDeviceByName(kwargs["name"]):
raise KickstartValueError(formatErrorMsg(self.lineno,
msg=_("BTRFS partition \"%s\" is defined multiple times.") % kwargs["name"]))
msg=_("Btrfs partition \"%s\" is defined multiple times.") % kwargs["name"]))
if self.onPart:
ksdata.onPart[kwargs["name"]] = self.onPart
@ -1229,7 +1248,7 @@ class PartitionData(commands.partition.F18_PartData):
size=size)
if not kwargs["fmt"].type:
raise KickstartValueError(formatErrorMsg(self.lineno,
msg=_("The \"%s\" filesystem type is not supported.") % ty))
msg=_("The \"%s\" file system type is not supported.") % ty))
# If we were given a specific disk to create the partition on, verify
# that it exists first. If it doesn't exist, see if it exists with
@ -1275,6 +1294,7 @@ class PartitionData(commands.partition.F18_PartData):
kwargs["primary"] = self.primOnly
add_fstab_swap = None
# If we were given a pre-existing partition to create a filesystem on,
# we need to verify it exists and then schedule a new format action to
# take place there. Also, we only support a subset of all the options
@ -1297,11 +1317,14 @@ class PartitionData(commands.partition.F18_PartData):
devicetree.registerAction(ActionCreateFormat(device, kwargs["fmt"]))
if ty == "swap":
storage.addFstabSwap(device)
add_fstab_swap = device
# tmpfs mounts are not disks and don't occupy a disk partition,
# so handle them here
elif self.fstype == "tmpfs":
request = storage.newTmpFS(**kwargs)
try:
request = storage.newTmpFS(**kwargs)
except (StorageError, ValueError) as e:
raise KickstartValueError(formatErrorMsg(self.lineno, msg=e.message))
storage.createDevice(request)
else:
# If a previous device has claimed this mount point, delete the
@ -1313,15 +1336,28 @@ class PartitionData(commands.partition.F18_PartData):
except KeyError:
pass
request = storage.newPartition(**kwargs)
try:
request = storage.newPartition(**kwargs)
except (StorageError, ValueError) as e:
raise KickstartValueError(formatErrorMsg(self.lineno, msg=e.message))
storage.createDevice(request)
if ty == "swap":
storage.addFstabSwap(request)
add_fstab_swap = request
if self.encrypted:
if self.passphrase and not storage.encryptionPassphrase:
storage.encryptionPassphrase = self.passphrase
# try to use the global passphrase if available
# XXX: we require the LV/part with --passphrase to be processed
# before this one to setup the storage.encryptionPassphrase
self.passphrase = self.passphrase or storage.encryptionPassphrase
if not self.passphrase:
raise KickstartValueError(formatErrorMsg(self.lineno,
msg=_("No passphrase given for encrypted part")))
cert = getEscrowCertificate(storage.escrowCertificates, self.escrowcert)
if self.onPart:
luksformat = kwargs["fmt"]
@ -1343,8 +1379,17 @@ class PartitionData(commands.partition.F18_PartData):
luksdev = LUKSDevice("luks%d" % storage.nextID,
fmt=luksformat,
parents=request)
if ty == "swap":
# swap is on the LUKS device not on the LUKS' parent device,
# override the info here
add_fstab_swap = luksdev
storage.createDevice(luksdev)
if add_fstab_swap:
storage.addFstabSwap(add_fstab_swap)
class Raid(commands.raid.F20_Raid):
def execute(self, storage, ksdata, instClass):
for r in self.raidList:
@ -1384,7 +1429,7 @@ class RaidData(commands.raid.F18_RaidData):
if devicetree.getDeviceByName(kwargs["name"]):
raise KickstartValueError(formatErrorMsg(self.lineno,
msg=_("BTRFS partition \"%s\" is defined multiple times.") % kwargs["name"]))
msg=_("Btrfs partition \"%s\" is defined multiple times.") % kwargs["name"]))
self.mountpoint = ""
else:
@ -1449,7 +1494,7 @@ class RaidData(commands.raid.F18_RaidData):
mountopts=self.fsopts)
if not kwargs["fmt"].type:
raise KickstartValueError(formatErrorMsg(self.lineno,
msg=_("The \"%s\" filesystem type is not supported.") % ty))
msg=_("The \"%s\" file system type is not supported.") % ty))
kwargs["name"] = devicename
kwargs["level"] = self.level
@ -1485,8 +1530,8 @@ class RaidData(commands.raid.F18_RaidData):
try:
request = storage.newMDArray(**kwargs)
except ValueError as e:
raise KickstartValueError(formatErrorMsg(self.lineno, msg=str(e)))
except (StorageError, ValueError) as e:
raise KickstartValueError(formatErrorMsg(self.lineno, msg=e.message))
storage.createDevice(request)
@ -1515,7 +1560,7 @@ class RaidData(commands.raid.F18_RaidData):
parents=request)
storage.createDevice(luksdev)
class RepoData(commands.repo.F15_RepoData):
class RepoData(commands.repo.F21_RepoData):
def __init__(self, *args, **kwargs):
""" Add enabled kwarg
@ -1524,7 +1569,7 @@ class RepoData(commands.repo.F15_RepoData):
"""
self.enabled = kwargs.pop("enabled", True)
commands.repo.F15_RepoData.__init__(self, *args, **kwargs)
commands.repo.F21_RepoData.__init__(self, *args, **kwargs)
class RootPw(commands.rootpw.F18_RootPw):
def __init__(self, writePriority=100, *args, **kwargs):
@ -1574,6 +1619,11 @@ class Services(commands.services.FC6_Services):
iutil.execInSysroot("systemctl", ["enable", svc])
class SshKey(commands.sshkey.F22_SshKey):
def execute(self, storage, ksdata, instClass, users):
for usr in self.sshUserList:
users.setUserSshKey(usr.username, usr.key)
class Timezone(commands.timezone.F18_Timezone):
def __init__(self, *args):
commands.timezone.F18_Timezone.__init__(self, *args)
@ -1637,9 +1687,9 @@ class Timezone(commands.timezone.F18_Timezone):
# write out NTP configuration (if set)
if not self.nontp and self.ntpservers:
chronyd_conf_path = os.path.normpath(iutil.getSysroot() + ntp.NTP_CONFIG_FILE)
pools, servers = ntp.internal_to_pools_and_servers(self.ntpservers)
try:
ntp.save_servers_to_config(self.ntpservers,
conf_file_path=chronyd_conf_path)
ntp.save_servers_to_config(pools, servers, conf_file_path=chronyd_conf_path)
except ntp.NTPconfigError as ntperr:
log.warning("Failed to save NTP configuration: %s", ntperr)
@ -1702,10 +1752,10 @@ class VolGroupData(commands.volgroup.F21_VolGroupData):
if self.pesize == 0:
# default PE size requested -- we use blivet's default in KiB
self.pesize = LVM_PE_SIZE.convertTo(spec="KiB")
self.pesize = LVM_PE_SIZE.convertTo(KiB)
pesize = Size("%d KiB" % self.pesize)
possible_extents = getPossiblePhysicalExtents()
possible_extents = LVMVolumeGroupDevice.get_supported_pe_sizes()
if pesize not in possible_extents:
raise KickstartValueError(formatErrorMsg(self.lineno,
msg=_("Volume group given physical extent size of \"%(extentSize)s\", but must be one of:\n%(validExtentSizes)s.") %
@ -1725,9 +1775,12 @@ class VolGroupData(commands.volgroup.F21_VolGroupData):
raise KickstartValueError(formatErrorMsg(self.lineno,
msg=_("The volume group name \"%s\" is already in use.") % self.vgname))
else:
request = storage.newVG(parents=pvs,
try:
request = storage.newVG(parents=pvs,
name=self.vgname,
peSize=pesize)
except (StorageError, ValueError) as e:
raise KickstartValueError(formatErrorMsg(self.lineno, msg=e.message))
storage.createDevice(request)
if self.reserved_space:
@ -1777,8 +1830,64 @@ class Upgrade(commands.upgrade.F20_Upgrade):
def parse(self, *args):
log.error("The upgrade kickstart command is no longer supported. Upgrade functionality is provided through fedup.")
sys.stderr.write(_("The upgrade kickstart command is no longer supported. Upgrade functionality is provided through fedup."))
iutil.ipmi_report(IPMI_ABORTED)
sys.exit(1)
###
### %anaconda Section
###
class AnacondaSectionHandler(BaseHandler):
"""A handler for only the anaconda ection's commands."""
commandMap = {
"pwpolicy": F22_PwPolicy
}
dataMap = {
"PwPolicyData": F22_PwPolicyData
}
def __init__(self):
BaseHandler.__init__(self, mapping=self.commandMap, dataMapping=self.dataMap)
def __str__(self):
"""Return the %anaconda section"""
retval = ""
lst = sorted(self._writeOrder.keys())
for prio in lst:
for obj in self._writeOrder[prio]:
retval += str(obj)
if retval:
retval = "\n%anaconda\n" + retval + "%end\n"
return retval
class AnacondaSection(Section):
"""A section for anaconda specific commands."""
sectionOpen = "%anaconda"
def __init__(self, *args, **kwargs):
Section.__init__(self, *args, **kwargs)
self.cmdno = 0
def handleLine(self, line):
if not self.handler:
return
self.cmdno += 1
args = shlex.split(line, comments=True)
self.handler.currentCmd = args[0]
self.handler.currentLine = self.cmdno
return self.handler.dispatcher(args, self.cmdno)
def handleHeader(self, lineno, args):
"""Process the arguments to the %anaconda header."""
Section.handleHeader(self, lineno, args)
def finalize(self):
"""Let %anaconda know no additional data will come."""
Section.finalize(self)
###
### HANDLERS
###
@ -1814,6 +1923,7 @@ commandMap = {
"rootpw": RootPw,
"selinux": SELinux,
"services": Services,
"sshkey": SshKey,
"skipx": SkipX,
"timezone": Timezone,
"upgrade": Upgrade,
@ -1870,8 +1980,11 @@ class AnacondaKSHandler(superclass):
# Prepare the final structures for 3rd party addons
self.addons = AddonRegistry(addons)
# The %anaconda section uses its own handler for a limited set of commands
self.anaconda = AnacondaSectionHandler()
def __str__(self):
return superclass.__str__(self) + "\n" + str(self.addons)
return superclass.__str__(self) + "\n" + str(self.addons) + str(self.anaconda)
class AnacondaPreParser(KickstartParser):
# A subclass of KickstartParser that only looks for %pre scripts and
@ -1889,6 +2002,7 @@ class AnacondaPreParser(KickstartParser):
self.registerSection(NullSection(self.handler, sectionOpen="%traceback"))
self.registerSection(NullSection(self.handler, sectionOpen="%packages"))
self.registerSection(NullSection(self.handler, sectionOpen="%addon"))
self.registerSection(NullSection(self.handler.anaconda, sectionOpen="%anaconda"))
class AnacondaKSParser(KickstartParser):
@ -1909,6 +2023,7 @@ class AnacondaKSParser(KickstartParser):
self.registerSection(TracebackScriptSection(self.handler, dataObj=self.scriptClass))
self.registerSection(PackageSection(self.handler))
self.registerSection(AddonSection(self.handler))
self.registerSection(AnacondaSection(self.handler.anaconda))
def preScriptPass(f):
# The first pass through kickstart file processing - look for %pre scripts
@ -1922,6 +2037,7 @@ def preScriptPass(f):
# We do not have an interface here yet, so we cannot use our error
# handling callback.
print(e)
iutil.ipmi_report(IPMI_ABORTED)
sys.exit(1)
# run %pre scripts
@ -1949,6 +2065,7 @@ def parseKickstart(f):
# We do not have an interface here yet, so we cannot use our error
# handling callback.
print(e)
iutil.ipmi_report(IPMI_ABORTED)
sys.exit(1)
return handler
@ -1970,22 +2087,17 @@ def appendPostScripts(ksdata):
ksparser.readKickstartFromString(scripts, reset=False)
def runPostScripts(scripts):
postScripts = filter (lambda s: s.type == KS_SCRIPT_POST, scripts)
postScripts = [s for s in scripts if s.type == KS_SCRIPT_POST]
if len(postScripts) == 0:
return
# Remove environment variables that cause problems for %post scripts.
for var in ["LIBUSER_CONF"]:
if var in os.environ:
del(os.environ[var])
log.info("Running kickstart %%post script(s)")
map (lambda s: s.run(iutil.getSysroot()), postScripts)
log.info("All kickstart %%post script(s) have been run")
def runPreScripts(scripts):
preScripts = filter (lambda s: s.type == KS_SCRIPT_PRE, scripts)
preScripts = [s for s in scripts if s.type == KS_SCRIPT_PRE]
if len(preScripts) == 0:
return

@ -29,7 +29,7 @@ import glob
from collections import namedtuple
from pyanaconda import constants
from pyanaconda.iutil import upcase_first_letter
from pyanaconda.iutil import upcase_first_letter, setenv
import logging
log = logging.getLogger("anaconda")
@ -152,7 +152,7 @@ def find_best_locale_match(locale, langcodes):
if not locale_parts or not langcode_parts:
return score
for part, part_score in score_map.iteritems():
for part, part_score in score_map.items():
if locale_parts[part] and langcode_parts[part]:
if locale_parts[part] == langcode_parts[part]:
# match
@ -187,6 +187,9 @@ def setup_locale(locale, lang=None):
ksdata.lang object (if given). DOES NOT PERFORM ANY CHECKS OF THE GIVEN
LOCALE.
$LANG must be set by the caller in order to set the language used by gettext.
Doing this in a thread-safe way is up to the caller.
:param locale: locale to setup
:type locale: str
:param lang: ksdata.lang object or None
@ -198,7 +201,7 @@ def setup_locale(locale, lang=None):
if lang:
lang.lang = locale
os.environ["LANG"] = locale
setenv("LANG", locale)
locale_mod.setlocale(locale_mod.LC_ALL, locale)
def get_english_name(locale):
@ -395,7 +398,7 @@ def get_xlated_timezone(tz_spec_part):
territoryIdQuery=parts.get("territory", ""),
scriptIdQuery=parts.get("script", ""))
return xlated.encode("utf-8")
return xlated
def write_language_configuration(lang, root):
"""
@ -420,6 +423,8 @@ def load_firmware_language(lang):
information in the given ksdata.lang object and sets the $LANG environment
variable.
This method must be run before any other threads are started.
:param lang: ksdata.lang object
:return: None
:rtype: None
@ -471,6 +476,8 @@ def load_firmware_language(lang):
log.debug("Using UEFI PlatformLang '%s' ('%s') as our language.", d, locales[0])
setup_locale(locales[0], lang)
os.environ["LANG"] = locales[0] # pylint: disable=environment-modify
_DateFieldSpec = namedtuple("DateFieldSpec", ["format", "suffix"])
def resolve_date_format(year, month, day, fail_safe=True):

@ -71,11 +71,6 @@ def setup_ifcfg_log():
logger = logging.getLogger("ifcfg")
logger.setLevel(logging.DEBUG)
anaconda_log.logger.addFileHandler(ifcfgLogFile, logger, logging.DEBUG)
if os.access("/dev/tty3", os.W_OK):
anaconda_log.logger.addFileHandler("/dev/tty3", logger,
anaconda_log.DEFAULT_TTY_LEVEL,
anaconda_log.TTY_FORMAT,
autoLevel=True)
anaconda_log.logger.forwardToSyslog(logger)
ifcfglog = logging.getLogger("ifcfg")
@ -100,13 +95,13 @@ def sanityCheckHostname(hostname):
"""
if not hostname:
return (False, _("Hostname cannot be None or an empty string."))
return (False, _("Host name cannot be None or an empty string."))
if len(hostname) > 255:
return (False, _("Hostname must be 255 or fewer characters in length."))
return (False, _("Host name must be 255 or fewer characters in length."))
if not (re.match('^' + HOSTNAME_PATTERN_WITHOUT_ANCHORS + '$', hostname)):
return (False, _("Hostnames can only contain the characters 'a-z', "
return (False, _("Host names can only contain the characters 'a-z', "
"'A-Z', '0-9', '-', or '.', parts between periods "
"must contain something and cannot start or end with "
"'-'."))
@ -363,6 +358,11 @@ def dracutBootArguments(devname, ifcfg, storage_ipaddr, hostname=None):
if hwaddr:
netargs.add("ifname=%s:%s" % (devname, hwaddr.lower()))
if ifcfg.get("TYPE") == "Team" or ifcfg.get("DEVICETYPE") == "Team":
slaves = get_team_slaves([devname, ifcfg.get("UUID")])
netargs.add("team=%s:%s" % (devname,
",".join(dev for dev, _cfg in slaves)))
nettype = ifcfg.get("NETTYPE")
subchannels = ifcfg.get("SUBCHANNELS")
if blivet.arch.isS390() and nettype and subchannels:
@ -463,31 +463,8 @@ def add_connection_for_ksdata(networkdata, devname):
values.append(['bond', 'interface-name', devname, 's'])
options = bond_options_ksdata_to_dbus(networkdata.bondopts)
values.append(['bond', 'options', options, 'a{ss}'])
for _i, slave in enumerate(networkdata.bondslaves.split(","), 1):
#slave_name = "%s slave %d" % (devname, i)
slave_name = slave
svalues = []
suuid = str(uuid4())
svalues.append(['connection', 'uuid', suuid, 's'])
svalues.append(['connection', 'id', slave_name, 's'])
svalues.append(['connection', 'slave-type', 'bond', 's'])
svalues.append(['connection', 'master', devname, 's'])
svalues.append(['connection', 'type', '802-3-ethernet', 's'])
mac = nm.nm_device_perm_hwaddress(slave)
mac = [int(b, 16) for b in mac.split(":")]
svalues.append(['802-3-ethernet', 'mac-address', mac, 'ay'])
# disconnect slaves
if networkdata.activate:
nm.nm_disconnect_device(slave)
# remove ifcfg file
ifcfg_path = find_ifcfg_file_of_device(slave)
if ifcfg_path and os.access(ifcfg_path, os.R_OK):
os.unlink(ifcfg_path)
nm.nm_add_connection(svalues)
for slave in networkdata.bondslaves.split(","):
suuid = _add_slave_connection('bond', slave, devname, networkdata.activate)
added_connections.append((suuid, slave))
dev_spec = None
# type "team"
@ -496,33 +473,9 @@ def add_connection_for_ksdata(networkdata, devname):
values.append(['connection', 'id', devname, 's'])
values.append(['team', 'interface-name', devname, 's'])
values.append(['team', 'config', networkdata.teamconfig, 's'])
for _i, (slave, cfg) in enumerate(networkdata.teamslaves):
# assume ethernet, TODO: infiniband, wifi, vlan
#slave_name = "%s slave %d" % (devname, i)
slave_name = slave
svalues = []
suuid = str(uuid4())
svalues.append(['connection', 'uuid', suuid, 's'])
svalues.append(['connection', 'id', slave_name, 's'])
svalues.append(['connection', 'slave-type', 'team', 's'])
svalues.append(['connection', 'master', devname, 's'])
svalues.append(['connection', 'type', '802-3-ethernet', 's'])
mac = nm.nm_device_perm_hwaddress(slave)
mac = [int(b, 16) for b in mac.split(":")]
svalues.append(['802-3-ethernet', 'mac-address', mac, 'ay'])
svalues.append(['team-port', 'config', cfg, 's'])
# disconnect slaves
if networkdata.activate:
nm.nm_disconnect_device(slave)
# remove ifcfg file
ifcfg_path = find_ifcfg_file_of_device(slave)
if ifcfg_path and os.access(ifcfg_path, os.R_OK):
os.unlink(ifcfg_path)
nm.nm_add_connection(svalues)
for (slave, cfg) in networkdata.teamslaves:
values = [['team-port', 'config', cfg, 's']]
suuid = _add_slave_connection('team', slave, devname, networkdata.activate, values)
added_connections.append((suuid, slave))
dev_spec = None
# type "vlan"
@ -533,19 +486,84 @@ def add_connection_for_ksdata(networkdata, devname):
values.append(['vlan', 'interface-name', devname, 's'])
values.append(['vlan', 'id', int(networkdata.vlanid), 'u'])
dev_spec = None
# type "bridge"
elif networkdata.bridgeslaves:
# bridge connection is autoactivated
values.append(['connection', 'type', 'bridge', 's'])
values.append(['connection', 'id', devname, 's'])
values.append(['bridge', 'interface-name', devname, 's'])
for opt in networkdata.bridgeopts.split(","):
key, _sep, value = opt.partition("=")
if key == "stp":
if value == "yes":
values.append(['bridge', key, True, 'b'])
elif value == "no":
values.append(['bridge', key, False, 'b'])
continue
try:
value = int(value)
except ValueError:
log.error("Invalid bridge option %s", opt)
continue
values.append(['bridge', key, int(value), 'u'])
for slave in networkdata.bridgeslaves.split(","):
suuid = _add_slave_connection('bridge', slave, devname, networkdata.activate)
added_connections.append((suuid, slave))
dev_spec = None
# type "802-3-ethernet"
else:
mac = nm.nm_device_perm_hwaddress(devname)
if flags.cmdline.get("ifname", "").upper() == "{0}:{1}".format(devname, mac).upper():
mac = [int(b, 16) for b in mac.split(":")]
values.append(['802-3-ethernet', 'mac-address', mac, 'ay'])
else:
values.append(['802-3-ethernet', 'name', devname, 's'])
values.append(['connection', 'type', '802-3-ethernet', 's'])
values.append(['connection', 'id', devname, 's'])
mac = nm.nm_device_perm_hwaddress(devname)
mac = [int(b, 16) for b in mac.split(":")]
values.append(['802-3-ethernet', 'mac-address', mac, 'ay'])
values.append(['connection', 'interface-name', devname, 's'])
dev_spec = devname
nm.nm_add_connection(values)
try:
nm.nm_add_connection(values)
except nm.BondOptionsError as e:
log.error(e)
return []
added_connections.insert(0, (con_uuid, dev_spec))
return added_connections
def _add_slave_connection(slave_type, slave, master, activate, values=None):
values = values or []
#slave_name = "%s slave %d" % (devname, slave_idx)
slave_name = slave
values = []
suuid = str(uuid4())
# assume ethernet, TODO: infiniband, wifi, vlan
values.append(['connection', 'uuid', suuid, 's'])
values.append(['connection', 'id', slave_name, 's'])
values.append(['connection', 'slave-type', slave_type, 's'])
values.append(['connection', 'master', master, 's'])
values.append(['connection', 'type', '802-3-ethernet', 's'])
mac = nm.nm_device_perm_hwaddress(slave)
mac = [int(b, 16) for b in mac.split(":")]
values.append(['802-3-ethernet', 'mac-address', mac, 'ay'])
# disconnect slaves
if activate:
try:
nm.nm_disconnect_device(slave)
except nm.DeviceNotActiveError:
pass
# remove ifcfg file
ifcfg_path = find_ifcfg_file_of_device(slave)
if ifcfg_path and os.access(ifcfg_path, os.R_OK):
os.unlink(ifcfg_path)
nm.nm_add_connection(values)
return suuid
def ksdata_from_ifcfg(devname, uuid=None):
if devname not in nm.nm_devices():
@ -586,6 +604,8 @@ def ksdata_from_ifcfg(devname, uuid=None):
nd.device = devname
elif nm.nm_device_type_is_team(devname):
nd.device = devname
elif nm.nm_device_type_is_bridge(devname):
nd.device = devname
elif nm.nm_device_type_is_vlan(devname):
if devname != default_ks_vlan_interface_name(nd.device, nd.vlanid):
nd.interfacename = devname
@ -604,6 +624,9 @@ def ifcfg_to_ksdata(ifcfg, devname):
# no network command for team slaves
if ifcfg.get("TEAM_MASTER"):
return None
# no network command for bridge slaves
if ifcfg.get("BRIDGE"):
return None
# ipv4 and ipv6
if ifcfg.get("ONBOOT") and ifcfg.get("ONBOOT" ) == "no":
@ -682,7 +705,7 @@ def ifcfg_to_ksdata(ifcfg, devname):
# bonding
# FIXME: dracut has only BOND_OPTS
if ifcfg.get("BONDING_MASTER") == "yes" or ifcfg.get("TYPE") == "Bond":
slaves = get_bond_slaves_from_ifcfgs([devname, ifcfg.get("UUID")])
slaves = get_slaves_from_ifcfgs("MASTER", [devname, ifcfg.get("UUID")])
if slaves:
kwargs["bondslaves"] = ",".join(slaves)
bondopts = ifcfg.get("BONDING_OPTS")
@ -697,6 +720,20 @@ def ifcfg_to_ksdata(ifcfg, devname):
kwargs["device"] = ifcfg.get("PHYSDEV")
kwargs["vlanid"] = ifcfg.get("VLAN_ID")
# bridging
if ifcfg.get("TYPE") == "Bridge":
slaves = get_slaves_from_ifcfgs("BRIDGE", [devname, ifcfg.get("UUID")])
if slaves:
kwargs["bridgeslaves"] = ",".join(slaves)
bridgeopts = ifcfg.get("BRIDGING_OPTS").replace('_', '-').split()
if ifcfg.get("STP"):
bridgeopts.append("%s=%s" % ("stp", ifcfg.get("STP")))
if ifcfg.get("DELAY"):
bridgeopts.append("%s=%s" % ("forward-delay", ifcfg.get("DELAY")))
if bridgeopts:
kwargs["bridgeopts"] = ",".join(bridgeopts)
# pylint: disable=no-member
nd = handler.NetworkData(**kwargs)
@ -734,6 +771,8 @@ def find_ifcfg_file_of_device(devname, root_path=""):
ifcfg_path = find_ifcfg_file([("DEVICE", devname)])
elif nm.nm_device_type_is_vlan(devname):
ifcfg_path = find_ifcfg_file([("DEVICE", devname)])
elif nm.nm_device_type_is_bridge(devname):
ifcfg_path = find_ifcfg_file([("DEVICE", devname)])
elif nm.nm_device_type_is_ethernet(devname):
try:
hwaddr = nm.nm_device_perm_hwaddress(devname)
@ -750,6 +789,10 @@ def find_ifcfg_file_of_device(devname, root_path=""):
ifcfg_path = find_ifcfg_file([("HWADDR", hwaddr_check),
("TEAM_MASTER", nonempty)],
root_path)
if not ifcfg_path:
ifcfg_path = find_ifcfg_file([("HWADDR", hwaddr_check),
("BRIDGE", nonempty)],
root_path)
if not ifcfg_path:
ifcfg_path = find_ifcfg_file([("HWADDR", hwaddr_check)], root_path)
if not ifcfg_path:
@ -772,9 +815,10 @@ def find_ifcfg_file(values, root_path=""):
return filepath
return None
def get_bond_slaves_from_ifcfgs(master_specs):
"""List of slave device names of master specified by master_specs.
def get_slaves_from_ifcfgs(master_option, master_specs):
"""List of slaves of master specified by master_specs in master_option.
master_option is ifcfg option containing spec of master
master_specs is a list containing device name of master (dracut)
and/or master's connection uuid
"""
@ -783,7 +827,7 @@ def get_bond_slaves_from_ifcfgs(master_specs):
for filepath in _ifcfg_files(netscriptsDir):
ifcfg = IfcfgFile(filepath)
ifcfg.read()
master = ifcfg.get("MASTER")
master = ifcfg.get(master_option)
if master in master_specs:
device = ifcfg.get("DEVICE")
if device:
@ -894,6 +938,15 @@ def copyDhclientConfFiles(destPath):
copyFileToPath(dhclientfile, destPath)
def ks_spec_to_device_name(ksspec=""):
"""
Find the first network device which matches the kickstart specification.
Will not match derived types such as bonds and vlans.
:param ksspec: kickstart-specified device name
:returns: a string naming a physical device, or "" meaning none matched
:rtype: str
"""
bootif_mac = ''
if ksspec == 'bootif' and "BOOTIF" in flags.cmdline:
bootif_mac = flags.cmdline["BOOTIF"][3:].replace("-", ":").upper()
@ -901,7 +954,7 @@ def ks_spec_to_device_name(ksspec=""):
# "eth0"
if ksspec == dev:
break
# "link"
# "link" - match the first device which is plugged (has a carrier)
elif ksspec == 'link':
try:
link_up = nm.nm_device_carrier(dev)
@ -936,7 +989,7 @@ def ks_spec_to_device_name(ksspec=""):
def set_hostname(hn):
if can_touch_runtime_system("set hostname", touch_live=True):
log.info("setting installation environment hostname to %s", hn)
log.info("setting installation environment host name to %s", hn)
iutil.execWithRedirect("hostnamectl", ["set-hostname", hn])
def write_hostname(rootpath, ksdata, overwrite=False):
@ -1040,7 +1093,7 @@ def update_hostname_data(ksdata, hostname=None):
if not hostname:
# Default to 'dom0' in Qubes
hostname = 'dom0'
log.debug("updating hostname %s", hostname)
log.debug("updating host name %s", hostname)
hostname_found = False
for nd in ksdata.network.network:
if nd.hostname:
@ -1051,13 +1104,20 @@ def update_hostname_data(ksdata, hostname=None):
ksdata.network.network.append(nd)
def get_device_name(network_data):
"""
Find the first network device which matches the kickstart specification.
:param network_data: A pykickstart NetworkData object
:returns: a string naming a physical device, or "" meaning none matched
:rtype: str
"""
ksspec = network_data.device or flags.cmdline.get('ksdevice') or ""
dev_name = ks_spec_to_device_name(ksspec)
if not dev_name:
return ""
if dev_name not in nm.nm_devices():
if not any((network_data.vlanid, network_data.bondslaves, network_data.teamslaves)):
if not any((network_data.vlanid, network_data.bondslaves, network_data.teamslaves, network_data.bridgeslaves)):
return ""
if network_data.vlanid:
network_data.parent = dev_name
@ -1134,9 +1194,9 @@ def apply_kickstart(ksdata):
for con_uuid, dev_name in added_connections:
try:
nm.nm_activate_device_connection(dev_name, con_uuid)
except nm.UnknownConnectionError:
log.warning("network: pre kickstart: can't activate connection %s on %s",
con_uuid, dev_name)
except (nm.UnknownConnectionError, nm.UnknownDeviceError) as e:
log.warning("network: pre kickstart: can't activate connection %s on %s: %s",
con_uuid, dev_name, e)
return applied_devices
def networkInitialize(ksdata):
@ -1181,7 +1241,7 @@ def _get_ntp_servers_from_dhcp(ksdata):
hostname = socket.gethostbyaddr(server_address)[0]
except socket.error:
# getting hostname failed, just use the address returned from DHCP
log.debug("getting NTP server hostname failed for address: %s",
log.debug("getting NTP server host name failed for address: %s",
server_address)
hostname = server_address
hostnames.append(hostname)
@ -1301,10 +1361,14 @@ def status_message():
msg = _("Team%(interface_name)s (%(list_of_slaves)s) connected") \
% {"interface_name": devname, \
"list_of_slaves": ",".join(slaves[devname])}
elif nm.nm_device_type_is_bridge(devname):
msg = _("Bridge%(interface_name)s (%(list_of_slaves)s) connected") \
% {"interface_name": devname, \
"list_of_slaves": ",".join(slaves[devname])}
elif nm.nm_device_type_is_vlan(devname):
parent = nm.nm_device_setting_value(devname, "vlan", "parent")
vlanid = nm.nm_device_setting_value(devname, "vlan", "id")
msg = _("Vlan %(interface_name)s (%(parent_device)s, ID %(vlanid)s) connected") \
msg = _("VLAN %(interface_name)s (%(parent_device)s, ID %(vlanid)s) connected") \
% {"interface_name": devname, "parent_device": parent, "vlanid": vlanid}
elif len(nonslaves) > 1:
devlist = []
@ -1317,6 +1381,8 @@ def status_message():
devlist.append("%s (%s)" % (devname, ",".join(slaves[devname])))
elif nm.nm_device_type_is_team(devname):
devlist.append("%s (%s)" % (devname, ",".join(slaves[devname])))
elif nm.nm_device_type_is_bridge(devname):
devlist.append("%s (%s)" % (devname, ",".join(slaves[devname])))
elif nm.nm_device_type_is_vlan(devname):
devlist.append("%s" % devname)
msg = _("Connected: %(list_of_interface_names)s") \
@ -1362,3 +1428,6 @@ def update_onboot_value(devname, value, ksdata):
if nd.device == devname:
nd.onboot = True
break
def is_using_team_device():
return any(nm.nm_device_type_is_team(d) for d in nm.nm_devices())

@ -21,10 +21,8 @@
from gi.repository import Gio, GLib
from gi.repository import NetworkManager
import IPy
import struct
import socket
import re
import logging
log = logging.getLogger("anaconda")
@ -77,6 +75,15 @@ class UnknownConnectionError(Exception):
def __str__(self):
return self.__repr__()
class AddConnectionError(Exception):
"""Connection is not available for the device"""
def __str__(self):
return self.__repr__()
# bug #1039006
class BondOptionsError(AddConnectionError):
pass
def _get_proxy(bus_type=Gio.BusType.SYSTEM,
proxy_flags=Gio.DBusProxyFlags.NONE,
info=None,
@ -312,6 +319,18 @@ def nm_device_type_is_team(name):
"""
return nm_device_type(name) == NetworkManager.DeviceType.TEAM
def nm_device_type_is_bridge(name):
"""Is the type of device bridge?
:param name: name of device
:type name: str
:return: True if type of device is BRIDGE, False otherwise
:rtype: bool
:raise UnknownDeviceError: if device is not found
:raise PropertyNotFoundError: if property is not found
"""
return nm_device_type(name) == NetworkManager.DeviceType.BRIDGE
def nm_device_type_is_vlan(name):
"""Is the type of device vlan?
@ -483,7 +502,8 @@ def nm_device_ip_config(name, version=4):
addr_list = []
for addr, prefix, gateway in addresses:
# TODO - look for a library function (could have used IPy but byte order!)
# NOTE: There is IPy for python2, ipaddress for python3 but
# byte order of dbus value would need to be switched
if version == 4:
addr_str = nm_dbus_int_to_ipv4(addr)
gateway_str = nm_dbus_int_to_ipv4(gateway)
@ -584,6 +604,10 @@ def _device_settings(name):
settings = _find_settings(name, 'bond', 'interface-name')
elif devtype == NetworkManager.DeviceType.VLAN:
settings = _find_settings(name, 'vlan', 'interface-name')
if not settings:
# connections generated by NM from iBFT
_parent, _sep, vlanid = name.partition(".")
settings = _find_settings(int(vlanid), 'vlan', 'id')
else:
settings = _find_settings(name, 'connection', 'interface-name')
if not settings:
@ -796,6 +820,8 @@ def nm_activate_device_connection(dev_name, con_uuid):
raise UnmanagedDeviceError(dev_name, e)
elif "org.freedesktop.NetworkManager.UnknownConnection" in e.message:
raise UnknownConnectionError(dev_name, e)
if "org.freedesktop.NetworkManager.UnknownDevice" in e.message:
raise UnknownDeviceError(dev_name, e)
raise
def nm_add_connection(values):
@ -823,7 +849,12 @@ def nm_add_connection(values):
proxy = _get_proxy(object_path="/org/freedesktop/NetworkManager/Settings",
interface_name="org.freedesktop.NetworkManager.Settings")
connection = proxy.AddConnection('(a{sa{sv}})', settings)
try:
connection = proxy.AddConnection('(a{sa{sv}})', settings)
except GLib.GError as e:
if "bond.options: invalid option" in e.message:
raise BondOptionsError(e)
raise
return connection
def nm_delete_connection(uuid):
@ -973,7 +1004,7 @@ def nm_ipv6_to_dbus_ay(address):
:return: address in format 'ay' for NM dbus setting
:rtype: list of bytes
"""
return [int(byte, 16) for byte in re.findall('.{1,2}', IPy.IP(address).strFullsize().replace(':', ''))]
return [int(byte) for byte in bytearray(socket.inet_pton(socket.AF_INET6, address))]
def nm_ipv4_to_dbus_int(address):
"""Convert ipv4 address from string to int for dbus (switched endianess).

@ -23,6 +23,8 @@ Module facilitating the work with NTP servers and NTP daemon's configuration
"""
from __future__ import division
import re
import os
import tempfile
@ -39,7 +41,10 @@ NTP_CONFIG_FILE = "/etc/chrony.conf"
#example line:
#server 0.fedora.pool.ntp.org iburst
SRV_LINE_REGEXP = re.compile(r"^\s*server\s*([-a-zA-Z.0-9]+)\s*[a-zA-Z]+\s*$")
SRV_LINE_REGEXP = re.compile(r"^\s*(server|pool)\s*([-a-zA-Z.0-9]+)\s*[a-zA-Z]+\s*$")
#treat pools as four servers with the same name
SERVERS_PER_POOL = 4
class NTPconfigError(Exception):
"""Exception class for NTP related problems"""
@ -72,6 +77,31 @@ def ntp_server_working(server):
return True
def pools_servers_to_internal(pools, servers):
ret = []
for pool in pools:
ret.extend(SERVERS_PER_POOL * [pool])
ret.extend(servers)
return ret
def internal_to_pools_and_servers(pools_servers):
server_nums = dict()
pools = []
servers = []
for item in pools_servers:
server_nums[item] = server_nums.get(item, 0) + 1
for item in server_nums.keys():
if server_nums[item] >= SERVERS_PER_POOL:
pools.extend((server_nums[item] // SERVERS_PER_POOL) * [item])
servers.extend((server_nums[item] % SERVERS_PER_POOL) * [item])
else:
servers.extend(server_nums[item] * [item])
return (pools, servers)
def get_servers_from_config(conf_file_path=NTP_CONFIG_FILE,
srv_regexp=SRV_LINE_REGEXP):
"""
@ -83,29 +113,34 @@ def get_servers_from_config(conf_file_path=NTP_CONFIG_FILE,
"""
ret = list()
pools = list()
servers = list()
try:
with open(conf_file_path, "r") as conf_file:
for line in conf_file:
match = srv_regexp.match(line)
if match:
ret.append(match.group(1))
if match.group(1) == "pool":
pools.append(match.group(2))
else:
servers.append(match.group(2))
except IOError as ioerr:
msg = "Cannot open config file %s for reading (%s)" % (conf_file_path,
ioerr.strerror)
raise NTPconfigError(msg)
return ret
return (pools, servers)
def save_servers_to_config(servers, conf_file_path=NTP_CONFIG_FILE,
def save_servers_to_config(pools, servers, conf_file_path=NTP_CONFIG_FILE,
srv_regexp=SRV_LINE_REGEXP, out_file_path=None):
"""
Replaces the servers defined in the chronyd's configuration file with
the given ones. If the out_file is not None, then it is used for the
Replaces the pools and servers defined in the chronyd's configuration file
with the given ones. If the out_file is not None, then it is used for the
resulting config.
:type pools: iterable
:type servers: iterable
:param out_file_path: path to the file used for the resulting config
@ -141,7 +176,10 @@ def save_servers_to_config(servers, conf_file_path=NTP_CONFIG_FILE,
#write info about the origin of the following lines
new_conf_file.write(heading)
#write new servers
#write new servers and pools
for pool in pools:
new_conf_file.write("pool " + pool + " iburst\n")
for server in servers:
new_conf_file.write("server " + server + " iburst\n")

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save