Bump version for adding support for the logging command.
[pykickstart.git] / pykickstart / parser.py
blob40624f903a513ac95e584e282ef3e83980c701d7
2 # parser.py: Kickstart file parser.
4 # Chris Lumens <clumens@redhat.com>
6 # Copyright 2005, 2006 Red Hat, Inc.
8 # This software may be freely redistributed under the terms of the GNU
9 # general public license.
11 # You should have received a copy of the GNU General Public License
12 # along with this program; if not, write to the Free Software
13 # Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
16 import shlex
17 import sys
18 import string
19 import warnings
20 from copy import copy
21 from optparse import *
23 from constants import *
24 from data import *
26 from rhpl.translate import _
27 import rhpl.translate as translate
29 translate.textdomain("pykickstart")
31 STATE_END = 0
32 STATE_COMMANDS = 1
33 STATE_PACKAGES = 2
34 STATE_SCRIPT_HDR = 3
35 STATE_PRE = 4
36 STATE_POST = 5
37 STATE_TRACEBACK = 6
39 ###
40 ### ERROR HANDLING
41 ###
43 def formatErrorMsg(lineno, msg=""):
44 if msg != "":
45 return _("The following problem occurred on line %s of the kickstart file:\n\n%s\n") % (lineno, msg)
46 else:
47 return _("There was a problem reading from line %s of the kickstart file") % lineno
49 class KickstartError(Exception):
50 def __init__(self, val = ""):
51 Exception.__init__(self)
52 self.value = val
54 def __str__ (self):
55 return self.value
57 class KickstartParseError(KickstartError):
58 def __init__(self, msg):
59 KickstartError.__init__(self, msg)
61 def __str__(self):
62 return self.value
64 class KickstartValueError(KickstartError):
65 def __init__(self, msg):
66 KickstartError.__init__(self, msg)
68 def __str__ (self):
69 return self.value
71 ###
72 ### OPTION HANDLING
73 ###
75 # Specialized OptionParser, mainly to handle the MappableOption and to turn
76 # off help.
77 class KSOptionParser(OptionParser):
78 def exit(self, status=0, msg=None):
79 pass
81 def error(self, msg):
82 if self.lineno != None:
83 raise KickstartParseError, formatErrorMsg(self.lineno, msg=msg)
84 else:
85 raise KickstartParseError, msg
87 def keys(self):
88 retval = []
90 for opt in self.option_list:
91 if opt not in retval:
92 retval.append(opt.dest)
94 return retval
96 def _init_parsing_state (self):
97 OptionParser._init_parsing_state(self)
98 self.option_seen = {}
100 def check_values (self, values, args):
101 for option in self.option_list:
102 if (isinstance(option, Option) and option.required and \
103 not self.option_seen.has_key(option)):
104 raise KickstartValueError, formatErrorMsg(self.lineno, _("Option %s is required") % option)
105 elif isinstance(option, Option) and option.deprecated and \
106 self.option_seen.has_key(option):
107 warnings.warn(_("Ignoring deprecated option on line %s: The %s option has been deprecated and no longer has any effect. It may be removed from future releases, which will result in a fatal error from kickstart. Please modify your kickstart file to remove this option.") % (self.lineno, option), DeprecationWarning)
109 return (values, args)
111 def __init__(self, map={}, lineno=None):
112 self.map = map
113 self.lineno = lineno
114 OptionParser.__init__(self, option_class=KSOption,
115 add_help_option=False)
117 # Creates a new Option class that supports two new attributes:
118 # - required: any option with this attribute must be supplied or an exception
119 # is thrown
120 # - deprecated: any option with this attribute will cause a DeprecationWarning
121 # to be thrown if the option is used
122 # Also creates a new type:
123 # - ksboolean: support various kinds of boolean values on an option
124 # And two new actions:
125 # - map : allows you to define an opt -> val mapping such that dest gets val
126 # when opt is seen
127 # - map_extend: allows you to define an opt -> [val1, ... valn] mapping such
128 # that dest gets a list of vals built up when opt is seen
129 class KSOption (Option):
130 ATTRS = Option.ATTRS + ['deprecated', 'required']
131 ACTIONS = Option.ACTIONS + ("map", "map_extend",)
132 STORE_ACTIONS = Option.STORE_ACTIONS + ("map", "map_extend",)
134 TYPES = Option.TYPES + ("ksboolean",)
135 TYPE_CHECKER = copy(Option.TYPE_CHECKER)
137 def _check_required(self):
138 if self.required and not self.takes_value():
139 raise OptionError(_("Required flag set for option that doesn't take a value"), self)
141 def _check_ksboolean(option, opt, value):
142 if value.lower() in ("on", "yes", "true", "1"):
143 return True
144 elif value.lower() in ("off", "no", "false", "0"):
145 return False
146 else:
147 raise OptionValueError(_("Option %s: invalid boolean value: %r") % (opt, value))
149 # Make sure _check_required() is called from the constructor!
150 CHECK_METHODS = Option.CHECK_METHODS + [_check_required]
151 TYPE_CHECKER["ksboolean"] = _check_ksboolean
153 def process (self, opt, value, values, parser):
154 Option.process(self, opt, value, values, parser)
155 parser.option_seen[self] = 1
157 # Override default take_action method to handle our custom actions.
158 def take_action(self, action, dest, opt, value, values, parser):
159 if action == "map":
160 values.ensure_value(dest, parser.map[opt.lstrip('-')])
161 elif action == "map_extend":
162 values.ensure_value(dest, []).extend(parser.map[opt.lstrip('-')])
163 else:
164 Option.take_action(self, action, dest, opt, value, values, parser)
167 ### SCRIPT HANDLING
170 # You may make a subclass of Script if you need additional script handling
171 # besides just a data representation. For instance, anaconda may subclass
172 # this to add a run method.
173 class Script:
174 def __repr__(self):
175 retval = ("(s: '%s' i: %s c: %d)") % \
176 (self.script, self.interp, self.inChroot)
177 return string.replace(retval, "\n", "|")
179 def __init__(self, script, interp = "/bin/sh", inChroot = False,
180 logfile = None, errorOnFail = False, type = KS_SCRIPT_PRE):
181 self.script = string.join(script, "")
182 self.interp = interp
183 self.inChroot = inChroot
184 self.logfile = logfile
185 self.errorOnFail = errorOnFail
186 self.type = type
188 # Produce a string representation of the script suitable for writing
189 # to a kickstart file. Add this to the end of the %whatever header.
190 def write(self):
191 retval = ""
192 if self.interp != "/bin/sh" and self.interp != "":
193 retval = retval + " --interp %s" % self.interp
194 if self.type == KS_SCRIPT_POST and not self.inChroot:
195 retval = retval + " --nochroot"
196 if self.logfile != None:
197 retval = retval + " --logfile %s" % self.logfile
198 if self.errorOnFail:
199 retval = retval + " --erroronfail"
201 retval = retval + "\n%s\n" % self.script
202 return retval
205 ### COMMAND HANDLERS
208 # You may make a subclass of KickstartHandlers if you need to do something
209 # besides just build up the data store. If you need to do additional processing
210 # just make a subclass, define handlers for each command in your subclass, and
211 # make sure to call the same handler in the super class before whatever you
212 # want to do. Also if you need to make a new parser that only takes action
213 # for a subset of commands, make a subclass and define all the handlers to
214 # None except the ones you care about.
215 class KickstartHandlers:
216 def __init__ (self, ksdata):
217 self.ksdata = ksdata
219 # These will get set by the handleCommand method in the parser.
220 self.lineno = 0
221 self.currentCmd = ""
223 self.handlers = { "auth" : self.doAuthconfig,
224 "authconfig" : self.doAuthconfig,
225 "autopart" : self.doAutoPart,
226 "autostep" : self.doAutoStep,
227 "bootloader" : self.doBootloader,
228 "cdrom" : self.doMethod,
229 "clearpart" : self.doClearPart,
230 "cmdline" : self.doDisplayMode,
231 "device" : self.doDevice,
232 "deviceprobe" : self.doDeviceProbe,
233 "driverdisk" : self.doDriverDisk,
234 "firewall" : self.doFirewall,
235 "firstboot" : self.doFirstboot,
236 "graphical" : self.doDisplayMode,
237 "halt" : self.doReboot,
238 "harddrive" : self.doMethod,
239 "ignoredisk" : self.doIgnoreDisk,
240 # implied by lack of "upgrade" command
241 "install" : None,
242 "interactive" : self.doInteractive,
243 "keyboard" : self.doKeyboard,
244 "lang" : self.doLang,
245 "langsupport" : self.doLangSupport,
246 "logvol" : self.doLogicalVolume,
247 "logging" : self.doLogging,
248 "mediacheck" : self.doMediaCheck,
249 "monitor" : self.doMonitor,
250 "mouse" : self.doMouse,
251 "network" : self.doNetwork,
252 "nfs" : self.doMethod,
253 "dmraid" : self.doDmRaid,
254 "part" : self.doPartition,
255 "partition" : self.doPartition,
256 "poweroff" : self.doReboot,
257 "raid" : self.doRaid,
258 "reboot" : self.doReboot,
259 "repo" : self.doRepo,
260 "rootpw" : self.doRootPw,
261 "selinux" : self.doSELinux,
262 "shutdown" : self.doReboot,
263 "skipx" : self.doSkipX,
264 "text" : self.doDisplayMode,
265 "timezone" : self.doTimezone,
266 "url" : self.doMethod,
267 "upgrade" : self.doUpgrade,
268 "vnc" : self.doVnc,
269 "volgroup" : self.doVolumeGroup,
270 "xconfig" : self.doXConfig,
271 "zerombr" : self.doZeroMbr,
272 "zfcp" : self.doZFCP,
275 def resetHandlers (self):
276 for key in self.handlers.keys():
277 self.handlers[key] = None
279 def deprecatedCommand(self, cmd):
280 warnings.warn(_("Ignoring deprecated command on line %s: The %s command has been deprecated and no longer has any effect. It may be removed from future releases, which will result in a fatal error from kickstart. Please modify your kickstart file to remove this command.") % (self.lineno, cmd), DeprecationWarning)
282 def doAuthconfig(self, args):
283 self.ksdata.authconfig = string.join(args)
285 def doAutoPart(self, args):
286 if len(args) > 0:
287 raise KickstartValueError, formatErrorMsg(self.lineno, msg=_("Command %s does not take any arguments") % "autopart")
289 self.ksdata.autopart = True
291 def doAutoStep(self, args):
292 op = KSOptionParser(lineno=self.lineno)
293 op.add_option("--autoscreenshot", dest="autoscreenshot",
294 action="store_true", default=False)
296 (opts, extra) = op.parse_args(args=args)
297 self.ksdata.autostep["autoscreenshot"] = opts.autoscreenshot
299 def doBootloader(self, args):
300 def driveorder_cb (option, opt_str, value, parser):
301 for d in value.split(','):
302 parser.values.ensure_value(option.dest, []).append(d)
304 op = KSOptionParser(lineno=self.lineno)
305 op.add_option("--append", dest="appendLine")
306 op.add_option("--location", dest="location", type="choice",
307 default="mbr",
308 choices=["mbr", "partition", "none", "boot"])
309 op.add_option("--lba32", dest="forceLBA", action="store_true",
310 default=False)
311 op.add_option("--password", dest="password", default="")
312 op.add_option("--md5pass", dest="md5pass", default="")
313 op.add_option("--upgrade", dest="upgrade", action="store_true",
314 default=False)
315 op.add_option("--driveorder", dest="driveorder", action="callback",
316 callback=driveorder_cb, nargs=1, type="string")
318 (opts, extra) = op.parse_args(args=args)
320 for key in filter (lambda k: getattr(opts, k) != None, op.keys()):
321 self.ksdata.bootloader[key] = getattr(opts, key)
323 def doClearPart(self, args):
324 def drive_cb (option, opt_str, value, parser):
325 for d in value.split(','):
326 parser.values.ensure_value(option.dest, []).append(d)
328 op = KSOptionParser(lineno=self.lineno)
329 op.add_option("--all", dest="type", action="store_const",
330 const=CLEARPART_TYPE_ALL)
331 op.add_option("--drives", dest="drives", action="callback",
332 callback=drive_cb, nargs=1, type="string")
333 op.add_option("--initlabel", dest="initAll", action="store_true",
334 default=False)
335 op.add_option("--linux", dest="type", action="store_const",
336 const=CLEARPART_TYPE_LINUX)
337 op.add_option("--none", dest="type", action="store_const",
338 const=CLEARPART_TYPE_NONE)
340 (opts, extra) = op.parse_args(args=args)
342 for key in filter (lambda k: getattr(opts, k) != None, op.keys()):
343 self.ksdata.clearpart[key] = getattr(opts, key)
345 def doDevice(self, args):
346 self.ksdata.device = string.join(args)
348 def doDeviceProbe(self, args):
349 self.ksdata.deviceprobe = string.join(args)
351 def doDisplayMode(self, args):
352 if self.currentCmd == "cmdline":
353 self.ksdata.displayMode = DISPLAY_MODE_CMDLINE
354 elif self.currentCmd == "graphical":
355 self.ksdata.displayMode = DISPLAY_MODE_GRAPHICAL
356 elif self.currentCmd == "text":
357 self.ksdata.displayMode = DISPLAY_MODE_TEXT
359 def doDriverDisk(self, args):
360 self.ksdata.driverdisk = string.join(args)
362 def doFirewall(self, args):
363 def firewall_port_cb (option, opt_str, value, parser):
364 for p in value.split(","):
365 p = p.strip()
366 if p.find(":") == -1:
367 p = "%s:tcp" % p
368 parser.values.ensure_value(option.dest, []).append(p)
370 op = KSOptionParser(map={"ssh":["22:tcp"], "telnet":["23:tcp"],
371 "smtp":["25:tcp"], "http":["80:tcp", "443:tcp"],
372 "ftp":["21:tcp"]}, lineno=self.lineno)
374 op.add_option("--disable", "--disabled", dest="enabled",
375 action="store_false")
376 op.add_option("--enable", "--enabled", dest="enabled",
377 action="store_true", default=True)
378 op.add_option("--ftp", "--http", "--smtp", "--ssh", "--telnet",
379 dest="ports", action="map_extend")
380 op.add_option("--port", dest="ports", action="callback",
381 callback=firewall_port_cb, nargs=1, type="string")
382 op.add_option("--trust", dest="trusts", action="append")
384 (opts, extra) = op.parse_args(args=args)
386 for key in filter (lambda k: getattr(opts, k) != None, op.keys()):
387 self.ksdata.firewall[key] = getattr(opts, key)
389 def doFirstboot(self, args):
390 op = KSOptionParser(lineno=self.lineno)
391 op.add_option("--disable", "--disabled", dest="firstboot",
392 action="store_const", const=FIRSTBOOT_SKIP)
393 op.add_option("--enable", "--enabled", dest="firstboot",
394 action="store_const", const=FIRSTBOOT_DEFAULT)
395 op.add_option("--reconfig", dest="firstboot", action="store_const",
396 const=FIRSTBOOT_RECONFIG)
398 (opts, extra) = op.parse_args(args=args)
399 self.ksdata.firstboot = opts.firstboot
401 def doIgnoreDisk(self, args):
402 def drive_cb (option, opt_str, value, parser):
403 for d in value.split(','):
404 parser.values.ensure_value(option.dest, []).append(d)
406 op = KSOptionParser(lineno=self.lineno)
407 op.add_option("--drives", dest="drives", action="callback",
408 callback=drive_cb, nargs=1, type="string")
410 (opts, extra) = op.parse_args(args=args)
412 self.ksdata.ignoredisk = opts.drives
414 def doInteractive(self, args):
415 if len(args) > 0:
416 raise KickstartValueError, formatErrorMsg(self.lineno, msg=_("Command %s does not take any arguments") % "interactive")
418 self.ksdata.interactive = True
420 def doKeyboard(self, args):
421 if len(args) > 1:
422 raise KickstartValueError, formatErrorMsg(self.lineno, msg=_("Command %s only takes one argument") % "keyboard")
424 self.ksdata.keyboard = args[0]
426 def doLang(self, args):
427 if len(args) > 1:
428 raise KickstartValueError, formatErrorMsg(self.lineno, msg=_("Command %s only takes one argument") % "lang")
430 self.ksdata.lang = args[0]
432 def doLangSupport(self, args):
433 self.deprecatedCommand("langsupport")
435 def doLogicalVolume(self, args):
436 def lv_cb (option, opt_str, value, parser):
437 parser.values.format = False
438 parser.values.preexist = True
440 op = KSOptionParser(lineno=self.lineno)
441 op.add_option("--bytes-per-inode", dest="bytesPerInode", action="store",
442 type="int", nargs=1)
443 op.add_option("--fsoptions", dest="fsopts")
444 op.add_option("--fstype", dest="fstype")
445 op.add_option("--grow", dest="grow", action="store_true",
446 default=False)
447 op.add_option("--maxsize", dest="maxSizeMB", action="store", type="int",
448 nargs=1)
449 op.add_option("--name", dest="name", required=1)
450 op.add_option("--noformat", action="callback", callback=lv_cb,
451 dest="format", default=True, nargs=0)
452 op.add_option("--percent", dest="percent", action="store", type="int",
453 nargs=1)
454 op.add_option("--recommended", dest="recommended", action="store_true",
455 default=False)
456 op.add_option("--size", dest="size", action="store", type="int",
457 nargs=1)
458 op.add_option("--useexisting", dest="preexist", action="store_true",
459 default=False)
460 op.add_option("--vgname", dest="vgname", required=1)
462 (opts, extra) = op.parse_args(args=args)
464 if len(extra) == 0:
465 raise KickstartValueError, formatErrorMsg(self.lineno, msg=_("Mount point required for %s") % "logvol")
467 lvd = KickstartLogVolData()
468 for key in filter (lambda k: getattr(opts, k) != None, op.keys()):
469 setattr(lvd, key, getattr(opts, key))
471 lvd.mountpoint = extra[0]
472 self.ksdata.lvList.append(lvd)
474 def doLogging(self, args):
475 op = KSOptionParser(lineno=self.lineno)
476 op.add_option("--host")
477 op.add_option("--level", type="choice",
478 choices=["debug", "info", "warning", "error", "critical"])
479 op.add_option("--port")
481 (opts, extra) = op.parse_args(args=args)
483 for key in filter (lambda k: getattr(opts, k) != None, op.keys()):
484 self.ksdata.logging[key] = getattr(opts, key)
486 def doMediaCheck(self, args):
487 if len(args) > 0:
488 raise KickstartValueError, formatErrorMsg(self.lineno, msg=_("Command %s does not take any arguments") % "mediacheck")
490 self.ksdata.mediacheck = True
492 def doMethod(self, args):
493 op = KSOptionParser(lineno=self.lineno)
495 self.ksdata.method["method"] = self.currentCmd
497 if self.currentCmd == "cdrom":
498 pass
499 elif self.currentCmd == "harddrive":
500 op.add_option("--partition", dest="partition", required=1)
501 op.add_option("--dir", dest="dir", required=1)
503 (opts, extra) = op.parse_args(args=args)
504 self.ksdata.method["partition"] = opts.partition
505 self.ksdata.method["dir"] = opts.dir
506 elif self.currentCmd == "nfs":
507 op.add_option("--server", dest="server", required=1)
508 op.add_option("--dir", dest="dir", required=1)
510 (opts, extra) = op.parse_args(args=args)
511 self.ksdata.method["server"] = opts.server
512 self.ksdata.method["dir"] = opts.dir
513 elif self.currentCmd == "url":
514 op.add_option("--url", dest="url", required=1)
516 (opts, extra) = op.parse_args(args=args)
517 self.ksdata.method["url"] = opts.url
519 def doMonitor(self, args):
520 op = KSOptionParser(lineno=self.lineno)
521 op.add_option("--hsync", dest="hsync")
522 op.add_option("--monitor", dest="monitor")
523 op.add_option("--noprobe", dest="probe", action="store_false",
524 default=True)
525 op.add_option("--vsync", dest="vsync")
527 (opts, extra) = op.parse_args(args=args)
529 if extra:
530 raise KickstartValueError, formatErrorMsg(self.lineno, msg=_("Unexpected arguments to %s command: %s") % ("monitor", extra))
532 for key in filter (lambda k: getattr(opts, k) != None, op.keys()):
533 self.ksdata.monitor[key] = getattr(opts, key)
535 def doMouse(self, args):
536 self.deprecatedCommand("mouse")
538 def doNetwork(self, args):
539 op = KSOptionParser(lineno=self.lineno)
540 op.add_option("--bootproto", dest="bootProto", default="dhcp")
541 op.add_option("--class", dest="dhcpclass")
542 op.add_option("--device", dest="device")
543 op.add_option("--essid", dest="essid")
544 op.add_option("--ethtool", dest="ethtool")
545 op.add_option("--gateway", dest="gateway")
546 op.add_option("--hostname", dest="hostname")
547 op.add_option("--ip", dest="ip")
548 op.add_option("--nameserver", dest="nameserver")
549 op.add_option("--netmask", dest="netmask")
550 op.add_option("--nodns", dest="nodns", action="store_true",
551 default=False)
552 op.add_option("--notksdevice", dest="notksdevice", action="store_true",
553 default=False)
554 op.add_option("--onboot", dest="onboot", action="store",
555 type="ksboolean")
556 op.add_option("--wepkey", dest="wepkey")
558 (opts, extra) = op.parse_args(args=args)
560 nd = KickstartNetworkData()
561 for key in filter (lambda k: getattr(opts, k) != None, op.keys()):
562 setattr(nd, key, getattr(opts, key))
564 self.ksdata.network.append(nd)
566 def doDmRaid(self, args):
567 op = KSOptionParser(lineno=self.lineno)
568 op.add_option("--name", dest="name", action="store", type="string",
569 required=1)
570 op.add_option("--dev", dest="devices", action="append", type="string",
571 required=1)
573 (opts, extra) = op.parse_args(args=args)
575 dd = KickstartDmRaidData()
576 for key in filter (lambda k: getattr(opts, k) != None, op.keys()):
577 setattr(dd, key, getattr(opts, key))
578 dd.name = dd.name.split('/')[-1]
580 self.ksdata.dmraids.append(dd)
582 def doPartition(self, args):
583 def part_cb (option, opt_str, value, parser):
584 if value.startswith("/dev/"):
585 parser.values.ensure_value(option.dest, value[5:])
586 else:
587 parser.values.ensure_value(option.dest, value)
589 op = KSOptionParser(lineno=self.lineno)
590 op.add_option("--active", dest="active", action="store_true",
591 default=False)
592 op.add_option("--asprimary", dest="primOnly", action="store_true",
593 default=False)
594 op.add_option("--bytes-per-inode", dest="bytesPerInode", action="store",
595 type="int", nargs=1)
596 op.add_option("--end", dest="end", action="store", type="int",
597 nargs=1)
598 op.add_option("--fsoptions", dest="fsopts")
599 op.add_option("--fstype", "--type", dest="fstype")
600 op.add_option("--grow", dest="grow", action="store_true", default=False)
601 op.add_option("--label", dest="label")
602 op.add_option("--maxsize", dest="maxSizeMB", action="store", type="int",
603 nargs=1)
604 op.add_option("--noformat", dest="format", action="store_false",
605 default=True)
606 op.add_option("--onbiosdisk", dest="onbiosdisk")
607 op.add_option("--ondisk", "--ondrive", dest="disk")
608 op.add_option("--onpart", "--usepart", dest="onPart", action="callback",
609 callback=part_cb, nargs=1, type="string")
610 op.add_option("--recommended", dest="recommended", action="store_true",
611 default=False)
612 op.add_option("--size", dest="size", action="store", type="int",
613 nargs=1)
614 op.add_option("--start", dest="start", action="store", type="int",
615 nargs=1)
617 (opts, extra) = op.parse_args(args=args)
619 if len(extra) != 1:
620 raise KickstartValueError, formatErrorMsg(self.lineno, msg=_("Mount point required for %s") % "partition")
622 pd = KickstartPartData()
623 for key in filter (lambda k: getattr(opts, k) != None, op.keys()):
624 setattr(pd, key, getattr(opts, key))
626 pd.mountpoint = extra[0]
627 self.ksdata.partitions.append(pd)
629 def doReboot(self, args):
630 if self.currentCmd == "reboot":
631 self.ksdata.reboot["action"] = KS_REBOOT
632 else:
633 self.ksdata.reboot["action"] = KS_SHUTDOWN
635 op = KSOptionParser(lineno=self.lineno)
636 op.add_option("--eject", dest="eject", action="store_true",
637 default=False)
639 (opts, extra) = op.parse_args(args=args)
640 self.ksdata.reboot["eject"] = opts.eject
642 def doRepo(self, args):
643 pass
645 def doRaid(self, args):
646 def raid_cb (option, opt_str, value, parser):
647 parser.values.format = False
648 parser.values.preexist = True
650 def device_cb (option, opt_str, value, parser):
651 if value[0:2] == "md":
652 parser.values.ensure_value(option.dest, value[2:])
653 else:
654 parser.values.ensure_value(option.dest, value)
656 def level_cb (option, opt_str, value, parser):
657 if value == "RAID0" or value == "0":
658 parser.values.ensure_value(option.dest, "RAID0")
659 elif value == "RAID1" or value == "1":
660 parser.values.ensure_value(option.dest, "RAID1")
661 elif value == "RAID5" or value == "5":
662 parser.values.ensure_value(option.dest, "RAID5")
663 elif value == "RAID6" or value == "6":
664 parser.values.ensure_value(option.dest, "RAID6")
666 op = KSOptionParser(lineno=self.lineno)
667 op.add_option("--bytes-per-inode", dest="bytesPerInode", action="store",
668 type="int", nargs=1)
669 op.add_option("--device", action="callback", callback=device_cb,
670 dest="device", type="string", nargs=1, required=1)
671 op.add_option("--fsoptions", dest="fsopts")
672 op.add_option("--fstype", dest="fstype")
673 op.add_option("--level", dest="level", action="callback",
674 callback=level_cb, type="string", nargs=1)
675 op.add_option("--noformat", action="callback", callback=raid_cb,
676 dest="format", default=True, nargs=0)
677 op.add_option("--spares", dest="spares", action="store", type="int",
678 nargs=1, default=0)
679 op.add_option("--useexisting", dest="preexist", action="store_true",
680 default=False)
682 (opts, extra) = op.parse_args(args=args)
684 if len(extra) == 0:
685 raise KickstartValueError, formatErrorMsg(self.lineno, msg=_("Mount point required for %s") % "raid")
687 rd = KickstartRaidData()
688 for key in filter (lambda k: getattr(opts, k) != None, op.keys()):
689 setattr(rd, key, getattr(opts, key))
691 rd.mountpoint = extra[0]
692 rd.members = extra[1:]
693 self.ksdata.raidList.append(rd)
695 def doRootPw(self, args):
696 op = KSOptionParser(lineno=self.lineno)
697 op.add_option("--iscrypted", dest="isCrypted", action="store_true",
698 default=False)
700 (opts, extra) = op.parse_args(args=args)
701 self.ksdata.rootpw["isCrypted"] = opts.isCrypted
703 if len(extra) != 1:
704 raise KickstartValueError, formatErrorMsg(self.lineno, msg=_("A single argument is expected for the %s command") % "rootpw")
706 self.ksdata.rootpw["password"] = extra[0]
708 def doSELinux(self, args):
709 op = KSOptionParser(lineno=self.lineno)
710 op.add_option("--disabled", dest="sel", action="store_const",
711 const=SELINUX_DISABLED)
712 op.add_option("--enforcing", dest="sel", action="store_const",
713 const=SELINUX_ENFORCING)
714 op.add_option("--permissive", dest="sel", action="store_const",
715 const=SELINUX_PERMISSIVE)
717 (opts, extra) = op.parse_args(args=args)
718 self.ksdata.selinux = opts.sel
720 def doSkipX(self, args):
721 if len(args) > 0:
722 raise KickstartValueError, formatErrorMsg(self.lineno, msg=_("Command %s does not take any arguments") % "skipx")
724 self.ksdata.skipx = True
726 def doTimezone(self, args):
727 op = KSOptionParser(lineno=self.lineno)
728 op.add_option("--utc", dest="isUtc", action="store_true", default=False)
730 (opts, extra) = op.parse_args(args=args)
731 self.ksdata.timezone["isUtc"] = opts.isUtc
733 if len(extra) != 1:
734 raise KickstartValueError, formatErrorMsg(self.lineno, msg=_("A single argument is expected for the %s command") % "timezone")
736 self.ksdata.timezone["timezone"] = extra[0]
738 def doUpgrade(self, args):
739 if len(args) > 0:
740 raise KickstartValueError, formatErrorMsg(self.lineno, msg=_("Command %s does not take any arguments") % "upgrade")
742 self.ksdata.upgrade = True
744 def doVnc(self, args):
745 def connect_cb (option, opt_str, value, parser):
746 cargs = value.split(":")
747 parser.values.ensure_value("host", cargs[0])
749 if len(cargs) > 1:
750 parser.values.ensure_value("port", cargs[1])
752 op = KSOptionParser(lineno=self.lineno)
753 op.add_option("--connect", action="callback", callback=connect_cb,
754 nargs=1, type="string", deprecated=1)
755 op.add_option("--password", dest="password")
756 op.add_option("--host", dest="host")
757 op.add_option("--port", dest="port")
759 (opts, extra) = op.parse_args(args=args)
761 self.ksdata.vnc["enabled"] = True
763 for key in filter (lambda k: getattr(opts, k) != None, op.keys()):
764 self.ksdata.vnc[key] = getattr(opts, key)
766 def doVolumeGroup(self, args):
767 # Have to be a little more complicated to set two values.
768 def vg_cb (option, opt_str, value, parser):
769 parser.values.format = False
770 parser.values.preexist = True
772 op = KSOptionParser(lineno=self.lineno)
773 op.add_option("--noformat", action="callback", callback=vg_cb,
774 dest="format", default=True, nargs=0)
775 op.add_option("--pesize", dest="pesize", type="int", nargs=1,
776 default=32768)
777 op.add_option("--useexisting", dest="preexist", action="store_true",
778 default=False)
780 (opts, extra) = op.parse_args(args=args)
782 vgd = KickstartVolGroupData()
783 for key in filter (lambda k: getattr(opts, k) != None, op.keys()):
784 setattr(vgd, key, getattr(opts, key))
786 vgd.vgname = extra[0]
787 vgd.physvols = extra[1:]
788 self.ksdata.vgList.append(vgd)
790 def doXConfig(self, args):
791 op = KSOptionParser(lineno=self.lineno)
792 op.add_option("--card", deprecated=1)
793 op.add_option("--driver", dest="driver")
794 op.add_option("--defaultdesktop", dest="defaultdesktop")
795 op.add_option("--depth", dest="depth", action="store", type="int",
796 nargs=1)
797 op.add_option("--hsync", deprecated=1)
798 op.add_option("--monitor", deprecated=1)
799 op.add_option("--noprobe", deprecated=1)
800 op.add_option("--resolution", dest="resolution")
801 op.add_option("--startxonboot", dest="startX", action="store_true",
802 default=False)
803 op.add_option("--videoram", dest="videoRam")
804 op.add_option("--vsync", deprecated=1)
806 (opts, extra) = op.parse_args(args=args)
807 if extra:
808 raise KickstartValueError, formatErrorMsg(self.lineno, msg=_("Unexpected arguments to %s command: %s" % ("xconfig", extra)))
810 for key in filter (lambda k: getattr(opts, k) != None, op.keys()):
811 self.ksdata.xconfig[key] = getattr(opts, key)
813 def doZeroMbr(self, args):
814 if len(args) > 0:
815 warnings.warn(_("Ignoring deprecated option on line %s: The zerombr command no longer takes any options. In future releases, this will result in a fatal error from kickstart. Please modify your kickstart file to remove any options.") % self.lineno, DeprecationWarning)
817 self.ksdata.zerombr = True
819 def doZFCP(self, args):
820 op = KSOptionParser(lineno=self.lineno)
821 op.add_option("--devnum", dest="devnum", required=1)
822 op.add_option("--fcplun", dest="fcplun", required=1)
823 op.add_option("--scsiid", dest="scsiid", required=1)
824 op.add_option("--scsilun", dest="scsilun", required=1)
825 op.add_option("--wwpn", dest="wwpn", required=1)
827 (opts, extra) = op.parse_args(args=args)
829 for key in filter (lambda k: getattr(opts, k) != None, op.keys()):
830 self.ksdata.zfcp[key] = getattr(opts, key)
833 ### PARSER
836 # The kickstart file parser. This only transitions between states and calls
837 # handlers at certain points. To create a specialized parser, make a subclass
838 # of this and override the methods you care about. Methods that don't need to
839 # do anything may just pass.
841 # Passing None for kshandlers is valid just in case you don't care about
842 # handling any commands.
843 class KickstartParser:
844 def __init__ (self, ksdata, kshandlers, followIncludes=True,
845 errorsAreFatal=True, missingIncludeIsFatal=True):
846 self.handler = kshandlers
847 self.ksdata = ksdata
848 self.followIncludes = followIncludes
849 self.missingIncludeIsFatal = missingIncludeIsFatal
850 self.state = STATE_COMMANDS
851 self.script = None
852 self.includeDepth = 0
853 self.errorsAreFatal = errorsAreFatal
855 # Functions to be called when we are at certain points in the
856 # kickstart file parsing. Override these if you need special
857 # behavior.
858 def addScript (self):
859 if string.join(self.script["body"]).strip() == "":
860 return
862 s = Script (self.script["body"], self.script["interp"],
863 self.script["chroot"], self.script["log"],
864 self.script["errorOnFail"], self.script["type"])
866 self.ksdata.scripts.append(s)
868 def addPackages (self, line):
869 if line[0] == '@':
870 line = line[1:]
871 self.ksdata.groupList.append(line.lstrip())
872 elif line[0] == '-':
873 line = line[1:]
874 self.ksdata.excludedList.append(line.lstrip())
875 else:
876 self.ksdata.packageList.append(line.lstrip())
878 def handleCommand (self, lineno, args):
879 if not self.handler:
880 return
882 cmd = args[0]
883 cmdArgs = args[1:]
885 if not self.handler.handlers.has_key(cmd):
886 raise KickstartParseError, formatErrorMsg(lineno)
887 else:
888 if self.handler.handlers[cmd] != None:
889 self.handler.currentCmd = cmd
890 self.handler.lineno = lineno
891 self.handler.handlers[cmd](cmdArgs)
893 def handlePackageHdr (self, lineno, args):
894 op = KSOptionParser(lineno=lineno)
895 op.add_option("--excludedocs", dest="excludedocs", action="store_true",
896 default=False)
897 op.add_option("--ignoremissing", dest="ignoremissing",
898 action="store_true", default=False)
899 op.add_option("--nobase", dest="nobase", action="store_true",
900 default=False)
901 op.add_option("--ignoredeps", dest="resolveDeps", action="store_false",
902 deprecated=1)
903 op.add_option("--resolvedeps", dest="resolveDeps", action="store_true",
904 deprecated=1)
906 (opts, extra) = op.parse_args(args=args[1:])
908 self.ksdata.excludeDocs = opts.excludedocs
909 self.ksdata.addBase = not opts.nobase
910 if opts.ignoremissing:
911 self.ksdata.handleMissing = KS_MISSING_IGNORE
912 else:
913 self.ksdata.handleMissing = KS_MISSING_PROMPT
915 def handleScriptHdr (self, lineno, args):
916 op = KSOptionParser(lineno=lineno)
917 op.add_option("--erroronfail", dest="errorOnFail", action="store_true",
918 default=False)
919 op.add_option("--interpreter", dest="interpreter", default="/bin/sh")
920 op.add_option("--log", "--logfile", dest="log")
922 if args[0] == "%pre" or args[0] == "%traceback":
923 self.script["chroot"] = False
924 elif args[0] == "%post":
925 self.script["chroot"] = True
926 op.add_option("--nochroot", dest="nochroot", action="store_true",
927 default=False)
929 (opts, extra) = op.parse_args(args=args[1:])
931 self.script["interp"] = opts.interpreter
932 self.script["log"] = opts.log
933 self.script["errorOnFail"] = opts.errorOnFail
934 if hasattr(opts, "nochroot"):
935 self.script["chroot"] = not opts.nochroot
937 # Parser state machine. Don't override this in a subclass.
938 def readKickstart (self, file):
939 # For error reporting.
940 lineno = 0
942 fh = open(file)
943 needLine = True
945 while True:
946 if needLine:
947 line = fh.readline()
948 lineno += 1
949 needLine = False
951 # At the end of an included file
952 if line == "" and self.includeDepth > 0:
953 fh.close()
954 break
956 # Don't eliminate whitespace or comments from scripts.
957 if line.isspace() or (line != "" and line.lstrip()[0] == '#'):
958 # Save the platform for s-c-kickstart, though.
959 if line[:10] == "#platform=" and self.state == STATE_COMMANDS:
960 self.ksdata.platform = line[11:]
962 if self.state in [STATE_PRE, STATE_POST, STATE_TRACEBACK]:
963 self.script["body"].append(line)
965 needLine = True
966 continue
968 # We only want to split the line if we're outside of a script,
969 # as inside the script might involve some pretty weird quoting
970 # that shlex doesn't understand.
971 if self.state in [STATE_PRE, STATE_POST, STATE_TRACEBACK]:
972 # Have we found a state transition? If so, we still want
973 # to split. Otherwise, args won't be set but we'll fall through
974 # all the way to the last case.
975 if line != "" and string.split(line.lstrip())[0] in \
976 ["%post", "%pre", "%traceback", "%include", "%packages"]:
977 args = shlex.split(line)
978 else:
979 args = None
980 else:
981 args = shlex.split(line)
983 if args and args[0] == "%include":
984 # This case comes up primarily in ksvalidator.
985 if not self.followIncludes:
986 needLine = True
987 continue
989 if not args[1]:
990 raise KickstartParseError, formatErrorMsg(lineno)
991 else:
992 self.includeDepth += 1
994 try:
995 self.readKickstart (args[1])
996 except IOError:
997 # Handle the include file being provided over the
998 # network in a %pre script. This case comes up in the
999 # early parsing in anaconda.
1000 if self.missingIncludeIsFatal:
1001 raise
1003 self.includeDepth -= 1
1004 needLine = True
1005 continue
1007 if self.state == STATE_COMMANDS:
1008 if not args and self.includeDepth == 0:
1009 self.state = STATE_END
1010 elif args[0] in ["%pre", "%post", "%traceback"]:
1011 self.state = STATE_SCRIPT_HDR
1012 elif args[0] == "%packages":
1013 self.state = STATE_PACKAGES
1014 elif args[0][0] == '%':
1015 # This error is too difficult to continue from, without
1016 # lots of resync code. So just print this one and quit.
1017 raise KickstartParseError, formatErrorMsg(lineno)
1018 else:
1019 needLine = True
1021 if self.errorsAreFatal:
1022 self.handleCommand(lineno, args)
1023 else:
1024 try:
1025 self.handleCommand(lineno, args)
1026 except Exception, msg:
1027 print msg
1029 elif self.state == STATE_PACKAGES:
1030 if not args and self.includeDepth == 0:
1031 self.state = STATE_END
1032 elif args[0] in ["%pre", "%post", "%traceback"]:
1033 self.state = STATE_SCRIPT_HDR
1034 elif args[0] == "%packages":
1035 needLine = True
1037 if self.errorsAreFatal:
1038 self.handlePackageHdr (lineno, args)
1039 else:
1040 try:
1041 self.handlePackageHdr (lineno, args)
1042 except Exception, msg:
1043 print msg
1044 elif args[0][0] == '%':
1045 # This error is too difficult to continue from, without
1046 # lots of resync code. So just print this one and quit.
1047 raise KickstartParseError, formatErrorMsg(lineno)
1048 else:
1049 needLine = True
1050 self.addPackages (string.rstrip(line))
1052 elif self.state == STATE_SCRIPT_HDR:
1053 needLine = True
1054 self.script = {"body": [], "interp": "/bin/sh", "log": None,
1055 "errorOnFail": False}
1057 if not args and self.includeDepth == 0:
1058 self.state = STATE_END
1059 elif args[0] == "%pre":
1060 self.state = STATE_PRE
1061 self.script["type"] = KS_SCRIPT_PRE
1062 elif args[0] == "%post":
1063 self.state = STATE_POST
1064 self.script["type"] = KS_SCRIPT_POST
1065 elif args[0] == "%traceback":
1066 self.state = STATE_TRACEBACK
1067 self.script["type"] = KS_SCRIPT_TRACEBACK
1068 elif args[0][0] == '%':
1069 # This error is too difficult to continue from, without
1070 # lots of resync code. So just print this one and quit.
1071 raise KickstartParseError, formatErrorMsg(lineno)
1073 if self.errorsAreFatal:
1074 self.handleScriptHdr (lineno, args)
1075 else:
1076 try:
1077 self.handleScriptHdr (lineno, args)
1078 except Exception, msg:
1079 print msg
1081 elif self.state in [STATE_PRE, STATE_POST, STATE_TRACEBACK]:
1082 if line == "" and self.includeDepth == 0:
1083 # If we're at the end of the kickstart file, add the script.
1084 self.addScript()
1085 self.state = STATE_END
1086 elif args and args[0] in ["%pre", "%post", "%traceback", "%packages"]:
1087 # Otherwise we're now at the start of the next section.
1088 # Figure out what kind of a script we just finished
1089 # reading, add it to the list, and switch to the initial
1090 # state.
1091 self.addScript()
1092 self.state = STATE_COMMANDS
1093 else:
1094 # Otherwise just add to the current script body.
1095 self.script["body"].append(line)
1096 needLine = True
1098 elif self.state == STATE_END:
1099 break