Bump version.
[pykickstart.git] / pykickstart / parser.py
blob53b5d1b2784147c043cb6489dd3081adac357227
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 op = KSOptionParser(lineno=self.lineno)
644 op.add_option("--name", dest="name", required=1)
645 op.add_option("--baseurl")
646 op.add_option("--mirrorlist")
648 (opts, extra) = op.parse_args(args=args)
650 # This is lame, but I can't think of a better way to make sure only
651 # one of these two is specified.
652 if opts.baseurl and opts.mirrorlist:
653 raise KickstartValueError, formatErrorMsg(self.lineno, msg=_("Only one of --baseurl and --mirrorlist may be specified for repo command."))
655 if not opts.baseurl and not opts.mirrorlist:
656 raise KickstartValueError, formatErrorMsg(self.lineno, msg=_("One of --baseurl or --mirrorlist must be specified for repo command."))
658 rd = KickstartRepoData()
659 for key in filter (lambda k: getattr(opts, k) != None, op.keys()):
660 setattr(rd, key, getattr(opts, key))
662 self.ksdata.repoList.append(rd)
664 def doRaid(self, args):
665 def raid_cb (option, opt_str, value, parser):
666 parser.values.format = False
667 parser.values.preexist = True
669 def device_cb (option, opt_str, value, parser):
670 if value[0:2] == "md":
671 parser.values.ensure_value(option.dest, value[2:])
672 else:
673 parser.values.ensure_value(option.dest, value)
675 def level_cb (option, opt_str, value, parser):
676 if value == "RAID0" or value == "0":
677 parser.values.ensure_value(option.dest, "RAID0")
678 elif value == "RAID1" or value == "1":
679 parser.values.ensure_value(option.dest, "RAID1")
680 elif value == "RAID5" or value == "5":
681 parser.values.ensure_value(option.dest, "RAID5")
682 elif value == "RAID6" or value == "6":
683 parser.values.ensure_value(option.dest, "RAID6")
685 op = KSOptionParser(lineno=self.lineno)
686 op.add_option("--bytes-per-inode", dest="bytesPerInode", action="store",
687 type="int", nargs=1)
688 op.add_option("--device", action="callback", callback=device_cb,
689 dest="device", type="string", nargs=1, required=1)
690 op.add_option("--fsoptions", dest="fsopts")
691 op.add_option("--fstype", dest="fstype")
692 op.add_option("--level", dest="level", action="callback",
693 callback=level_cb, type="string", nargs=1)
694 op.add_option("--noformat", action="callback", callback=raid_cb,
695 dest="format", default=True, nargs=0)
696 op.add_option("--spares", dest="spares", action="store", type="int",
697 nargs=1, default=0)
698 op.add_option("--useexisting", dest="preexist", action="store_true",
699 default=False)
701 (opts, extra) = op.parse_args(args=args)
703 if len(extra) == 0:
704 raise KickstartValueError, formatErrorMsg(self.lineno, msg=_("Mount point required for %s") % "raid")
706 rd = KickstartRaidData()
707 for key in filter (lambda k: getattr(opts, k) != None, op.keys()):
708 setattr(rd, key, getattr(opts, key))
710 rd.mountpoint = extra[0]
711 rd.members = extra[1:]
712 self.ksdata.raidList.append(rd)
714 def doRootPw(self, args):
715 op = KSOptionParser(lineno=self.lineno)
716 op.add_option("--iscrypted", dest="isCrypted", action="store_true",
717 default=False)
719 (opts, extra) = op.parse_args(args=args)
720 self.ksdata.rootpw["isCrypted"] = opts.isCrypted
722 if len(extra) != 1:
723 raise KickstartValueError, formatErrorMsg(self.lineno, msg=_("A single argument is expected for the %s command") % "rootpw")
725 self.ksdata.rootpw["password"] = extra[0]
727 def doSELinux(self, args):
728 op = KSOptionParser(lineno=self.lineno)
729 op.add_option("--disabled", dest="sel", action="store_const",
730 const=SELINUX_DISABLED)
731 op.add_option("--enforcing", dest="sel", action="store_const",
732 const=SELINUX_ENFORCING)
733 op.add_option("--permissive", dest="sel", action="store_const",
734 const=SELINUX_PERMISSIVE)
736 (opts, extra) = op.parse_args(args=args)
737 self.ksdata.selinux = opts.sel
739 def doSkipX(self, args):
740 if len(args) > 0:
741 raise KickstartValueError, formatErrorMsg(self.lineno, msg=_("Command %s does not take any arguments") % "skipx")
743 self.ksdata.skipx = True
745 def doTimezone(self, args):
746 op = KSOptionParser(lineno=self.lineno)
747 op.add_option("--utc", dest="isUtc", action="store_true", default=False)
749 (opts, extra) = op.parse_args(args=args)
750 self.ksdata.timezone["isUtc"] = opts.isUtc
752 if len(extra) != 1:
753 raise KickstartValueError, formatErrorMsg(self.lineno, msg=_("A single argument is expected for the %s command") % "timezone")
755 self.ksdata.timezone["timezone"] = extra[0]
757 def doUpgrade(self, args):
758 if len(args) > 0:
759 raise KickstartValueError, formatErrorMsg(self.lineno, msg=_("Command %s does not take any arguments") % "upgrade")
761 self.ksdata.upgrade = True
763 def doVnc(self, args):
764 def connect_cb (option, opt_str, value, parser):
765 cargs = value.split(":")
766 parser.values.ensure_value("host", cargs[0])
768 if len(cargs) > 1:
769 parser.values.ensure_value("port", cargs[1])
771 op = KSOptionParser(lineno=self.lineno)
772 op.add_option("--connect", action="callback", callback=connect_cb,
773 nargs=1, type="string", deprecated=1)
774 op.add_option("--password", dest="password")
775 op.add_option("--host", dest="host")
776 op.add_option("--port", dest="port")
778 (opts, extra) = op.parse_args(args=args)
780 self.ksdata.vnc["enabled"] = True
782 for key in filter (lambda k: getattr(opts, k) != None, op.keys()):
783 self.ksdata.vnc[key] = getattr(opts, key)
785 def doVolumeGroup(self, args):
786 # Have to be a little more complicated to set two values.
787 def vg_cb (option, opt_str, value, parser):
788 parser.values.format = False
789 parser.values.preexist = True
791 op = KSOptionParser(lineno=self.lineno)
792 op.add_option("--noformat", action="callback", callback=vg_cb,
793 dest="format", default=True, nargs=0)
794 op.add_option("--pesize", dest="pesize", type="int", nargs=1,
795 default=32768)
796 op.add_option("--useexisting", dest="preexist", action="store_true",
797 default=False)
799 (opts, extra) = op.parse_args(args=args)
801 vgd = KickstartVolGroupData()
802 for key in filter (lambda k: getattr(opts, k) != None, op.keys()):
803 setattr(vgd, key, getattr(opts, key))
805 vgd.vgname = extra[0]
806 vgd.physvols = extra[1:]
807 self.ksdata.vgList.append(vgd)
809 def doXConfig(self, args):
810 op = KSOptionParser(lineno=self.lineno)
811 op.add_option("--card", deprecated=1)
812 op.add_option("--driver", dest="driver")
813 op.add_option("--defaultdesktop", dest="defaultdesktop")
814 op.add_option("--depth", dest="depth", action="store", type="int",
815 nargs=1)
816 op.add_option("--hsync", deprecated=1)
817 op.add_option("--monitor", deprecated=1)
818 op.add_option("--noprobe", deprecated=1)
819 op.add_option("--resolution", dest="resolution")
820 op.add_option("--startxonboot", dest="startX", action="store_true",
821 default=False)
822 op.add_option("--videoram", dest="videoRam")
823 op.add_option("--vsync", deprecated=1)
825 (opts, extra) = op.parse_args(args=args)
826 if extra:
827 raise KickstartValueError, formatErrorMsg(self.lineno, msg=_("Unexpected arguments to %s command: %s" % ("xconfig", extra)))
829 for key in filter (lambda k: getattr(opts, k) != None, op.keys()):
830 self.ksdata.xconfig[key] = getattr(opts, key)
832 def doZeroMbr(self, args):
833 if len(args) > 0:
834 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)
836 self.ksdata.zerombr = True
838 def doZFCP(self, args):
839 op = KSOptionParser(lineno=self.lineno)
840 op.add_option("--devnum", dest="devnum", required=1)
841 op.add_option("--fcplun", dest="fcplun", required=1)
842 op.add_option("--scsiid", dest="scsiid", required=1)
843 op.add_option("--scsilun", dest="scsilun", required=1)
844 op.add_option("--wwpn", dest="wwpn", required=1)
846 (opts, extra) = op.parse_args(args=args)
848 for key in filter (lambda k: getattr(opts, k) != None, op.keys()):
849 self.ksdata.zfcp[key] = getattr(opts, key)
852 ### PARSER
855 # The kickstart file parser. This only transitions between states and calls
856 # handlers at certain points. To create a specialized parser, make a subclass
857 # of this and override the methods you care about. Methods that don't need to
858 # do anything may just pass.
860 # Passing None for kshandlers is valid just in case you don't care about
861 # handling any commands.
862 class KickstartParser:
863 def __init__ (self, ksdata, kshandlers, followIncludes=True,
864 errorsAreFatal=True, missingIncludeIsFatal=True):
865 self.handler = kshandlers
866 self.ksdata = ksdata
867 self.followIncludes = followIncludes
868 self.missingIncludeIsFatal = missingIncludeIsFatal
869 self.state = STATE_COMMANDS
870 self.script = None
871 self.includeDepth = 0
872 self.errorsAreFatal = errorsAreFatal
874 # Functions to be called when we are at certain points in the
875 # kickstart file parsing. Override these if you need special
876 # behavior.
877 def addScript (self):
878 if string.join(self.script["body"]).strip() == "":
879 return
881 s = Script (self.script["body"], self.script["interp"],
882 self.script["chroot"], self.script["log"],
883 self.script["errorOnFail"], self.script["type"])
885 self.ksdata.scripts.append(s)
887 def addPackages (self, line):
888 stripped = line.strip()
890 if stripped[0] == '@':
891 self.ksdata.groupList.append(stripped[1:].lstrip())
892 elif stripped[0] == '-':
893 self.ksdata.excludedList.append(stripped[1:].lstrip())
894 else:
895 self.ksdata.packageList.append(stripped)
897 def handleCommand (self, lineno, args):
898 if not self.handler:
899 return
901 cmd = args[0]
902 cmdArgs = args[1:]
904 if not self.handler.handlers.has_key(cmd):
905 raise KickstartParseError, formatErrorMsg(lineno)
906 else:
907 if self.handler.handlers[cmd] != None:
908 self.handler.currentCmd = cmd
909 self.handler.lineno = lineno
910 self.handler.handlers[cmd](cmdArgs)
912 def handlePackageHdr (self, lineno, args):
913 op = KSOptionParser(lineno=lineno)
914 op.add_option("--excludedocs", dest="excludedocs", action="store_true",
915 default=False)
916 op.add_option("--ignoremissing", dest="ignoremissing",
917 action="store_true", default=False)
918 op.add_option("--nobase", dest="nobase", action="store_true",
919 default=False)
920 op.add_option("--ignoredeps", dest="resolveDeps", action="store_false",
921 deprecated=1)
922 op.add_option("--resolvedeps", dest="resolveDeps", action="store_true",
923 deprecated=1)
925 (opts, extra) = op.parse_args(args=args[1:])
927 self.ksdata.excludeDocs = opts.excludedocs
928 self.ksdata.addBase = not opts.nobase
929 if opts.ignoremissing:
930 self.ksdata.handleMissing = KS_MISSING_IGNORE
931 else:
932 self.ksdata.handleMissing = KS_MISSING_PROMPT
934 def handleScriptHdr (self, lineno, args):
935 op = KSOptionParser(lineno=lineno)
936 op.add_option("--erroronfail", dest="errorOnFail", action="store_true",
937 default=False)
938 op.add_option("--interpreter", dest="interpreter", default="/bin/sh")
939 op.add_option("--log", "--logfile", dest="log")
941 if args[0] == "%pre" or args[0] == "%traceback":
942 self.script["chroot"] = False
943 elif args[0] == "%post":
944 self.script["chroot"] = True
945 op.add_option("--nochroot", dest="nochroot", action="store_true",
946 default=False)
948 (opts, extra) = op.parse_args(args=args[1:])
950 self.script["interp"] = opts.interpreter
951 self.script["log"] = opts.log
952 self.script["errorOnFail"] = opts.errorOnFail
953 if hasattr(opts, "nochroot"):
954 self.script["chroot"] = not opts.nochroot
956 # Parser state machine. Don't override this in a subclass.
957 def readKickstart (self, file):
958 # For error reporting.
959 lineno = 0
961 fh = open(file)
962 needLine = True
964 while True:
965 if needLine:
966 line = fh.readline()
967 lineno += 1
968 needLine = False
970 # At the end of an included file
971 if line == "" and self.includeDepth > 0:
972 fh.close()
973 break
975 # Don't eliminate whitespace or comments from scripts.
976 if line.isspace() or (line != "" and line.lstrip()[0] == '#'):
977 # Save the platform for s-c-kickstart, though.
978 if line[:10] == "#platform=" and self.state == STATE_COMMANDS:
979 self.ksdata.platform = line[11:]
981 if self.state in [STATE_PRE, STATE_POST, STATE_TRACEBACK]:
982 self.script["body"].append(line)
984 needLine = True
985 continue
987 # We only want to split the line if we're outside of a script,
988 # as inside the script might involve some pretty weird quoting
989 # that shlex doesn't understand.
990 if self.state in [STATE_PRE, STATE_POST, STATE_TRACEBACK]:
991 # Have we found a state transition? If so, we still want
992 # to split. Otherwise, args won't be set but we'll fall through
993 # all the way to the last case.
994 if line != "" and string.split(line.lstrip())[0] in \
995 ["%post", "%pre", "%traceback", "%include", "%packages"]:
996 args = shlex.split(line)
997 else:
998 args = None
999 else:
1000 args = shlex.split(line)
1002 if args and args[0] == "%include":
1003 # This case comes up primarily in ksvalidator.
1004 if not self.followIncludes:
1005 needLine = True
1006 continue
1008 if not args[1]:
1009 raise KickstartParseError, formatErrorMsg(lineno)
1010 else:
1011 self.includeDepth += 1
1013 try:
1014 self.readKickstart (args[1])
1015 except IOError:
1016 # Handle the include file being provided over the
1017 # network in a %pre script. This case comes up in the
1018 # early parsing in anaconda.
1019 if self.missingIncludeIsFatal:
1020 raise
1022 self.includeDepth -= 1
1023 needLine = True
1024 continue
1026 if self.state == STATE_COMMANDS:
1027 if not args and self.includeDepth == 0:
1028 self.state = STATE_END
1029 elif args[0] in ["%pre", "%post", "%traceback"]:
1030 self.state = STATE_SCRIPT_HDR
1031 elif args[0] == "%packages":
1032 self.state = STATE_PACKAGES
1033 elif args[0][0] == '%':
1034 # This error is too difficult to continue from, without
1035 # lots of resync code. So just print this one and quit.
1036 raise KickstartParseError, formatErrorMsg(lineno)
1037 else:
1038 needLine = True
1040 if self.errorsAreFatal:
1041 self.handleCommand(lineno, args)
1042 else:
1043 try:
1044 self.handleCommand(lineno, args)
1045 except Exception, msg:
1046 print msg
1048 elif self.state == STATE_PACKAGES:
1049 if not args and self.includeDepth == 0:
1050 self.state = STATE_END
1051 elif args[0] in ["%pre", "%post", "%traceback"]:
1052 self.state = STATE_SCRIPT_HDR
1053 elif args[0] == "%packages":
1054 needLine = True
1056 if self.errorsAreFatal:
1057 self.handlePackageHdr (lineno, args)
1058 else:
1059 try:
1060 self.handlePackageHdr (lineno, args)
1061 except Exception, msg:
1062 print msg
1063 elif args[0][0] == '%':
1064 # This error is too difficult to continue from, without
1065 # lots of resync code. So just print this one and quit.
1066 raise KickstartParseError, formatErrorMsg(lineno)
1067 else:
1068 needLine = True
1069 self.addPackages (string.rstrip(line))
1071 elif self.state == STATE_SCRIPT_HDR:
1072 needLine = True
1073 self.script = {"body": [], "interp": "/bin/sh", "log": None,
1074 "errorOnFail": False}
1076 if not args and self.includeDepth == 0:
1077 self.state = STATE_END
1078 elif args[0] == "%pre":
1079 self.state = STATE_PRE
1080 self.script["type"] = KS_SCRIPT_PRE
1081 elif args[0] == "%post":
1082 self.state = STATE_POST
1083 self.script["type"] = KS_SCRIPT_POST
1084 elif args[0] == "%traceback":
1085 self.state = STATE_TRACEBACK
1086 self.script["type"] = KS_SCRIPT_TRACEBACK
1087 elif args[0][0] == '%':
1088 # This error is too difficult to continue from, without
1089 # lots of resync code. So just print this one and quit.
1090 raise KickstartParseError, formatErrorMsg(lineno)
1092 if self.errorsAreFatal:
1093 self.handleScriptHdr (lineno, args)
1094 else:
1095 try:
1096 self.handleScriptHdr (lineno, args)
1097 except Exception, msg:
1098 print msg
1100 elif self.state in [STATE_PRE, STATE_POST, STATE_TRACEBACK]:
1101 if line == "" and self.includeDepth == 0:
1102 # If we're at the end of the kickstart file, add the script.
1103 self.addScript()
1104 self.state = STATE_END
1105 elif args and args[0] in ["%pre", "%post", "%traceback", "%packages"]:
1106 # Otherwise we're now at the start of the next section.
1107 # Figure out what kind of a script we just finished
1108 # reading, add it to the list, and switch to the initial
1109 # state.
1110 self.addScript()
1111 self.state = STATE_COMMANDS
1112 else:
1113 # Otherwise just add to the current script body.
1114 self.script["body"].append(line)
1115 needLine = True
1117 elif self.state == STATE_END:
1118 break