Add support for translations.
[pykickstart.git] / pykickstart / parser.py
blob7490288ecf0927fdf8923bd7a921dc58e51b97a9
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 "mediacheck" : self.doMediaCheck,
248 "monitor" : self.doMonitor,
249 "mouse" : self.doMouse,
250 "network" : self.doNetwork,
251 "nfs" : self.doMethod,
252 "dmraid" : self.doDmRaid,
253 "part" : self.doPartition,
254 "partition" : self.doPartition,
255 "poweroff" : self.doReboot,
256 "raid" : self.doRaid,
257 "reboot" : self.doReboot,
258 "rootpw" : self.doRootPw,
259 "selinux" : self.doSELinux,
260 "shutdown" : self.doReboot,
261 "skipx" : self.doSkipX,
262 "text" : self.doDisplayMode,
263 "timezone" : self.doTimezone,
264 "url" : self.doMethod,
265 "upgrade" : self.doUpgrade,
266 "vnc" : self.doVnc,
267 "volgroup" : self.doVolumeGroup,
268 "xconfig" : self.doXConfig,
269 "zerombr" : self.doZeroMbr,
270 "zfcp" : self.doZFCP,
273 def resetHandlers (self):
274 for key in self.handlers.keys():
275 self.handlers[key] = None
277 def deprecatedCommand(self, cmd):
278 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)
280 def doAuthconfig(self, args):
281 self.ksdata.authconfig = string.join(args)
283 def doAutoPart(self, args):
284 if len(args) > 0:
285 raise KickstartValueError, formatErrorMsg(self.lineno, msg=_("Command %s does not take any arguments") % "autopart")
287 self.ksdata.autopart = True
289 def doAutoStep(self, args):
290 op = KSOptionParser(lineno=self.lineno)
291 op.add_option("--autoscreenshot", dest="autoscreenshot",
292 action="store_true", default=False)
294 (opts, extra) = op.parse_args(args=args)
295 self.ksdata.autostep["autoscreenshot"] = opts.autoscreenshot
297 def doBootloader(self, args):
298 def driveorder_cb (option, opt_str, value, parser):
299 for d in value.split(','):
300 parser.values.ensure_value(option.dest, []).append(d)
302 op = KSOptionParser(lineno=self.lineno)
303 op.add_option("--append", dest="appendLine")
304 op.add_option("--location", dest="location", type="choice",
305 default="mbr",
306 choices=["mbr", "partition", "none", "boot"])
307 op.add_option("--lba32", dest="forceLBA", action="store_true",
308 default=False)
309 op.add_option("--password", dest="password", default="")
310 op.add_option("--md5pass", dest="md5pass", default="")
311 op.add_option("--upgrade", dest="upgrade", action="store_true",
312 default=False)
313 op.add_option("--driveorder", dest="driveorder", action="callback",
314 callback=driveorder_cb, nargs=1, type="string")
316 (opts, extra) = op.parse_args(args=args)
318 for key in filter (lambda k: getattr(opts, k) != None, op.keys()):
319 self.ksdata.bootloader[key] = getattr(opts, key)
321 def doClearPart(self, args):
322 def drive_cb (option, opt_str, value, parser):
323 for d in value.split(','):
324 parser.values.ensure_value(option.dest, []).append(d)
326 op = KSOptionParser(lineno=self.lineno)
327 op.add_option("--all", dest="type", action="store_const",
328 const=CLEARPART_TYPE_ALL)
329 op.add_option("--drives", dest="drives", action="callback",
330 callback=drive_cb, nargs=1, type="string")
331 op.add_option("--initlabel", dest="initAll", action="store_true",
332 default=False)
333 op.add_option("--linux", dest="type", action="store_const",
334 const=CLEARPART_TYPE_LINUX)
335 op.add_option("--none", dest="type", action="store_const",
336 const=CLEARPART_TYPE_NONE)
338 (opts, extra) = op.parse_args(args=args)
340 for key in filter (lambda k: getattr(opts, k) != None, op.keys()):
341 self.ksdata.clearpart[key] = getattr(opts, key)
343 def doDevice(self, args):
344 self.ksdata.device = string.join(args)
346 def doDeviceProbe(self, args):
347 self.ksdata.deviceprobe = string.join(args)
349 def doDisplayMode(self, args):
350 if self.currentCmd == "cmdline":
351 self.ksdata.displayMode = DISPLAY_MODE_CMDLINE
352 elif self.currentCmd == "graphical":
353 self.ksdata.displayMode = DISPLAY_MODE_GRAPHICAL
354 elif self.currentCmd == "text":
355 self.ksdata.displayMode = DISPLAY_MODE_TEXT
357 def doDriverDisk(self, args):
358 self.ksdata.driverdisk = string.join(args)
360 def doFirewall(self, args):
361 def firewall_port_cb (option, opt_str, value, parser):
362 for p in value.split(","):
363 p = p.strip()
364 if p.find(":") == -1:
365 p = "%s:tcp" % p
366 parser.values.ensure_value(option.dest, []).append(p)
368 op = KSOptionParser(map={"ssh":["22:tcp"], "telnet":["23:tcp"],
369 "smtp":["25:tcp"], "http":["80:tcp", "443:tcp"],
370 "ftp":["21:tcp"]}, lineno=self.lineno)
372 op.add_option("--disable", "--disabled", dest="enabled",
373 action="store_false")
374 op.add_option("--enable", "--enabled", dest="enabled",
375 action="store_true", default=True)
376 op.add_option("--ftp", "--http", "--smtp", "--ssh", "--telnet",
377 dest="ports", action="map_extend")
378 op.add_option("--port", dest="ports", action="callback",
379 callback=firewall_port_cb, nargs=1, type="string")
380 op.add_option("--trust", dest="trusts", action="append")
382 (opts, extra) = op.parse_args(args=args)
384 for key in filter (lambda k: getattr(opts, k) != None, op.keys()):
385 self.ksdata.firewall[key] = getattr(opts, key)
387 def doFirstboot(self, args):
388 op = KSOptionParser(lineno=self.lineno)
389 op.add_option("--disable", "--disabled", dest="firstboot",
390 action="store_const", const=FIRSTBOOT_SKIP)
391 op.add_option("--enable", "--enabled", dest="firstboot",
392 action="store_const", const=FIRSTBOOT_DEFAULT)
393 op.add_option("--reconfig", dest="firstboot", action="store_const",
394 const=FIRSTBOOT_RECONFIG)
396 (opts, extra) = op.parse_args(args=args)
397 self.ksdata.firstboot = opts.firstboot
399 def doIgnoreDisk(self, args):
400 def drive_cb (option, opt_str, value, parser):
401 for d in value.split(','):
402 parser.values.ensure_value(option.dest, []).append(d)
404 op = KSOptionParser(lineno=self.lineno)
405 op.add_option("--drives", dest="drives", action="callback",
406 callback=drive_cb, nargs=1, type="string")
408 (opts, extra) = op.parse_args(args=args)
410 self.ksdata.ignoredisk = opts.drives
412 def doInteractive(self, args):
413 if len(args) > 0:
414 raise KickstartValueError, formatErrorMsg(self.lineno, msg=_("Command %s does not take any arguments") % "interactive")
416 self.ksdata.interactive = True
418 def doKeyboard(self, args):
419 if len(args) > 1:
420 raise KickstartValueError, formatErrorMsg(self.lineno, msg=_("Command %s only takes one argument") % "keyboard")
422 self.ksdata.keyboard = args[0]
424 def doLang(self, args):
425 if len(args) > 1:
426 raise KickstartValueError, formatErrorMsg(self.lineno, msg=_("Command %s only takes one argument") % "lang")
428 self.ksdata.lang = args[0]
430 def doLangSupport(self, args):
431 self.deprecatedCommand("langsupport")
433 def doLogicalVolume(self, args):
434 def lv_cb (option, opt_str, value, parser):
435 parser.values.format = False
436 parser.values.preexist = True
438 op = KSOptionParser(lineno=self.lineno)
439 op.add_option("--bytes-per-inode", dest="bytesPerInode", action="store",
440 type="int", nargs=1)
441 op.add_option("--fsoptions", dest="fsopts")
442 op.add_option("--fstype", dest="fstype")
443 op.add_option("--grow", dest="grow", action="store_true",
444 default=False)
445 op.add_option("--maxsize", dest="maxSizeMB", action="store", type="int",
446 nargs=1)
447 op.add_option("--name", dest="name", required=1)
448 op.add_option("--noformat", action="callback", callback=lv_cb,
449 dest="format", default=True, nargs=0)
450 op.add_option("--percent", dest="percent", action="store", type="int",
451 nargs=1)
452 op.add_option("--recommended", dest="recommended", action="store_true",
453 default=False)
454 op.add_option("--size", dest="size", action="store", type="int",
455 nargs=1)
456 op.add_option("--useexisting", dest="preexist", action="store_true",
457 default=False)
458 op.add_option("--vgname", dest="vgname", required=1)
460 (opts, extra) = op.parse_args(args=args)
462 if len(extra) == 0:
463 raise KickstartValueError, formatErrorMsg(self.lineno, msg=_("Mount point required for %s") % "logvol")
465 lvd = KickstartLogVolData()
466 for key in filter (lambda k: getattr(opts, k) != None, op.keys()):
467 setattr(lvd, key, getattr(opts, key))
469 lvd.mountpoint = extra[0]
470 self.ksdata.lvList.append(lvd)
472 def doMediaCheck(self, args):
473 if len(args) > 0:
474 raise KickstartValueError, formatErrorMsg(self.lineno, msg=_("Command %s does not take any arguments") % "mediacheck")
476 self.ksdata.mediacheck = True
478 def doMethod(self, args):
479 op = KSOptionParser(lineno=self.lineno)
481 self.ksdata.method["method"] = self.currentCmd
483 if self.currentCmd == "cdrom":
484 pass
485 elif self.currentCmd == "harddrive":
486 op.add_option("--partition", dest="partition", required=1)
487 op.add_option("--dir", dest="dir", required=1)
489 (opts, extra) = op.parse_args(args=args)
490 self.ksdata.method["partition"] = opts.partition
491 self.ksdata.method["dir"] = opts.dir
492 elif self.currentCmd == "nfs":
493 op.add_option("--server", dest="server", required=1)
494 op.add_option("--dir", dest="dir", required=1)
496 (opts, extra) = op.parse_args(args=args)
497 self.ksdata.method["server"] = opts.server
498 self.ksdata.method["dir"] = opts.dir
499 elif self.currentCmd == "url":
500 op.add_option("--url", dest="url", required=1)
502 (opts, extra) = op.parse_args(args=args)
503 self.ksdata.method["url"] = opts.url
505 def doMonitor(self, args):
506 op = KSOptionParser(lineno=self.lineno)
507 op.add_option("--hsync", dest="hsync")
508 op.add_option("--monitor", dest="monitor")
509 op.add_option("--noprobe", dest="probe", action="store_false",
510 default=True)
511 op.add_option("--vsync", dest="vsync")
513 (opts, extra) = op.parse_args(args=args)
515 if extra:
516 raise KickstartValueError, formatErrorMsg(self.lineno, msg=_("Unexpected arguments to %s command: %s") % ("monitor", extra))
518 for key in filter (lambda k: getattr(opts, k) != None, op.keys()):
519 self.ksdata.monitor[key] = getattr(opts, key)
521 def doMouse(self, args):
522 self.deprecatedCommand("mouse")
524 def doNetwork(self, args):
525 op = KSOptionParser(lineno=self.lineno)
526 op.add_option("--bootproto", dest="bootProto", default="dhcp")
527 op.add_option("--class", dest="dhcpclass")
528 op.add_option("--device", dest="device")
529 op.add_option("--essid", dest="essid")
530 op.add_option("--ethtool", dest="ethtool")
531 op.add_option("--gateway", dest="gateway")
532 op.add_option("--hostname", dest="hostname")
533 op.add_option("--ip", dest="ip")
534 op.add_option("--nameserver", dest="nameserver")
535 op.add_option("--netmask", dest="netmask")
536 op.add_option("--nodns", dest="nodns", action="store_true",
537 default=False)
538 op.add_option("--notksdevice", dest="notksdevice", action="store_true",
539 default=False)
540 op.add_option("--onboot", dest="onboot", action="store",
541 type="ksboolean")
542 op.add_option("--wepkey", dest="wepkey")
544 (opts, extra) = op.parse_args(args=args)
546 nd = KickstartNetworkData()
547 for key in filter (lambda k: getattr(opts, k) != None, op.keys()):
548 setattr(nd, key, getattr(opts, key))
550 self.ksdata.network.append(nd)
552 def doDmRaid(self, args):
553 op = KSOptionParser(lineno=self.lineno)
554 op.add_option("--name", dest="name", action="store", type="string",
555 required=1)
556 op.add_option("--dev", dest="devices", action="append", type="string",
557 required=1)
559 (opts, extra) = op.parse_args(args=args)
561 dd = KickstartDmRaidData()
562 for key in filter (lambda k: getattr(opts, k) != None, op.keys()):
563 setattr(dd, key, getattr(opts, key))
564 dd.name = dd.name.split('/')[-1]
566 self.ksdata.dmraids.append(dd)
568 def doPartition(self, args):
569 def part_cb (option, opt_str, value, parser):
570 if value.startswith("/dev/"):
571 parser.values.ensure_value(option.dest, value[5:])
572 else:
573 parser.values.ensure_value(option.dest, value)
575 op = KSOptionParser(lineno=self.lineno)
576 op.add_option("--active", dest="active", action="store_true",
577 default=False)
578 op.add_option("--asprimary", dest="primOnly", action="store_true",
579 default=False)
580 op.add_option("--bytes-per-inode", dest="bytesPerInode", action="store",
581 type="int", nargs=1)
582 op.add_option("--end", dest="end", action="store", type="int",
583 nargs=1)
584 op.add_option("--fsoptions", dest="fsopts")
585 op.add_option("--fstype", "--type", dest="fstype")
586 op.add_option("--grow", dest="grow", action="store_true", default=False)
587 op.add_option("--label", dest="label")
588 op.add_option("--maxsize", dest="maxSizeMB", action="store", type="int",
589 nargs=1)
590 op.add_option("--noformat", dest="format", action="store_false",
591 default=True)
592 op.add_option("--onbiosdisk", dest="onbiosdisk")
593 op.add_option("--ondisk", "--ondrive", dest="disk")
594 op.add_option("--onpart", "--usepart", dest="onPart", action="callback",
595 callback=part_cb, nargs=1, type="string")
596 op.add_option("--recommended", dest="recommended", action="store_true",
597 default=False)
598 op.add_option("--size", dest="size", action="store", type="int",
599 nargs=1)
600 op.add_option("--start", dest="start", action="store", type="int",
601 nargs=1)
603 (opts, extra) = op.parse_args(args=args)
605 if len(extra) != 1:
606 raise KickstartValueError, formatErrorMsg(self.lineno, msg=_("Mount point required for %s") % "partition")
608 pd = KickstartPartData()
609 for key in filter (lambda k: getattr(opts, k) != None, op.keys()):
610 setattr(pd, key, getattr(opts, key))
612 pd.mountpoint = extra[0]
613 self.ksdata.partitions.append(pd)
615 def doReboot(self, args):
616 if self.currentCmd == "reboot":
617 self.ksdata.reboot["action"] = KS_REBOOT
618 else:
619 self.ksdata.reboot["action"] = KS_SHUTDOWN
621 op = KSOptionParser(lineno=self.lineno)
622 op.add_option("--eject", dest="eject", action="store_true",
623 default=False)
625 (opts, extra) = op.parse_args(args=args)
626 self.ksdata.reboot["eject"] = opts.eject
628 def doRaid(self, args):
629 def raid_cb (option, opt_str, value, parser):
630 parser.values.format = False
631 parser.values.preexist = True
633 def device_cb (option, opt_str, value, parser):
634 if value[0:2] == "md":
635 parser.values.ensure_value(option.dest, value[2:])
636 else:
637 parser.values.ensure_value(option.dest, value)
639 def level_cb (option, opt_str, value, parser):
640 if value == "RAID0" or value == "0":
641 parser.values.ensure_value(option.dest, "RAID0")
642 elif value == "RAID1" or value == "1":
643 parser.values.ensure_value(option.dest, "RAID1")
644 elif value == "RAID5" or value == "5":
645 parser.values.ensure_value(option.dest, "RAID5")
646 elif value == "RAID6" or value == "6":
647 parser.values.ensure_value(option.dest, "RAID6")
649 op = KSOptionParser(lineno=self.lineno)
650 op.add_option("--bytes-per-inode", dest="bytesPerInode", action="store",
651 type="int", nargs=1)
652 op.add_option("--device", action="callback", callback=device_cb,
653 dest="device", type="string", nargs=1, required=1)
654 op.add_option("--fsoptions", dest="fsopts")
655 op.add_option("--fstype", dest="fstype")
656 op.add_option("--level", dest="level", action="callback",
657 callback=level_cb, type="string", nargs=1)
658 op.add_option("--noformat", action="callback", callback=raid_cb,
659 dest="format", default=True, nargs=0)
660 op.add_option("--spares", dest="spares", action="store", type="int",
661 nargs=1, default=0)
662 op.add_option("--useexisting", dest="preexist", action="store_true",
663 default=False)
665 (opts, extra) = op.parse_args(args=args)
667 if len(extra) == 0:
668 raise KickstartValueError, formatErrorMsg(self.lineno, msg=_("Mount point required for %s") % "raid")
670 rd = KickstartRaidData()
671 for key in filter (lambda k: getattr(opts, k) != None, op.keys()):
672 setattr(rd, key, getattr(opts, key))
674 rd.mountpoint = extra[0]
675 rd.members = extra[1:]
676 self.ksdata.raidList.append(rd)
678 def doRootPw(self, args):
679 op = KSOptionParser(lineno=self.lineno)
680 op.add_option("--iscrypted", dest="isCrypted", action="store_true",
681 default=False)
683 (opts, extra) = op.parse_args(args=args)
684 self.ksdata.rootpw["isCrypted"] = opts.isCrypted
686 if len(extra) != 1:
687 raise KickstartValueError, formatErrorMsg(self.lineno, msg=_("A single argument is expected for the %s command") % "rootpw")
689 self.ksdata.rootpw["password"] = extra[0]
691 def doSELinux(self, args):
692 op = KSOptionParser(lineno=self.lineno)
693 op.add_option("--disabled", dest="sel", action="store_const",
694 const=SELINUX_DISABLED)
695 op.add_option("--enforcing", dest="sel", action="store_const",
696 const=SELINUX_ENFORCING)
697 op.add_option("--permissive", dest="sel", action="store_const",
698 const=SELINUX_PERMISSIVE)
700 (opts, extra) = op.parse_args(args=args)
701 self.ksdata.selinux = opts.sel
703 def doSkipX(self, args):
704 if len(args) > 0:
705 raise KickstartValueError, formatErrorMsg(self.lineno, msg=_("Command %s does not take any arguments") % "skipx")
707 self.ksdata.skipx = True
709 def doTimezone(self, args):
710 op = KSOptionParser(lineno=self.lineno)
711 op.add_option("--utc", dest="isUtc", action="store_true", default=False)
713 (opts, extra) = op.parse_args(args=args)
714 self.ksdata.timezone["isUtc"] = opts.isUtc
716 if len(extra) != 1:
717 raise KickstartValueError, formatErrorMsg(self.lineno, msg=_("A single argument is expected for the %s command") % "timezone")
719 self.ksdata.timezone["timezone"] = extra[0]
721 def doUpgrade(self, args):
722 if len(args) > 0:
723 raise KickstartValueError, formatErrorMsg(self.lineno, msg=_("Command %s does not take any arguments") % "upgrade")
725 self.ksdata.upgrade = True
727 def doVnc(self, args):
728 def connect_cb (option, opt_str, value, parser):
729 cargs = value.split(":")
730 parser.values.ensure_value("host", cargs[0])
732 if len(cargs) > 1:
733 parser.values.ensure_value("port", cargs[1])
735 op = KSOptionParser(lineno=self.lineno)
736 op.add_option("--connect", action="callback", callback=connect_cb,
737 nargs=1, type="string", deprecated=1)
738 op.add_option("--password", dest="password")
739 op.add_option("--host", dest="host")
740 op.add_option("--port", dest="port")
742 (opts, extra) = op.parse_args(args=args)
744 self.ksdata.vnc["enabled"] = True
746 for key in filter (lambda k: getattr(opts, k) != None, op.keys()):
747 self.ksdata.vnc[key] = getattr(opts, key)
749 def doVolumeGroup(self, args):
750 # Have to be a little more complicated to set two values.
751 def vg_cb (option, opt_str, value, parser):
752 parser.values.format = False
753 parser.values.preexist = True
755 op = KSOptionParser(lineno=self.lineno)
756 op.add_option("--noformat", action="callback", callback=vg_cb,
757 dest="format", default=True, nargs=0)
758 op.add_option("--pesize", dest="pesize", type="int", nargs=1,
759 default=32768)
760 op.add_option("--useexisting", dest="preexist", action="store_true",
761 default=False)
763 (opts, extra) = op.parse_args(args=args)
765 vgd = KickstartVolGroupData()
766 for key in filter (lambda k: getattr(opts, k) != None, op.keys()):
767 setattr(vgd, key, getattr(opts, key))
769 vgd.vgname = extra[0]
770 vgd.physvols = extra[1:]
771 self.ksdata.vgList.append(vgd)
773 def doXConfig(self, args):
774 op = KSOptionParser(lineno=self.lineno)
775 op.add_option("--card", deprecated=1)
776 op.add_option("--driver", dest="driver")
777 op.add_option("--defaultdesktop", dest="defaultdesktop")
778 op.add_option("--depth", dest="depth", action="store", type="int",
779 nargs=1)
780 op.add_option("--hsync", deprecated=1)
781 op.add_option("--monitor", deprecated=1)
782 op.add_option("--noprobe", deprecated=1)
783 op.add_option("--resolution", dest="resolution")
784 op.add_option("--startxonboot", dest="startX", action="store_true",
785 default=False)
786 op.add_option("--videoram", dest="videoRam")
787 op.add_option("--vsync", deprecated=1)
789 (opts, extra) = op.parse_args(args=args)
790 if extra:
791 raise KickstartValueError, formatErrorMsg(self.lineno, msg=_("Unexpected arguments to %s command: %s" % ("xconfig", extra)))
793 for key in filter (lambda k: getattr(opts, k) != None, op.keys()):
794 self.ksdata.xconfig[key] = getattr(opts, key)
796 def doZeroMbr(self, args):
797 if len(args) > 0:
798 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)
800 self.ksdata.zerombr = True
802 def doZFCP(self, args):
803 op = KSOptionParser(lineno=self.lineno)
804 op.add_option("--devnum", dest="devnum", required=1)
805 op.add_option("--fcplun", dest="fcplun", required=1)
806 op.add_option("--scsiid", dest="scsiid", required=1)
807 op.add_option("--scsilun", dest="scsilun", required=1)
808 op.add_option("--wwpn", dest="wwpn", required=1)
810 (opts, extra) = op.parse_args(args=args)
812 for key in filter (lambda k: getattr(opts, k) != None, op.keys()):
813 self.ksdata.zfcp[key] = getattr(opts, key)
816 ### PARSER
819 # The kickstart file parser. This only transitions between states and calls
820 # handlers at certain points. To create a specialized parser, make a subclass
821 # of this and override the methods you care about. Methods that don't need to
822 # do anything may just pass.
824 # Passing None for kshandlers is valid just in case you don't care about
825 # handling any commands.
826 class KickstartParser:
827 def __init__ (self, ksdata, kshandlers, followIncludes=True,
828 errorsAreFatal=True, missingIncludeIsFatal=True):
829 self.handler = kshandlers
830 self.ksdata = ksdata
831 self.followIncludes = followIncludes
832 self.missingIncludeIsFatal = missingIncludeIsFatal
833 self.state = STATE_COMMANDS
834 self.script = None
835 self.includeDepth = 0
836 self.errorsAreFatal = errorsAreFatal
838 # Functions to be called when we are at certain points in the
839 # kickstart file parsing. Override these if you need special
840 # behavior.
841 def addScript (self):
842 if string.join(self.script["body"]).strip() == "":
843 return
845 s = Script (self.script["body"], self.script["interp"],
846 self.script["chroot"], self.script["log"],
847 self.script["errorOnFail"], self.script["type"])
849 self.ksdata.scripts.append(s)
851 def addPackages (self, line):
852 if line[0] == '@':
853 line = line[1:]
854 self.ksdata.groupList.append(line.lstrip())
855 elif line[0] == '-':
856 line = line[1:]
857 self.ksdata.excludedList.append(line.lstrip())
858 else:
859 self.ksdata.packageList.append(line.lstrip())
861 def handleCommand (self, lineno, args):
862 if not self.handler:
863 return
865 cmd = args[0]
866 cmdArgs = args[1:]
868 if not self.handler.handlers.has_key(cmd):
869 raise KickstartParseError, formatErrorMsg(lineno)
870 else:
871 if self.handler.handlers[cmd] != None:
872 self.handler.currentCmd = cmd
873 self.handler.lineno = lineno
874 self.handler.handlers[cmd](cmdArgs)
876 def handlePackageHdr (self, lineno, args):
877 op = KSOptionParser(lineno=lineno)
878 op.add_option("--excludedocs", dest="excludedocs", action="store_true",
879 default=False)
880 op.add_option("--ignoremissing", dest="ignoremissing",
881 action="store_true", default=False)
882 op.add_option("--nobase", dest="nobase", action="store_true",
883 default=False)
884 op.add_option("--ignoredeps", dest="resolveDeps", action="store_false",
885 deprecated=1)
886 op.add_option("--resolvedeps", dest="resolveDeps", action="store_true",
887 deprecated=1)
889 (opts, extra) = op.parse_args(args=args[1:])
891 self.ksdata.excludeDocs = opts.excludedocs
892 self.ksdata.addBase = not opts.nobase
893 if opts.ignoremissing:
894 self.ksdata.handleMissing = KS_MISSING_IGNORE
895 else:
896 self.ksdata.handleMissing = KS_MISSING_PROMPT
898 def handleScriptHdr (self, lineno, args):
899 op = KSOptionParser(lineno=lineno)
900 op.add_option("--erroronfail", dest="errorOnFail", action="store_true",
901 default=False)
902 op.add_option("--interpreter", dest="interpreter", default="/bin/sh")
903 op.add_option("--log", "--logfile", dest="log")
905 if args[0] == "%pre" or args[0] == "%traceback":
906 self.script["chroot"] = False
907 elif args[0] == "%post":
908 self.script["chroot"] = True
909 op.add_option("--nochroot", dest="nochroot", action="store_true",
910 default=False)
912 (opts, extra) = op.parse_args(args=args[1:])
914 self.script["interp"] = opts.interpreter
915 self.script["log"] = opts.log
916 self.script["errorOnFail"] = opts.errorOnFail
917 if hasattr(opts, "nochroot"):
918 self.script["chroot"] = not opts.nochroot
920 # Parser state machine. Don't override this in a subclass.
921 def readKickstart (self, file):
922 # For error reporting.
923 lineno = 0
925 fh = open(file)
926 needLine = True
928 while True:
929 if needLine:
930 line = fh.readline()
931 lineno += 1
932 needLine = False
934 # At the end of an included file
935 if line == "" and self.includeDepth > 0:
936 fh.close()
937 break
939 # Don't eliminate whitespace or comments from scripts.
940 if line.isspace() or (line != "" and line.lstrip()[0] == '#'):
941 # Save the platform for s-c-kickstart, though.
942 if line[:10] == "#platform=" and self.state == STATE_COMMANDS:
943 self.ksdata.platform = line[11:]
945 if self.state in [STATE_PRE, STATE_POST, STATE_TRACEBACK]:
946 self.script["body"].append(line)
948 needLine = True
949 continue
951 # We only want to split the line if we're outside of a script,
952 # as inside the script might involve some pretty weird quoting
953 # that shlex doesn't understand.
954 if self.state in [STATE_PRE, STATE_POST, STATE_TRACEBACK]:
955 # Have we found a state transition? If so, we still want
956 # to split. Otherwise, args won't be set but we'll fall through
957 # all the way to the last case.
958 if line != "" and string.split(line.lstrip())[0] in \
959 ["%post", "%pre", "%traceback", "%include", "%packages"]:
960 args = shlex.split(line)
961 else:
962 args = None
963 else:
964 args = shlex.split(line)
966 if args and args[0] == "%include":
967 # This case comes up primarily in ksvalidator.
968 if not self.followIncludes:
969 needLine = True
970 continue
972 if not args[1]:
973 raise KickstartParseError, formatErrorMsg(lineno)
974 else:
975 self.includeDepth += 1
977 try:
978 self.readKickstart (args[1])
979 except IOError:
980 # Handle the include file being provided over the
981 # network in a %pre script. This case comes up in the
982 # early parsing in anaconda.
983 if self.missingIncludeIsFatal:
984 raise
986 self.includeDepth -= 1
987 needLine = True
988 continue
990 if self.state == STATE_COMMANDS:
991 if not args and self.includeDepth == 0:
992 self.state = STATE_END
993 elif args[0] in ["%pre", "%post", "%traceback"]:
994 self.state = STATE_SCRIPT_HDR
995 elif args[0] == "%packages":
996 self.state = STATE_PACKAGES
997 elif args[0][0] == '%':
998 # This error is too difficult to continue from, without
999 # lots of resync code. So just print this one and quit.
1000 raise KickstartParseError, formatErrorMsg(lineno)
1001 else:
1002 needLine = True
1004 if self.errorsAreFatal:
1005 self.handleCommand(lineno, args)
1006 else:
1007 try:
1008 self.handleCommand(lineno, args)
1009 except Exception, msg:
1010 print msg
1012 elif self.state == STATE_PACKAGES:
1013 if not args and self.includeDepth == 0:
1014 self.state = STATE_END
1015 elif args[0] in ["%pre", "%post", "%traceback"]:
1016 self.state = STATE_SCRIPT_HDR
1017 elif args[0] == "%packages":
1018 needLine = True
1020 if self.errorsAreFatal:
1021 self.handlePackageHdr (lineno, args)
1022 else:
1023 try:
1024 self.handlePackageHdr (lineno, args)
1025 except Exception, msg:
1026 print msg
1027 elif args[0][0] == '%':
1028 # This error is too difficult to continue from, without
1029 # lots of resync code. So just print this one and quit.
1030 raise KickstartParseError, formatErrorMsg(lineno)
1031 else:
1032 needLine = True
1033 self.addPackages (string.rstrip(line))
1035 elif self.state == STATE_SCRIPT_HDR:
1036 needLine = True
1037 self.script = {"body": [], "interp": "/bin/sh", "log": None,
1038 "errorOnFail": False}
1040 if not args and self.includeDepth == 0:
1041 self.state = STATE_END
1042 elif args[0] == "%pre":
1043 self.state = STATE_PRE
1044 self.script["type"] = KS_SCRIPT_PRE
1045 elif args[0] == "%post":
1046 self.state = STATE_POST
1047 self.script["type"] = KS_SCRIPT_POST
1048 elif args[0] == "%traceback":
1049 self.state = STATE_TRACEBACK
1050 self.script["type"] = KS_SCRIPT_TRACEBACK
1051 elif args[0][0] == '%':
1052 # This error is too difficult to continue from, without
1053 # lots of resync code. So just print this one and quit.
1054 raise KickstartParseError, formatErrorMsg(lineno)
1056 if self.errorsAreFatal:
1057 self.handleScriptHdr (lineno, args)
1058 else:
1059 try:
1060 self.handleScriptHdr (lineno, args)
1061 except Exception, msg:
1062 print msg
1064 elif self.state in [STATE_PRE, STATE_POST, STATE_TRACEBACK]:
1065 if line == "" and self.includeDepth == 0:
1066 # If we're at the end of the kickstart file, add the script.
1067 self.addScript()
1068 self.state = STATE_END
1069 elif args and args[0] in ["%pre", "%post", "%traceback", "%packages"]:
1070 # Otherwise we're now at the start of the next section.
1071 # Figure out what kind of a script we just finished
1072 # reading, add it to the list, and switch to the initial
1073 # state.
1074 self.addScript()
1075 self.state = STATE_COMMANDS
1076 else:
1077 # Otherwise just add to the current script body.
1078 self.script["body"].append(line)
1079 needLine = True
1081 elif self.state == STATE_END:
1082 break