diff options
Diffstat (limited to 'scripts/lib/wic/3rdparty/pykickstart/parser.py')
-rw-r--r-- | scripts/lib/wic/3rdparty/pykickstart/parser.py | 619 |
1 files changed, 619 insertions, 0 deletions
diff --git a/scripts/lib/wic/3rdparty/pykickstart/parser.py b/scripts/lib/wic/3rdparty/pykickstart/parser.py new file mode 100644 index 0000000000..9c9674bf73 --- /dev/null +++ b/scripts/lib/wic/3rdparty/pykickstart/parser.py | |||
@@ -0,0 +1,619 @@ | |||
1 | # | ||
2 | # parser.py: Kickstart file parser. | ||
3 | # | ||
4 | # Chris Lumens <clumens@redhat.com> | ||
5 | # | ||
6 | # Copyright 2005, 2006, 2007, 2008, 2011 Red Hat, Inc. | ||
7 | # | ||
8 | # This copyrighted material is made available to anyone wishing to use, modify, | ||
9 | # copy, or redistribute it subject to the terms and conditions of the GNU | ||
10 | # General Public License v.2. This program is distributed in the hope that it | ||
11 | # will be useful, but WITHOUT ANY WARRANTY expressed or implied, including the | ||
12 | # implied warranties of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. | ||
13 | # See the GNU General Public License for more details. | ||
14 | # | ||
15 | # You should have received a copy of the GNU General Public License along with | ||
16 | # this program; if not, write to the Free Software Foundation, Inc., 51 | ||
17 | # Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. Any Red Hat | ||
18 | # trademarks that are incorporated in the source code or documentation are not | ||
19 | # subject to the GNU General Public License and may only be used or replicated | ||
20 | # with the express permission of Red Hat, Inc. | ||
21 | # | ||
22 | """ | ||
23 | Main kickstart file processing module. | ||
24 | |||
25 | This module exports several important classes: | ||
26 | |||
27 | Script - Representation of a single %pre, %post, or %traceback script. | ||
28 | |||
29 | Packages - Representation of the %packages section. | ||
30 | |||
31 | KickstartParser - The kickstart file parser state machine. | ||
32 | """ | ||
33 | |||
34 | from collections import Iterator | ||
35 | import os | ||
36 | import shlex | ||
37 | import sys | ||
38 | import tempfile | ||
39 | from copy import copy | ||
40 | from optparse import * | ||
41 | |||
42 | import constants | ||
43 | from errors import KickstartError, KickstartParseError, KickstartValueError, formatErrorMsg | ||
44 | from ko import KickstartObject | ||
45 | from sections import * | ||
46 | import version | ||
47 | |||
48 | import gettext | ||
49 | _ = lambda x: gettext.ldgettext("pykickstart", x) | ||
50 | |||
51 | STATE_END = "end" | ||
52 | STATE_COMMANDS = "commands" | ||
53 | |||
54 | ver = version.DEVEL | ||
55 | |||
56 | |||
57 | class PutBackIterator(Iterator): | ||
58 | def __init__(self, iterable): | ||
59 | self._iterable = iter(iterable) | ||
60 | self._buf = None | ||
61 | |||
62 | def __iter__(self): | ||
63 | return self | ||
64 | |||
65 | def put(self, s): | ||
66 | self._buf = s | ||
67 | |||
68 | def next(self): | ||
69 | if self._buf: | ||
70 | retval = self._buf | ||
71 | self._buf = None | ||
72 | return retval | ||
73 | else: | ||
74 | return self._iterable.next() | ||
75 | |||
76 | ### | ||
77 | ### SCRIPT HANDLING | ||
78 | ### | ||
79 | class Script(KickstartObject): | ||
80 | """A class representing a single kickstart script. If functionality beyond | ||
81 | just a data representation is needed (for example, a run method in | ||
82 | anaconda), Script may be subclassed. Although a run method is not | ||
83 | provided, most of the attributes of Script have to do with running the | ||
84 | script. Instances of Script are held in a list by the Version object. | ||
85 | """ | ||
86 | def __init__(self, script, *args , **kwargs): | ||
87 | """Create a new Script instance. Instance attributes: | ||
88 | |||
89 | errorOnFail -- If execution of the script fails, should anaconda | ||
90 | stop, display an error, and then reboot without | ||
91 | running any other scripts? | ||
92 | inChroot -- Does the script execute in anaconda's chroot | ||
93 | environment or not? | ||
94 | interp -- The program that should be used to interpret this | ||
95 | script. | ||
96 | lineno -- The line number this script starts on. | ||
97 | logfile -- Where all messages from the script should be logged. | ||
98 | script -- A string containing all the lines of the script. | ||
99 | type -- The type of the script, which can be KS_SCRIPT_* from | ||
100 | pykickstart.constants. | ||
101 | """ | ||
102 | KickstartObject.__init__(self, *args, **kwargs) | ||
103 | self.script = "".join(script) | ||
104 | |||
105 | self.interp = kwargs.get("interp", "/bin/sh") | ||
106 | self.inChroot = kwargs.get("inChroot", False) | ||
107 | self.lineno = kwargs.get("lineno", None) | ||
108 | self.logfile = kwargs.get("logfile", None) | ||
109 | self.errorOnFail = kwargs.get("errorOnFail", False) | ||
110 | self.type = kwargs.get("type", constants.KS_SCRIPT_PRE) | ||
111 | |||
112 | def __str__(self): | ||
113 | """Return a string formatted for output to a kickstart file.""" | ||
114 | retval = "" | ||
115 | |||
116 | if self.type == constants.KS_SCRIPT_PRE: | ||
117 | retval += '\n%pre' | ||
118 | elif self.type == constants.KS_SCRIPT_POST: | ||
119 | retval += '\n%post' | ||
120 | elif self.type == constants.KS_SCRIPT_TRACEBACK: | ||
121 | retval += '\n%traceback' | ||
122 | |||
123 | if self.interp != "/bin/sh" and self.interp != "": | ||
124 | retval += " --interpreter=%s" % self.interp | ||
125 | if self.type == constants.KS_SCRIPT_POST and not self.inChroot: | ||
126 | retval += " --nochroot" | ||
127 | if self.logfile != None: | ||
128 | retval += " --logfile %s" % self.logfile | ||
129 | if self.errorOnFail: | ||
130 | retval += " --erroronfail" | ||
131 | |||
132 | if self.script.endswith("\n"): | ||
133 | if ver >= version.F8: | ||
134 | return retval + "\n%s%%end\n" % self.script | ||
135 | else: | ||
136 | return retval + "\n%s\n" % self.script | ||
137 | else: | ||
138 | if ver >= version.F8: | ||
139 | return retval + "\n%s\n%%end\n" % self.script | ||
140 | else: | ||
141 | return retval + "\n%s\n" % self.script | ||
142 | |||
143 | |||
144 | ## | ||
145 | ## PACKAGE HANDLING | ||
146 | ## | ||
147 | class Group: | ||
148 | """A class representing a single group in the %packages section.""" | ||
149 | def __init__(self, name="", include=constants.GROUP_DEFAULT): | ||
150 | """Create a new Group instance. Instance attributes: | ||
151 | |||
152 | name -- The group's identifier | ||
153 | include -- The level of how much of the group should be included. | ||
154 | Values can be GROUP_* from pykickstart.constants. | ||
155 | """ | ||
156 | self.name = name | ||
157 | self.include = include | ||
158 | |||
159 | def __str__(self): | ||
160 | """Return a string formatted for output to a kickstart file.""" | ||
161 | if self.include == constants.GROUP_REQUIRED: | ||
162 | return "@%s --nodefaults" % self.name | ||
163 | elif self.include == constants.GROUP_ALL: | ||
164 | return "@%s --optional" % self.name | ||
165 | else: | ||
166 | return "@%s" % self.name | ||
167 | |||
168 | def __cmp__(self, other): | ||
169 | if self.name < other.name: | ||
170 | return -1 | ||
171 | elif self.name > other.name: | ||
172 | return 1 | ||
173 | return 0 | ||
174 | |||
175 | class Packages(KickstartObject): | ||
176 | """A class representing the %packages section of the kickstart file.""" | ||
177 | def __init__(self, *args, **kwargs): | ||
178 | """Create a new Packages instance. Instance attributes: | ||
179 | |||
180 | addBase -- Should the Base group be installed even if it is | ||
181 | not specified? | ||
182 | default -- Should the default package set be selected? | ||
183 | excludedList -- A list of all the packages marked for exclusion in | ||
184 | the %packages section, without the leading minus | ||
185 | symbol. | ||
186 | excludeDocs -- Should documentation in each package be excluded? | ||
187 | groupList -- A list of Group objects representing all the groups | ||
188 | specified in the %packages section. Names will be | ||
189 | stripped of the leading @ symbol. | ||
190 | excludedGroupList -- A list of Group objects representing all the | ||
191 | groups specified for removal in the %packages | ||
192 | section. Names will be stripped of the leading | ||
193 | -@ symbols. | ||
194 | handleMissing -- If unknown packages are specified in the %packages | ||
195 | section, should it be ignored or not? Values can | ||
196 | be KS_MISSING_* from pykickstart.constants. | ||
197 | packageList -- A list of all the packages specified in the | ||
198 | %packages section. | ||
199 | instLangs -- A list of languages to install. | ||
200 | """ | ||
201 | KickstartObject.__init__(self, *args, **kwargs) | ||
202 | |||
203 | self.addBase = True | ||
204 | self.default = False | ||
205 | self.excludedList = [] | ||
206 | self.excludedGroupList = [] | ||
207 | self.excludeDocs = False | ||
208 | self.groupList = [] | ||
209 | self.handleMissing = constants.KS_MISSING_PROMPT | ||
210 | self.packageList = [] | ||
211 | self.instLangs = None | ||
212 | |||
213 | def __str__(self): | ||
214 | """Return a string formatted for output to a kickstart file.""" | ||
215 | pkgs = "" | ||
216 | |||
217 | if not self.default: | ||
218 | grps = self.groupList | ||
219 | grps.sort() | ||
220 | for grp in grps: | ||
221 | pkgs += "%s\n" % grp.__str__() | ||
222 | |||
223 | p = self.packageList | ||
224 | p.sort() | ||
225 | for pkg in p: | ||
226 | pkgs += "%s\n" % pkg | ||
227 | |||
228 | grps = self.excludedGroupList | ||
229 | grps.sort() | ||
230 | for grp in grps: | ||
231 | pkgs += "-%s\n" % grp.__str__() | ||
232 | |||
233 | p = self.excludedList | ||
234 | p.sort() | ||
235 | for pkg in p: | ||
236 | pkgs += "-%s\n" % pkg | ||
237 | |||
238 | if pkgs == "": | ||
239 | return "" | ||
240 | |||
241 | retval = "\n%packages" | ||
242 | |||
243 | if self.default: | ||
244 | retval += " --default" | ||
245 | if self.excludeDocs: | ||
246 | retval += " --excludedocs" | ||
247 | if not self.addBase: | ||
248 | retval += " --nobase" | ||
249 | if self.handleMissing == constants.KS_MISSING_IGNORE: | ||
250 | retval += " --ignoremissing" | ||
251 | if self.instLangs: | ||
252 | retval += " --instLangs=%s" % self.instLangs | ||
253 | |||
254 | if ver >= version.F8: | ||
255 | return retval + "\n" + pkgs + "\n%end\n" | ||
256 | else: | ||
257 | return retval + "\n" + pkgs + "\n" | ||
258 | |||
259 | def _processGroup (self, line): | ||
260 | op = OptionParser() | ||
261 | op.add_option("--nodefaults", action="store_true", default=False) | ||
262 | op.add_option("--optional", action="store_true", default=False) | ||
263 | |||
264 | (opts, extra) = op.parse_args(args=line.split()) | ||
265 | |||
266 | if opts.nodefaults and opts.optional: | ||
267 | raise KickstartValueError, _("Group cannot specify both --nodefaults and --optional") | ||
268 | |||
269 | # If the group name has spaces in it, we have to put it back together | ||
270 | # now. | ||
271 | grp = " ".join(extra) | ||
272 | |||
273 | if opts.nodefaults: | ||
274 | self.groupList.append(Group(name=grp, include=constants.GROUP_REQUIRED)) | ||
275 | elif opts.optional: | ||
276 | self.groupList.append(Group(name=grp, include=constants.GROUP_ALL)) | ||
277 | else: | ||
278 | self.groupList.append(Group(name=grp, include=constants.GROUP_DEFAULT)) | ||
279 | |||
280 | def add (self, pkgList): | ||
281 | """Given a list of lines from the input file, strip off any leading | ||
282 | symbols and add the result to the appropriate list. | ||
283 | """ | ||
284 | existingExcludedSet = set(self.excludedList) | ||
285 | existingPackageSet = set(self.packageList) | ||
286 | newExcludedSet = set() | ||
287 | newPackageSet = set() | ||
288 | |||
289 | excludedGroupList = [] | ||
290 | |||
291 | for pkg in pkgList: | ||
292 | stripped = pkg.strip() | ||
293 | |||
294 | if stripped[0] == "@": | ||
295 | self._processGroup(stripped[1:]) | ||
296 | elif stripped[0] == "-": | ||
297 | if stripped[1] == "@": | ||
298 | excludedGroupList.append(Group(name=stripped[2:])) | ||
299 | else: | ||
300 | newExcludedSet.add(stripped[1:]) | ||
301 | else: | ||
302 | newPackageSet.add(stripped) | ||
303 | |||
304 | # Groups have to be excluded in two different ways (note: can't use | ||
305 | # sets here because we have to store objects): | ||
306 | excludedGroupNames = map(lambda g: g.name, excludedGroupList) | ||
307 | |||
308 | # First, an excluded group may be cancelling out a previously given | ||
309 | # one. This is often the case when using %include. So there we should | ||
310 | # just remove the group from the list. | ||
311 | self.groupList = filter(lambda g: g.name not in excludedGroupNames, self.groupList) | ||
312 | |||
313 | # Second, the package list could have included globs which are not | ||
314 | # processed by pykickstart. In that case we need to preserve a list of | ||
315 | # excluded groups so whatever tool doing package/group installation can | ||
316 | # take appropriate action. | ||
317 | self.excludedGroupList.extend(excludedGroupList) | ||
318 | |||
319 | existingPackageSet = (existingPackageSet - newExcludedSet) | newPackageSet | ||
320 | existingExcludedSet = (existingExcludedSet - existingPackageSet) | newExcludedSet | ||
321 | |||
322 | self.packageList = list(existingPackageSet) | ||
323 | self.excludedList = list(existingExcludedSet) | ||
324 | |||
325 | |||
326 | ### | ||
327 | ### PARSER | ||
328 | ### | ||
329 | class KickstartParser: | ||
330 | """The kickstart file parser class as represented by a basic state | ||
331 | machine. To create a specialized parser, make a subclass and override | ||
332 | any of the methods you care about. Methods that don't need to do | ||
333 | anything may just pass. However, _stateMachine should never be | ||
334 | overridden. | ||
335 | """ | ||
336 | def __init__ (self, handler, followIncludes=True, errorsAreFatal=True, | ||
337 | missingIncludeIsFatal=True): | ||
338 | """Create a new KickstartParser instance. Instance attributes: | ||
339 | |||
340 | errorsAreFatal -- Should errors cause processing to halt, or | ||
341 | just print a message to the screen? This | ||
342 | is most useful for writing syntax checkers | ||
343 | that may want to continue after an error is | ||
344 | encountered. | ||
345 | followIncludes -- If %include is seen, should the included | ||
346 | file be checked as well or skipped? | ||
347 | handler -- An instance of a BaseHandler subclass. If | ||
348 | None, the input file will still be parsed | ||
349 | but no data will be saved and no commands | ||
350 | will be executed. | ||
351 | missingIncludeIsFatal -- Should missing include files be fatal, even | ||
352 | if errorsAreFatal is False? | ||
353 | """ | ||
354 | self.errorsAreFatal = errorsAreFatal | ||
355 | self.followIncludes = followIncludes | ||
356 | self.handler = handler | ||
357 | self.currentdir = {} | ||
358 | self.missingIncludeIsFatal = missingIncludeIsFatal | ||
359 | |||
360 | self._state = STATE_COMMANDS | ||
361 | self._includeDepth = 0 | ||
362 | self._line = "" | ||
363 | |||
364 | self.version = self.handler.version | ||
365 | |||
366 | global ver | ||
367 | ver = self.version | ||
368 | |||
369 | self._sections = {} | ||
370 | self.setupSections() | ||
371 | |||
372 | def _reset(self): | ||
373 | """Reset the internal variables of the state machine for a new kickstart file.""" | ||
374 | self._state = STATE_COMMANDS | ||
375 | self._includeDepth = 0 | ||
376 | |||
377 | def getSection(self, s): | ||
378 | """Return a reference to the requested section (s must start with '%'s), | ||
379 | or raise KeyError if not found. | ||
380 | """ | ||
381 | return self._sections[s] | ||
382 | |||
383 | def handleCommand (self, lineno, args): | ||
384 | """Given the list of command and arguments, call the Version's | ||
385 | dispatcher method to handle the command. Returns the command or | ||
386 | data object returned by the dispatcher. This method may be | ||
387 | overridden in a subclass if necessary. | ||
388 | """ | ||
389 | if self.handler: | ||
390 | self.handler.currentCmd = args[0] | ||
391 | self.handler.currentLine = self._line | ||
392 | retval = self.handler.dispatcher(args, lineno) | ||
393 | |||
394 | return retval | ||
395 | |||
396 | def registerSection(self, obj): | ||
397 | """Given an instance of a Section subclass, register the new section | ||
398 | with the parser. Calling this method means the parser will | ||
399 | recognize your new section and dispatch into the given object to | ||
400 | handle it. | ||
401 | """ | ||
402 | if not obj.sectionOpen: | ||
403 | raise TypeError, "no sectionOpen given for section %s" % obj | ||
404 | |||
405 | if not obj.sectionOpen.startswith("%"): | ||
406 | raise TypeError, "section %s tag does not start with a %%" % obj.sectionOpen | ||
407 | |||
408 | self._sections[obj.sectionOpen] = obj | ||
409 | |||
410 | def _finalize(self, obj): | ||
411 | """Called at the close of a kickstart section to take any required | ||
412 | actions. Internally, this is used to add scripts once we have the | ||
413 | whole body read. | ||
414 | """ | ||
415 | obj.finalize() | ||
416 | self._state = STATE_COMMANDS | ||
417 | |||
418 | def _handleSpecialComments(self, line): | ||
419 | """Kickstart recognizes a couple special comments.""" | ||
420 | if self._state != STATE_COMMANDS: | ||
421 | return | ||
422 | |||
423 | # Save the platform for s-c-kickstart. | ||
424 | if line[:10] == "#platform=": | ||
425 | self.handler.platform = self._line[11:] | ||
426 | |||
427 | def _readSection(self, lineIter, lineno): | ||
428 | obj = self._sections[self._state] | ||
429 | |||
430 | while True: | ||
431 | try: | ||
432 | line = lineIter.next() | ||
433 | if line == "": | ||
434 | # This section ends at the end of the file. | ||
435 | if self.version >= version.F8: | ||
436 | raise KickstartParseError, formatErrorMsg(lineno, msg=_("Section does not end with %%end.")) | ||
437 | |||
438 | self._finalize(obj) | ||
439 | except StopIteration: | ||
440 | break | ||
441 | |||
442 | lineno += 1 | ||
443 | |||
444 | # Throw away blank lines and comments, unless the section wants all | ||
445 | # lines. | ||
446 | if self._isBlankOrComment(line) and not obj.allLines: | ||
447 | continue | ||
448 | |||
449 | if line.startswith("%"): | ||
450 | args = shlex.split(line) | ||
451 | |||
452 | if args and args[0] == "%end": | ||
453 | # This is a properly terminated section. | ||
454 | self._finalize(obj) | ||
455 | break | ||
456 | elif args and args[0] == "%ksappend": | ||
457 | continue | ||
458 | elif args and (self._validState(args[0]) or args[0] in ["%include", "%ksappend"]): | ||
459 | # This is an unterminated section. | ||
460 | if self.version >= version.F8: | ||
461 | raise KickstartParseError, formatErrorMsg(lineno, msg=_("Section does not end with %%end.")) | ||
462 | |||
463 | # Finish up. We do not process the header here because | ||
464 | # kicking back out to STATE_COMMANDS will ensure that happens. | ||
465 | lineIter.put(line) | ||
466 | lineno -= 1 | ||
467 | self._finalize(obj) | ||
468 | break | ||
469 | else: | ||
470 | # This is just a line within a section. Pass it off to whatever | ||
471 | # section handles it. | ||
472 | obj.handleLine(line) | ||
473 | |||
474 | return lineno | ||
475 | |||
476 | def _validState(self, st): | ||
477 | """Is the given section tag one that has been registered with the parser?""" | ||
478 | return st in self._sections.keys() | ||
479 | |||
480 | def _tryFunc(self, fn): | ||
481 | """Call the provided function (which doesn't take any arguments) and | ||
482 | do the appropriate error handling. If errorsAreFatal is False, this | ||
483 | function will just print the exception and keep going. | ||
484 | """ | ||
485 | try: | ||
486 | fn() | ||
487 | except Exception, msg: | ||
488 | if self.errorsAreFatal: | ||
489 | raise | ||
490 | else: | ||
491 | print msg | ||
492 | |||
493 | def _isBlankOrComment(self, line): | ||
494 | return line.isspace() or line == "" or line.lstrip()[0] == '#' | ||
495 | |||
496 | def _stateMachine(self, lineIter): | ||
497 | # For error reporting. | ||
498 | lineno = 0 | ||
499 | |||
500 | while True: | ||
501 | # Get the next line out of the file, quitting if this is the last line. | ||
502 | try: | ||
503 | self._line = lineIter.next() | ||
504 | if self._line == "": | ||
505 | break | ||
506 | except StopIteration: | ||
507 | break | ||
508 | |||
509 | lineno += 1 | ||
510 | |||
511 | # Eliminate blank lines, whitespace-only lines, and comments. | ||
512 | if self._isBlankOrComment(self._line): | ||
513 | self._handleSpecialComments(self._line) | ||
514 | continue | ||
515 | |||
516 | # Remove any end-of-line comments. | ||
517 | sanitized = self._line.split("#")[0] | ||
518 | |||
519 | # Then split the line. | ||
520 | args = shlex.split(sanitized.rstrip()) | ||
521 | |||
522 | if args[0] == "%include": | ||
523 | # This case comes up primarily in ksvalidator. | ||
524 | if not self.followIncludes: | ||
525 | continue | ||
526 | |||
527 | if len(args) == 1 or not args[1]: | ||
528 | raise KickstartParseError, formatErrorMsg(lineno) | ||
529 | |||
530 | self._includeDepth += 1 | ||
531 | |||
532 | try: | ||
533 | self.readKickstart(args[1], reset=False) | ||
534 | except KickstartError: | ||
535 | # Handle the include file being provided over the | ||
536 | # network in a %pre script. This case comes up in the | ||
537 | # early parsing in anaconda. | ||
538 | if self.missingIncludeIsFatal: | ||
539 | raise | ||
540 | |||
541 | self._includeDepth -= 1 | ||
542 | continue | ||
543 | |||
544 | # Now on to the main event. | ||
545 | if self._state == STATE_COMMANDS: | ||
546 | if args[0] == "%ksappend": | ||
547 | # This is handled by the preprocess* functions, so continue. | ||
548 | continue | ||
549 | elif args[0][0] == '%': | ||
550 | # This is the beginning of a new section. Handle its header | ||
551 | # here. | ||
552 | newSection = args[0] | ||
553 | if not self._validState(newSection): | ||
554 | raise KickstartParseError, formatErrorMsg(lineno, msg=_("Unknown kickstart section: %s" % newSection)) | ||
555 | |||
556 | self._state = newSection | ||
557 | obj = self._sections[self._state] | ||
558 | self._tryFunc(lambda: obj.handleHeader(lineno, args)) | ||
559 | |||
560 | # This will handle all section processing, kicking us back | ||
561 | # out to STATE_COMMANDS at the end with the current line | ||
562 | # being the next section header, etc. | ||
563 | lineno = self._readSection(lineIter, lineno) | ||
564 | else: | ||
565 | # This is a command in the command section. Dispatch to it. | ||
566 | self._tryFunc(lambda: self.handleCommand(lineno, args)) | ||
567 | elif self._state == STATE_END: | ||
568 | break | ||
569 | |||
570 | def readKickstartFromString (self, s, reset=True): | ||
571 | """Process a kickstart file, provided as the string str.""" | ||
572 | if reset: | ||
573 | self._reset() | ||
574 | |||
575 | # Add a "" to the end of the list so the string reader acts like the | ||
576 | # file reader and we only get StopIteration when we're after the final | ||
577 | # line of input. | ||
578 | i = PutBackIterator(s.splitlines(True) + [""]) | ||
579 | self._stateMachine (i) | ||
580 | |||
581 | def readKickstart(self, f, reset=True): | ||
582 | """Process a kickstart file, given by the filename f.""" | ||
583 | if reset: | ||
584 | self._reset() | ||
585 | |||
586 | # an %include might not specify a full path. if we don't try to figure | ||
587 | # out what the path should have been, then we're unable to find it | ||
588 | # requiring full path specification, though, sucks. so let's make | ||
589 | # the reading "smart" by keeping track of what the path is at each | ||
590 | # include depth. | ||
591 | if not os.path.exists(f): | ||
592 | if self.currentdir.has_key(self._includeDepth - 1): | ||
593 | if os.path.exists(os.path.join(self.currentdir[self._includeDepth - 1], f)): | ||
594 | f = os.path.join(self.currentdir[self._includeDepth - 1], f) | ||
595 | |||
596 | cd = os.path.dirname(f) | ||
597 | if not cd.startswith("/"): | ||
598 | cd = os.path.abspath(cd) | ||
599 | self.currentdir[self._includeDepth] = cd | ||
600 | |||
601 | try: | ||
602 | s = file(f).read() | ||
603 | except IOError, e: | ||
604 | raise KickstartError, formatErrorMsg(0, msg=_("Unable to open input kickstart file: %s") % e.strerror) | ||
605 | |||
606 | self.readKickstartFromString(s, reset=False) | ||
607 | |||
608 | def setupSections(self): | ||
609 | """Install the sections all kickstart files support. You may override | ||
610 | this method in a subclass, but should avoid doing so unless you know | ||
611 | what you're doing. | ||
612 | """ | ||
613 | self._sections = {} | ||
614 | |||
615 | # Install the sections all kickstart files support. | ||
616 | self.registerSection(PreScriptSection(self.handler, dataObj=Script)) | ||
617 | self.registerSection(PostScriptSection(self.handler, dataObj=Script)) | ||
618 | self.registerSection(TracebackScriptSection(self.handler, dataObj=Script)) | ||
619 | self.registerSection(PackageSection(self.handler)) | ||