summaryrefslogtreecommitdiff
path: root/scripts/lib/wic/3rdparty/pykickstart/parser.py
diff options
context:
space:
mode:
Diffstat (limited to 'scripts/lib/wic/3rdparty/pykickstart/parser.py')
-rw-r--r--scripts/lib/wic/3rdparty/pykickstart/parser.py619
1 files changed, 0 insertions, 619 deletions
diff --git a/scripts/lib/wic/3rdparty/pykickstart/parser.py b/scripts/lib/wic/3rdparty/pykickstart/parser.py
deleted file mode 100644
index 9c9674bf73..0000000000
--- a/scripts/lib/wic/3rdparty/pykickstart/parser.py
+++ /dev/null
@@ -1,619 +0,0 @@
-#
-# parser.py: Kickstart file parser.
-#
-# Chris Lumens <clumens@redhat.com>
-#
-# Copyright 2005, 2006, 2007, 2008, 2011 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. 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.
-#
-"""
-Main kickstart file processing module.
-
-This module exports several important classes:
-
- Script - Representation of a single %pre, %post, or %traceback script.
-
- Packages - Representation of the %packages section.
-
- KickstartParser - The kickstart file parser state machine.
-"""
-
-from collections import Iterator
-import os
-import shlex
-import sys
-import tempfile
-from copy import copy
-from optparse import *
-
-import constants
-from errors import KickstartError, KickstartParseError, KickstartValueError, formatErrorMsg
-from ko import KickstartObject
-from sections import *
-import version
-
-import gettext
-_ = lambda x: gettext.ldgettext("pykickstart", x)
-
-STATE_END = "end"
-STATE_COMMANDS = "commands"
-
-ver = version.DEVEL
-
-
-class PutBackIterator(Iterator):
- def __init__(self, iterable):
- self._iterable = iter(iterable)
- self._buf = None
-
- def __iter__(self):
- return self
-
- def put(self, s):
- self._buf = s
-
- def next(self):
- if self._buf:
- retval = self._buf
- self._buf = None
- return retval
- else:
- return self._iterable.next()
-
-###
-### SCRIPT HANDLING
-###
-class Script(KickstartObject):
- """A class representing a single kickstart script. If functionality beyond
- just a data representation is needed (for example, a run method in
- anaconda), Script may be subclassed. Although a run method is not
- provided, most of the attributes of Script have to do with running the
- script. Instances of Script are held in a list by the Version object.
- """
- def __init__(self, script, *args , **kwargs):
- """Create a new Script instance. Instance attributes:
-
- errorOnFail -- If execution of the script fails, should anaconda
- stop, display an error, and then reboot without
- running any other scripts?
- inChroot -- Does the script execute in anaconda's chroot
- environment or not?
- interp -- The program that should be used to interpret this
- script.
- lineno -- The line number this script starts on.
- logfile -- Where all messages from the script should be logged.
- script -- A string containing all the lines of the script.
- type -- The type of the script, which can be KS_SCRIPT_* from
- pykickstart.constants.
- """
- KickstartObject.__init__(self, *args, **kwargs)
- self.script = "".join(script)
-
- self.interp = kwargs.get("interp", "/bin/sh")
- self.inChroot = kwargs.get("inChroot", False)
- self.lineno = kwargs.get("lineno", None)
- self.logfile = kwargs.get("logfile", None)
- self.errorOnFail = kwargs.get("errorOnFail", False)
- self.type = kwargs.get("type", constants.KS_SCRIPT_PRE)
-
- def __str__(self):
- """Return a string formatted for output to a kickstart file."""
- retval = ""
-
- if self.type == constants.KS_SCRIPT_PRE:
- retval += '\n%pre'
- elif self.type == constants.KS_SCRIPT_POST:
- retval += '\n%post'
- elif self.type == constants.KS_SCRIPT_TRACEBACK:
- retval += '\n%traceback'
-
- if self.interp != "/bin/sh" and self.interp != "":
- retval += " --interpreter=%s" % self.interp
- if self.type == constants.KS_SCRIPT_POST and not self.inChroot:
- retval += " --nochroot"
- if self.logfile != None:
- retval += " --logfile %s" % self.logfile
- if self.errorOnFail:
- retval += " --erroronfail"
-
- if self.script.endswith("\n"):
- if ver >= version.F8:
- return retval + "\n%s%%end\n" % self.script
- else:
- return retval + "\n%s\n" % self.script
- else:
- if ver >= version.F8:
- return retval + "\n%s\n%%end\n" % self.script
- else:
- return retval + "\n%s\n" % self.script
-
-
-##
-## PACKAGE HANDLING
-##
-class Group:
- """A class representing a single group in the %packages section."""
- def __init__(self, name="", include=constants.GROUP_DEFAULT):
- """Create a new Group instance. Instance attributes:
-
- name -- The group's identifier
- include -- The level of how much of the group should be included.
- Values can be GROUP_* from pykickstart.constants.
- """
- self.name = name
- self.include = include
-
- def __str__(self):
- """Return a string formatted for output to a kickstart file."""
- if self.include == constants.GROUP_REQUIRED:
- return "@%s --nodefaults" % self.name
- elif self.include == constants.GROUP_ALL:
- return "@%s --optional" % self.name
- else:
- return "@%s" % self.name
-
- def __cmp__(self, other):
- if self.name < other.name:
- return -1
- elif self.name > other.name:
- return 1
- return 0
-
-class Packages(KickstartObject):
- """A class representing the %packages section of the kickstart file."""
- def __init__(self, *args, **kwargs):
- """Create a new Packages instance. Instance attributes:
-
- addBase -- Should the Base group be installed even if it is
- not specified?
- default -- Should the default package set be selected?
- excludedList -- A list of all the packages marked for exclusion in
- the %packages section, without the leading minus
- symbol.
- excludeDocs -- Should documentation in each package be excluded?
- groupList -- A list of Group objects representing all the groups
- specified in the %packages section. Names will be
- stripped of the leading @ symbol.
- excludedGroupList -- A list of Group objects representing all the
- groups specified for removal in the %packages
- section. Names will be stripped of the leading
- -@ symbols.
- handleMissing -- If unknown packages are specified in the %packages
- section, should it be ignored or not? Values can
- be KS_MISSING_* from pykickstart.constants.
- packageList -- A list of all the packages specified in the
- %packages section.
- instLangs -- A list of languages to install.
- """
- KickstartObject.__init__(self, *args, **kwargs)
-
- self.addBase = True
- self.default = False
- self.excludedList = []
- self.excludedGroupList = []
- self.excludeDocs = False
- self.groupList = []
- self.handleMissing = constants.KS_MISSING_PROMPT
- self.packageList = []
- self.instLangs = None
-
- def __str__(self):
- """Return a string formatted for output to a kickstart file."""
- pkgs = ""
-
- if not self.default:
- grps = self.groupList
- grps.sort()
- for grp in grps:
- pkgs += "%s\n" % grp.__str__()
-
- p = self.packageList
- p.sort()
- for pkg in p:
- pkgs += "%s\n" % pkg
-
- grps = self.excludedGroupList
- grps.sort()
- for grp in grps:
- pkgs += "-%s\n" % grp.__str__()
-
- p = self.excludedList
- p.sort()
- for pkg in p:
- pkgs += "-%s\n" % pkg
-
- if pkgs == "":
- return ""
-
- retval = "\n%packages"
-
- if self.default:
- retval += " --default"
- if self.excludeDocs:
- retval += " --excludedocs"
- if not self.addBase:
- retval += " --nobase"
- if self.handleMissing == constants.KS_MISSING_IGNORE:
- retval += " --ignoremissing"
- if self.instLangs:
- retval += " --instLangs=%s" % self.instLangs
-
- if ver >= version.F8:
- return retval + "\n" + pkgs + "\n%end\n"
- else:
- return retval + "\n" + pkgs + "\n"
-
- def _processGroup (self, line):
- op = OptionParser()
- op.add_option("--nodefaults", action="store_true", default=False)
- op.add_option("--optional", action="store_true", default=False)
-
- (opts, extra) = op.parse_args(args=line.split())
-
- if opts.nodefaults and opts.optional:
- raise KickstartValueError, _("Group cannot specify both --nodefaults and --optional")
-
- # If the group name has spaces in it, we have to put it back together
- # now.
- grp = " ".join(extra)
-
- if opts.nodefaults:
- self.groupList.append(Group(name=grp, include=constants.GROUP_REQUIRED))
- elif opts.optional:
- self.groupList.append(Group(name=grp, include=constants.GROUP_ALL))
- else:
- self.groupList.append(Group(name=grp, include=constants.GROUP_DEFAULT))
-
- def add (self, pkgList):
- """Given a list of lines from the input file, strip off any leading
- symbols and add the result to the appropriate list.
- """
- existingExcludedSet = set(self.excludedList)
- existingPackageSet = set(self.packageList)
- newExcludedSet = set()
- newPackageSet = set()
-
- excludedGroupList = []
-
- for pkg in pkgList:
- stripped = pkg.strip()
-
- if stripped[0] == "@":
- self._processGroup(stripped[1:])
- elif stripped[0] == "-":
- if stripped[1] == "@":
- excludedGroupList.append(Group(name=stripped[2:]))
- else:
- newExcludedSet.add(stripped[1:])
- else:
- newPackageSet.add(stripped)
-
- # Groups have to be excluded in two different ways (note: can't use
- # sets here because we have to store objects):
- excludedGroupNames = map(lambda g: g.name, excludedGroupList)
-
- # First, an excluded group may be cancelling out a previously given
- # one. This is often the case when using %include. So there we should
- # just remove the group from the list.
- self.groupList = filter(lambda g: g.name not in excludedGroupNames, self.groupList)
-
- # Second, the package list could have included globs which are not
- # processed by pykickstart. In that case we need to preserve a list of
- # excluded groups so whatever tool doing package/group installation can
- # take appropriate action.
- self.excludedGroupList.extend(excludedGroupList)
-
- existingPackageSet = (existingPackageSet - newExcludedSet) | newPackageSet
- existingExcludedSet = (existingExcludedSet - existingPackageSet) | newExcludedSet
-
- self.packageList = list(existingPackageSet)
- self.excludedList = list(existingExcludedSet)
-
-
-###
-### PARSER
-###
-class KickstartParser:
- """The kickstart file parser class as represented by a basic state
- machine. To create a specialized parser, make a subclass and override
- any of the methods you care about. Methods that don't need to do
- anything may just pass. However, _stateMachine should never be
- overridden.
- """
- def __init__ (self, handler, followIncludes=True, errorsAreFatal=True,
- missingIncludeIsFatal=True):
- """Create a new KickstartParser instance. Instance attributes:
-
- errorsAreFatal -- Should errors cause processing to halt, or
- just print a message to the screen? This
- is most useful for writing syntax checkers
- that may want to continue after an error is
- encountered.
- followIncludes -- If %include is seen, should the included
- file be checked as well or skipped?
- handler -- An instance of a BaseHandler subclass. If
- None, the input file will still be parsed
- but no data will be saved and no commands
- will be executed.
- missingIncludeIsFatal -- Should missing include files be fatal, even
- if errorsAreFatal is False?
- """
- self.errorsAreFatal = errorsAreFatal
- self.followIncludes = followIncludes
- self.handler = handler
- self.currentdir = {}
- self.missingIncludeIsFatal = missingIncludeIsFatal
-
- self._state = STATE_COMMANDS
- self._includeDepth = 0
- self._line = ""
-
- self.version = self.handler.version
-
- global ver
- ver = self.version
-
- self._sections = {}
- self.setupSections()
-
- def _reset(self):
- """Reset the internal variables of the state machine for a new kickstart file."""
- self._state = STATE_COMMANDS
- self._includeDepth = 0
-
- def getSection(self, s):
- """Return a reference to the requested section (s must start with '%'s),
- or raise KeyError if not found.
- """
- return self._sections[s]
-
- def handleCommand (self, lineno, args):
- """Given the list of command and arguments, call the Version's
- dispatcher method to handle the command. Returns the command or
- data object returned by the dispatcher. This method may be
- overridden in a subclass if necessary.
- """
- if self.handler:
- self.handler.currentCmd = args[0]
- self.handler.currentLine = self._line
- retval = self.handler.dispatcher(args, lineno)
-
- return retval
-
- def registerSection(self, obj):
- """Given an instance of a Section subclass, register the new section
- with the parser. Calling this method means the parser will
- recognize your new section and dispatch into the given object to
- handle it.
- """
- if not obj.sectionOpen:
- raise TypeError, "no sectionOpen given for section %s" % obj
-
- if not obj.sectionOpen.startswith("%"):
- raise TypeError, "section %s tag does not start with a %%" % obj.sectionOpen
-
- self._sections[obj.sectionOpen] = obj
-
- def _finalize(self, obj):
- """Called at the close of a kickstart section to take any required
- actions. Internally, this is used to add scripts once we have the
- whole body read.
- """
- obj.finalize()
- self._state = STATE_COMMANDS
-
- def _handleSpecialComments(self, line):
- """Kickstart recognizes a couple special comments."""
- if self._state != STATE_COMMANDS:
- return
-
- # Save the platform for s-c-kickstart.
- if line[:10] == "#platform=":
- self.handler.platform = self._line[11:]
-
- def _readSection(self, lineIter, lineno):
- obj = self._sections[self._state]
-
- while True:
- try:
- line = lineIter.next()
- if line == "":
- # This section ends at the end of the file.
- if self.version >= version.F8:
- raise KickstartParseError, formatErrorMsg(lineno, msg=_("Section does not end with %%end."))
-
- self._finalize(obj)
- except StopIteration:
- break
-
- lineno += 1
-
- # Throw away blank lines and comments, unless the section wants all
- # lines.
- if self._isBlankOrComment(line) and not obj.allLines:
- continue
-
- if line.startswith("%"):
- args = shlex.split(line)
-
- if args and args[0] == "%end":
- # This is a properly terminated section.
- self._finalize(obj)
- break
- elif args and args[0] == "%ksappend":
- continue
- elif args and (self._validState(args[0]) or args[0] in ["%include", "%ksappend"]):
- # This is an unterminated section.
- if self.version >= version.F8:
- raise KickstartParseError, formatErrorMsg(lineno, msg=_("Section does not end with %%end."))
-
- # Finish up. We do not process the header here because
- # kicking back out to STATE_COMMANDS will ensure that happens.
- lineIter.put(line)
- lineno -= 1
- self._finalize(obj)
- break
- else:
- # This is just a line within a section. Pass it off to whatever
- # section handles it.
- obj.handleLine(line)
-
- return lineno
-
- def _validState(self, st):
- """Is the given section tag one that has been registered with the parser?"""
- return st in self._sections.keys()
-
- def _tryFunc(self, fn):
- """Call the provided function (which doesn't take any arguments) and
- do the appropriate error handling. If errorsAreFatal is False, this
- function will just print the exception and keep going.
- """
- try:
- fn()
- except Exception, msg:
- if self.errorsAreFatal:
- raise
- else:
- print msg
-
- def _isBlankOrComment(self, line):
- return line.isspace() or line == "" or line.lstrip()[0] == '#'
-
- def _stateMachine(self, lineIter):
- # For error reporting.
- lineno = 0
-
- while True:
- # Get the next line out of the file, quitting if this is the last line.
- try:
- self._line = lineIter.next()
- if self._line == "":
- break
- except StopIteration:
- break
-
- lineno += 1
-
- # Eliminate blank lines, whitespace-only lines, and comments.
- if self._isBlankOrComment(self._line):
- self._handleSpecialComments(self._line)
- continue
-
- # Remove any end-of-line comments.
- sanitized = self._line.split("#")[0]
-
- # Then split the line.
- args = shlex.split(sanitized.rstrip())
-
- if args[0] == "%include":
- # This case comes up primarily in ksvalidator.
- if not self.followIncludes:
- continue
-
- if len(args) == 1 or not args[1]:
- raise KickstartParseError, formatErrorMsg(lineno)
-
- self._includeDepth += 1
-
- try:
- self.readKickstart(args[1], reset=False)
- except KickstartError:
- # Handle the include file being provided over the
- # network in a %pre script. This case comes up in the
- # early parsing in anaconda.
- if self.missingIncludeIsFatal:
- raise
-
- self._includeDepth -= 1
- continue
-
- # Now on to the main event.
- if self._state == STATE_COMMANDS:
- if args[0] == "%ksappend":
- # This is handled by the preprocess* functions, so continue.
- continue
- elif args[0][0] == '%':
- # This is the beginning of a new section. Handle its header
- # here.
- newSection = args[0]
- if not self._validState(newSection):
- raise KickstartParseError, formatErrorMsg(lineno, msg=_("Unknown kickstart section: %s" % newSection))
-
- self._state = newSection
- obj = self._sections[self._state]
- self._tryFunc(lambda: obj.handleHeader(lineno, args))
-
- # This will handle all section processing, kicking us back
- # out to STATE_COMMANDS at the end with the current line
- # being the next section header, etc.
- lineno = self._readSection(lineIter, lineno)
- else:
- # This is a command in the command section. Dispatch to it.
- self._tryFunc(lambda: self.handleCommand(lineno, args))
- elif self._state == STATE_END:
- break
-
- def readKickstartFromString (self, s, reset=True):
- """Process a kickstart file, provided as the string str."""
- if reset:
- self._reset()
-
- # Add a "" to the end of the list so the string reader acts like the
- # file reader and we only get StopIteration when we're after the final
- # line of input.
- i = PutBackIterator(s.splitlines(True) + [""])
- self._stateMachine (i)
-
- def readKickstart(self, f, reset=True):
- """Process a kickstart file, given by the filename f."""
- if reset:
- self._reset()
-
- # an %include might not specify a full path. if we don't try to figure
- # out what the path should have been, then we're unable to find it
- # requiring full path specification, though, sucks. so let's make
- # the reading "smart" by keeping track of what the path is at each
- # include depth.
- if not os.path.exists(f):
- if self.currentdir.has_key(self._includeDepth - 1):
- if os.path.exists(os.path.join(self.currentdir[self._includeDepth - 1], f)):
- f = os.path.join(self.currentdir[self._includeDepth - 1], f)
-
- cd = os.path.dirname(f)
- if not cd.startswith("/"):
- cd = os.path.abspath(cd)
- self.currentdir[self._includeDepth] = cd
-
- try:
- s = file(f).read()
- except IOError, e:
- raise KickstartError, formatErrorMsg(0, msg=_("Unable to open input kickstart file: %s") % e.strerror)
-
- self.readKickstartFromString(s, reset=False)
-
- def setupSections(self):
- """Install the sections all kickstart files support. You may override
- this method in a subclass, but should avoid doing so unless you know
- what you're doing.
- """
- self._sections = {}
-
- # Install the sections all kickstart files support.
- self.registerSection(PreScriptSection(self.handler, dataObj=Script))
- self.registerSection(PostScriptSection(self.handler, dataObj=Script))
- self.registerSection(TracebackScriptSection(self.handler, dataObj=Script))
- self.registerSection(PackageSection(self.handler))