This commit adds AnacondaOptionParser, which is an OptionParser subclass that parses kernel boot arguments in addition to commandline arguments. It looks for boot args that match the parser's long options. You can also provide extra boot args to look for, e.g.: op.add_option("--kickstart", "ks", dest="ksfile") This patch also switches anaconda over to the new parser, which means we no longer need loader to parse boot args for us. NOTE BEHAVIOR CHANGE: anaconda now expects (but does not yet require) all boot arguments to begin with "inst.", and will emit warning log messages if you use the old forms (repo=..., ks=..., etc.). --- anaconda | 19 ++++-- pyanaconda/anaconda_optparse.py | 116 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 129 insertions(+), 6 deletions(-) create mode 100644 pyanaconda/anaconda_optparse.py diff --git a/anaconda b/anaconda index 149be4a4..f236397 100755 --- a/anaconda +++ b/anaconda @@ -31,7 +31,6 @@ # This toplevel file is a little messy at the moment... import atexit, sys, os, re, time, subprocess -from optparse import OptionParser from tempfile import mkstemp # keep up with process ID of the window manager if we start it @@ -190,7 +189,9 @@ def getAnacondaVersion(): return _isys.getAnacondaVersion() def parseOptions(argv = None): - op = OptionParser(version="%prog " + getAnacondaVersion()) + from pyanaconda.anaconda_optparse import AnacondaOptionParser + op = AnacondaOptionParser(version="%prog " + getAnacondaVersion(), + bootarg_prefix="inst.", require_prefix=False) # Interface op.add_option("-C", "--cmdline", dest="display_mode", action="store_const", const="c", @@ -208,9 +209,9 @@ def parseOptions(argv = None): # Method of operation op.add_option("--autostep", action="store_true", default=False) op.add_option("-d", "--debug", dest="debug", action="store_true", default=False) - op.add_option("--kickstart", dest="ksfile") + op.add_option("--kickstart", "ks", dest="ksfile") op.add_option("--rescue", dest="rescue", action="store_true", default=False) - op.add_option("--targetarch", dest="targetArch", nargs=1, type="string") + op.add_option("--targetarch", "rpmarch", dest="targetArch", type="string") op.add_option("-m", "--method", dest="method", default=None) op.add_option("--repo", dest="method", default=None) @@ -256,11 +257,13 @@ def parseOptions(argv = None): # Miscellaneous op.add_option("--module", action="append", default=[]) op.add_option("--nomount", dest="rescue_nomount", action="store_true", default=False) + # XXX updates should go away because dracut's gonna handle it op.add_option("--updates", dest="updateSrc", action="store", type="string") op.add_option("--dlabel", action="store_true", default=False) op.add_option("--image", action="append", dest="images", default=[]) - return op.parse_args(argv) + (opts, args) = op.parse_args(argv) + return (opts, args, op.deprecated_bootargs) def setupPythonPath(): sys.path.append('/usr/share/system-config-date') @@ -585,7 +588,7 @@ if __name__ == "__main__": setupPythonUpdates() # do this early so we can set flags before initializing logging - (opts, args) = parseOptions() + (opts, args, depr) = parseOptions() from pyanaconda.flags import flags if opts.images: flags.imageInstall = True @@ -604,6 +607,10 @@ if __name__ == "__main__": log.info("%s %s" % (sys.argv[0], getAnacondaVersion())) + for arg in depr: + stdoutLog.warn("Boot argument '%s' is deprecated. " + "In the future, use 'inst.%s'.", arg, arg) + # pull this in to get product name and versioning from pyanaconda import product from pyanaconda.constants import ROOT_PATH diff --git a/pyanaconda/anaconda_optparse.py b/pyanaconda/anaconda_optparse.py new file mode 100644 index 0000000..87b1fcc --- /dev/null +++ b/pyanaconda/anaconda_optparse.py @@ -0,0 +1,116 @@ +# +# anaconda_optparse.py: option parsing for anaconda (CLI and boot args) +# +# Copyright (C) 2012 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/>. +# +# Authors: +# Will Woods <wwoods@xxxxxxxxxx> + +from flags import BootArgs +from optparse import OptionParser, OptionConflictError + +class AnacondaOptionParser(OptionParser): + ''' + Subclass of OptionParser that also examines boot arguments. + + If the "bootarg_prefix" keyword argument is set, it's assumed that all + bootargs will start with that prefix. + + "require_prefix" is a bool: + False: accept the argument with or without the prefix. + True: ignore the argument without the prefix. (default) + ''' + def __init__(self, *args, **kwargs): + self._boot_arg = dict() + self.bootarg_prefix = kwargs.pop("bootarg_prefix","") + self.require_prefix = kwargs.pop("require_prefix",True) + OptionParser.__init__(self, *args, **kwargs) + + def add_option(self, *args, **kwargs): + ''' + Add a new option - like OptionParser.add_option. + + The long options will be added to the list of boot args, unless + the keyword argument 'bootarg' is set to False. + + Positional arguments that don't start with '-' are considered extra + boot args to look for. + + NOTE: conflict_handler is currently ignored for boot args - they will + always raise OptionConflictError if they conflict. + ''' + flags = [a for a in args if a.startswith('-')] + bootargs = [a for a in args if not a.startswith('-')] + do_bootarg = kwargs.pop("bootarg", True) + option = OptionParser.add_option(self, *flags, **kwargs) + bootargs += [flag[2:] for flag in option._long_opts] + if do_bootarg: + for b in bootargs: + if b in self._boot_arg: + raise OptionConflictError( + "conflicting bootopt string: %s" % b, option) + else: + self._boot_arg[b] = option + return option + + def _get_bootarg_option(self, arg): + '''Find the correct Option for a given bootarg (if one exists)''' + option = self._boot_arg.get(arg) + prefixed_option = self._boot_arg.get(self.bootarg_prefix+arg) + if not self.bootarg_prefix: return option + if self.require_prefix: return prefixed_option + # even if the prefix isn't required, we still prefer it + if prefixed_option: return prefixed_option + # deprecated option found? make a note of it. + if option: self.deprecated_bootargs.append(arg) + return option + + def parse_boot_cmdline(self, cmdline, values=None): + ''' + Parse the boot cmdline and set appropriate values according to + the options set by add_option. + + cmdline can be given as a string (to be parsed by BootArgs), or a + dict (or any object with .iteritems()) of {bootarg:value} pairs. + + If cmdline is None, the cmdline data will be whatever BootArgs reads + by default (/proc/cmdline, /run/initramfs/etc/cmdline, /etc/cmdline). + + If an option requires a value but the boot arg doesn't provide one, + we'll quietly not set anything. + ''' + if cmdline is None or type(cmdline) is str: + bootargs = BootArgs(cmdline) + else: + bootargs = cmdline + self.deprecated_bootargs = [] + for arg, val in bootargs.iteritems(): + option = self._get_bootarg_option(arg) + if option is None: continue + if option.takes_value() and val is None: + continue # TODO: emit a warning or something there? + option.process(arg, val, values, self) + return values + + def parse_args(self, args=None, values=None, cmdline=None): + ''' + Like OptionParser.parse_args(), but also parses the boot cmdline. + (see parse_boot_cmdline for details on that process.) + Commandline arguments will override boot arguments. + ''' + if values is None: values = self.get_default_values() + v = self.parse_boot_cmdline(cmdline, values) + return OptionParser.parse_args(self, args, v) -- 1.7.7.6 _______________________________________________ Anaconda-devel-list mailing list Anaconda-devel-list@xxxxxxxxxx https://www.redhat.com/mailman/listinfo/anaconda-devel-list