Don't trust ensure_value for these partition-related settings. Perhaps we
[pykickstart.git] / pykickstart / parser.py
blob480b2e915a66b5710bfc900171b382aae39cfe11
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 STATE_END = 0
27 STATE_COMMANDS = 1
28 STATE_PACKAGES = 2
29 STATE_SCRIPT_HDR = 3
30 STATE_PRE = 4
31 STATE_POST = 5
32 STATE_TRACEBACK = 6
34 ###
35 ### ERROR HANDLING
36 ###
38 def formatErrorMsg(lineno, msg=""):
39 if msg != "":
40 return "The following problem occurred on line %s of the kickstart file:\n\n%s\n" % (lineno, msg)
41 else:
42 return "There was a problem reading from line %s of the kickstart file" % lineno
44 class KickstartError(Exception):
45 def __init__(self, val = ""):
46 Exception.__init__(self)
47 self.value = val
49 def __str__ (self):
50 return self.value
52 class KickstartParseError(KickstartError):
53 def __init__(self, msg):
54 KickstartError.__init__(self, msg)
56 def __str__(self):
57 return self.value
59 class KickstartValueError(KickstartError):
60 def __init__(self, msg):
61 KickstartError.__init__(self, msg)
63 def __str__ (self):
64 return self.value
66 ###
67 ### OPTION HANDLING
68 ###
70 # Specialized OptionParser, mainly to handle the MappableOption and to turn
71 # off help.
72 class KSOptionParser(OptionParser):
73 def exit(self, status=0, msg=None):
74 pass
76 def error(self, msg):
77 if self.lineno != None:
78 raise KickstartParseError, formatErrorMsg(self.lineno, msg=msg)
79 else:
80 raise KickstartParseError, msg
82 def keys(self):
83 retval = []
85 for opt in self.option_list:
86 if opt not in retval:
87 retval.append(opt.dest)
89 return retval
91 def _init_parsing_state (self):
92 OptionParser._init_parsing_state(self)
93 self.option_seen = {}
95 def check_values (self, values, args):
96 for option in self.option_list:
97 if (isinstance(option, Option) and option.required and \
98 not self.option_seen.has_key(option)):
99 raise KickstartValueError, formatErrorMsg(self.lineno, "Option %s is required" % option)
100 elif isinstance(option, Option) and option.deprecated and \
101 self.option_seen.has_key(option):
102 warnings.warn("Ignoring deprecated option on line %s: The %s command has been reprecated anod 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)
104 return (values, args)
106 def __init__(self, map={}, lineno=None):
107 self.map = map
108 self.lineno = lineno
109 OptionParser.__init__(self, option_class=KSOption,
110 add_help_option=False)
112 # Creates a new Option class that supports two new attributes:
113 # - required: any option with this attribute must be supplied or an exception
114 # is thrown
115 # - deprecated: any option with this attribute will cause a DeprecationWarning
116 # to be thrown if the option is used
117 # Also creates a new type:
118 # - ksboolean: support various kinds of boolean values on an option
119 # And two new actions:
120 # - map : allows you to define an opt -> val mapping such that dest gets val
121 # when opt is seen
122 # - map_extend: allows you to define an opt -> [val1, ... valn] mapping such
123 # that dest gets a list of vals built up when opt is seen
124 class KSOption (Option):
125 ATTRS = Option.ATTRS + ['deprecated', 'required']
126 ACTIONS = Option.ACTIONS + ("map", "map_extend",)
127 STORE_ACTIONS = Option.STORE_ACTIONS + ("map", "map_extend",)
129 TYPES = Option.TYPES + ("ksboolean",)
130 TYPE_CHECKER = copy(Option.TYPE_CHECKER)
132 def _check_required(self):
133 if self.required and not self.takes_value():
134 raise OptionError("Required flag set for option that doesn't take a value", self)
136 def _check_ksboolean(option, opt, value):
137 if value.lower() in ("on", "yes", "true", "1"):
138 return True
139 elif value.lower() in ("off", "no", "false", "0"):
140 return False
141 else:
142 raise OptionValueError("option %s: invalid boolean "
143 "value: %r" % (opt, value))
145 # Make sure _check_required() is called from the constructor!
146 CHECK_METHODS = Option.CHECK_METHODS + [_check_required]
147 TYPE_CHECKER["ksboolean"] = _check_ksboolean
149 def process (self, opt, value, values, parser):
150 Option.process(self, opt, value, values, parser)
151 parser.option_seen[self] = 1
153 # Override default take_action method to handle our custom actions.
154 def take_action(self, action, dest, opt, value, values, parser):
155 if action == "map":
156 values.ensure_value(dest, parser.map[opt.lstrip('-')])
157 elif action == "map_extend":
158 values.ensure_value(dest, []).extend(parser.map[opt.lstrip('-')])
159 else:
160 Option.take_action(self, action, dest, opt, value, values, parser)
163 ### SCRIPT HANDLING
166 # You may make a subclass of Script if you need additional script handling
167 # besides just a data representation. For instance, anaconda may subclass
168 # this to add a run method.
169 class Script:
170 def __repr__(self):
171 retval = ("(s: '%s' i: %s c: %d)") % \
172 (self.script, self.interp, self.inChroot)
173 return string.replace(retval, "\n", "|")
175 def __init__(self, script, interp = "/bin/sh", inChroot = False,
176 logfile = None, errorOnFail = False, type = KS_SCRIPT_PRE):
177 self.script = string.join(script, "")
178 self.interp = interp
179 self.inChroot = inChroot
180 self.logfile = logfile
181 self.errorOnFail = errorOnFail
182 self.type = type
184 # Produce a string representation of the script suitable for writing
185 # to a kickstart file. Add this to the end of the %whatever header.
186 def write(self):
187 retval = ""
188 if self.interp != "/bin/sh" and self.interp != "":
189 retval = retval + " --interp %s" % self.interp
190 if self.type == KS_SCRIPT_POST and not self.inChroot:
191 retval = retval + " --nochroot"
192 if self.logfile != None:
193 retval = retval + " --logfile %s" % self.logfile
194 if self.errorOnFail:
195 retval = retval + " --erroronfail"
197 retval = retval + "\n%s\n" % self.script
198 return retval
201 ### COMMAND HANDLERS
204 # You may make a subclass of KickstartHandlers if you need to do something
205 # besides just build up the data store. If you need to do additional processing
206 # just make a subclass, define handlers for each command in your subclass, and
207 # make sure to call the same handler in the super class before whatever you
208 # want to do. Also if you need to make a new parser that only takes action
209 # for a subset of commands, make a subclass and define all the handlers to
210 # None except the ones you care about.
211 class KickstartHandlers:
212 def __init__ (self, ksdata):
213 self.ksdata = ksdata
215 # These will get set by the handleCommand method in the parser.
216 self.lineno = 0
217 self.currentCmd = ""
219 self.handlers = { "auth" : self.doAuthconfig,
220 "authconfig" : self.doAuthconfig,
221 "autopart" : self.doAutoPart,
222 "autostep" : self.doAutoStep,
223 "bootloader" : self.doBootloader,
224 "cdrom" : self.doMethod,
225 "clearpart" : self.doClearPart,
226 "cmdline" : self.doDisplayMode,
227 "device" : self.doDevice,
228 "deviceprobe" : self.doDeviceProbe,
229 "driverdisk" : self.doDriverDisk,
230 "firewall" : self.doFirewall,
231 "firstboot" : self.doFirstboot,
232 "graphical" : self.doDisplayMode,
233 "halt" : self.doReboot,
234 "harddrive" : self.doMethod,
235 "ignoredisk" : self.doIgnoreDisk,
236 # implied by lack of "upgrade" command
237 "install" : None,
238 "interactive" : self.doInteractive,
239 "keyboard" : self.doKeyboard,
240 "lang" : self.doLang,
241 "langsupport" : self.doLangSupport,
242 "logvol" : self.doLogicalVolume,
243 "mediacheck" : self.doMediaCheck,
244 "monitor" : self.doMonitor,
245 "mouse" : self.doMouse,
246 "network" : self.doNetwork,
247 "nfs" : self.doMethod,
248 "dmraid" : self.doDmRaid,
249 "part" : self.doPartition,
250 "partition" : self.doPartition,
251 "poweroff" : self.doReboot,
252 "raid" : self.doRaid,
253 "reboot" : self.doReboot,
254 "rootpw" : self.doRootPw,
255 "selinux" : self.doSELinux,
256 "shutdown" : self.doReboot,
257 "skipx" : self.doSkipX,
258 "text" : self.doDisplayMode,
259 "timezone" : self.doTimezone,
260 "url" : self.doMethod,
261 "upgrade" : self.doUpgrade,
262 "vnc" : self.doVnc,
263 "volgroup" : self.doVolumeGroup,
264 "xconfig" : self.doXConfig,
265 "zerombr" : self.doZeroMbr,
266 "zfcp" : self.doZFCP,
269 def resetHandlers (self):
270 for key in self.handlers.keys():
271 self.handlers[key] = None
273 def deprecatedCommand(self, cmd):
274 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)
276 def doAuthconfig(self, args):
277 self.ksdata.authconfig = string.join(args)
279 def doAutoPart(self, args):
280 if len(args) > 0:
281 raise KickstartValueError, formatErrorMsg(self.lineno, msg="Command does not take any arguments")
283 self.ksdata.autopart = True
285 def doAutoStep(self, args):
286 op = KSOptionParser(lineno=self.lineno)
287 op.add_option("--autoscreenshot", dest="autoscreenshot",
288 action="store_true", default=False)
290 (opts, extra) = op.parse_args(args=args)
291 self.ksdata.autostep["autoscreenshot"] = opts.autoscreenshot
293 def doBootloader(self, args):
294 def driveorder_cb (option, opt_str, value, parser):
295 for d in value.split(','):
296 parser.values.ensure_value(option.dest, []).append(d)
298 op = KSOptionParser(lineno=self.lineno)
299 op.add_option("--append", dest="appendLine")
300 op.add_option("--location", dest="location", type="choice",
301 default="mbr",
302 choices=["mbr", "partition", "none", "boot"])
303 op.add_option("--lba32", dest="forceLBA", action="store_true",
304 default=False)
305 op.add_option("--password", dest="password", default="")
306 op.add_option("--md5pass", dest="md5pass", default="")
307 op.add_option("--upgrade", dest="upgrade", action="store_true",
308 default=False)
309 op.add_option("--driveorder", dest="driveorder", action="callback",
310 callback=driveorder_cb, nargs=1, type="string")
312 (opts, extra) = op.parse_args(args=args)
314 for key in filter (lambda k: getattr(opts, k) != None, op.keys()):
315 self.ksdata.bootloader[key] = getattr(opts, key)
317 def doClearPart(self, args):
318 def drive_cb (option, opt_str, value, parser):
319 for d in value.split(','):
320 parser.values.ensure_value(option.dest, []).append(d)
322 op = KSOptionParser(lineno=self.lineno)
323 op.add_option("--all", dest="type", action="store_const",
324 const=CLEARPART_TYPE_ALL)
325 op.add_option("--drives", dest="drives", action="callback",
326 callback=drive_cb, nargs=1, type="string")
327 op.add_option("--initlabel", dest="initAll", action="store_true",
328 default=False)
329 op.add_option("--linux", dest="type", action="store_const",
330 const=CLEARPART_TYPE_LINUX)
331 op.add_option("--none", dest="type", action="store_const",
332 const=CLEARPART_TYPE_NONE)
334 (opts, extra) = op.parse_args(args=args)
336 for key in filter (lambda k: getattr(opts, k) != None, op.keys()):
337 self.ksdata.clearpart[key] = getattr(opts, key)
339 def doDevice(self, args):
340 self.ksdata.device = string.join(args)
342 def doDeviceProbe(self, args):
343 self.ksdata.deviceprobe = string.join(args)
345 def doDisplayMode(self, args):
346 if self.currentCmd == "cmdline":
347 self.ksdata.displayMode = DISPLAY_MODE_CMDLINE
348 elif self.currentCmd == "graphical":
349 self.ksdata.displayMode = DISPLAY_MODE_GRAPHICAL
350 elif self.currentCmd == "text":
351 self.ksdata.displayMode = DISPLAY_MODE_TEXT
353 def doDriverDisk(self, args):
354 self.ksdata.driverdisk = string.join(args)
356 def doFirewall(self, args):
357 def firewall_port_cb (option, opt_str, value, parser):
358 for p in value.split(","):
359 p = p.strip()
360 if p.find(":") == -1:
361 p = "%s:tcp" % p
362 parser.values.ensure_value(option.dest, []).append(p)
364 op = KSOptionParser(map={"ssh":["22:tcp"], "telnet":["23:tcp"],
365 "smtp":["25:tcp"], "http":["80:tcp", "443:tcp"],
366 "ftp":["21:tcp"]}, lineno=self.lineno)
368 op.add_option("--disable", "--disabled", dest="enabled",
369 action="store_false")
370 op.add_option("--enable", "--enabled", dest="enabled",
371 action="store_true", default=True)
372 op.add_option("--ftp", "--http", "--smtp", "--ssh", "--telnet",
373 dest="ports", action="map_extend")
374 op.add_option("--port", dest="ports", action="callback",
375 callback=firewall_port_cb, nargs=1, type="string")
376 op.add_option("--trust", dest="trusts", action="append")
378 (opts, extra) = op.parse_args(args=args)
380 for key in filter (lambda k: getattr(opts, k) != None, op.keys()):
381 self.ksdata.firewall[key] = getattr(opts, key)
383 def doFirstboot(self, args):
384 op = KSOptionParser(lineno=self.lineno)
385 op.add_option("--disable", "--disabled", dest="firstboot",
386 action="store_const", const=FIRSTBOOT_SKIP)
387 op.add_option("--enable", "--enabled", dest="firstboot",
388 action="store_const", const=FIRSTBOOT_DEFAULT)
389 op.add_option("--reconfig", dest="firstboot", action="store_const",
390 const=FIRSTBOOT_RECONFIG)
392 (opts, extra) = op.parse_args(args=args)
393 self.ksdata.firstboot = opts.firstboot
395 def doIgnoreDisk(self, args):
396 def drive_cb (option, opt_str, value, parser):
397 for d in value.split(','):
398 parser.values.ensure_value(option.dest, []).append(d)
400 op = KSOptionParser(lineno=self.lineno)
401 op.add_option("--drives", dest="drives", action="callback",
402 callback=drive_cb, nargs=1, type="string")
404 (opts, extra) = op.parse_args(args=args)
406 self.ksdata.ignoredisk = opts.ignoredisk
408 def doInteractive(self, args):
409 if len(args) > 0:
410 raise KickstartValueError, formatErrorMsg(self.lineno, msg="Command does not take any arguments")
412 self.ksdata.interactive = True
414 def doKeyboard(self, args):
415 if len(args) > 1:
416 raise KickstartValueError, formatErrorMsg(self.lineno, msg="Command only takes one argument")
418 self.ksdata.keyboard = args[0]
420 def doLang(self, args):
421 if len(args) > 1:
422 raise KickstartValueError, formatErrorMsg(self.lineno, msg="Command only takes one argument")
424 self.ksdata.lang = args[0]
426 def doLangSupport(self, args):
427 self.deprecatedCommand("langsupport")
429 def doLogicalVolume(self, args):
430 def lv_cb (option, opt_str, value, parser):
431 parser.values.format = False
432 parser.values.preexist = True
434 op = KSOptionParser(lineno=self.lineno)
435 op.add_option("--bytes-per-inode", dest="bytesPerInode", action="store",
436 type="int", nargs=1)
437 op.add_option("--fsoptions", dest="fsopts")
438 op.add_option("--fstype", dest="fstype")
439 op.add_option("--grow", dest="grow", action="store_true",
440 default=False)
441 op.add_option("--maxsize", dest="maxSizeMB", action="store", type="int",
442 nargs=1)
443 op.add_option("--name", dest="name", required=1)
444 op.add_option("--noformat", action="callback", callback=lv_cb,
445 dest="format", default=True, nargs=0)
446 op.add_option("--percent", dest="percent", action="store", type="int",
447 nargs=1)
448 op.add_option("--recommended", dest="recommended", action="store_true",
449 default=False)
450 op.add_option("--size", dest="size", action="store", type="int",
451 nargs=1)
452 op.add_option("--useexisting", dest="preexist", action="store_true",
453 default=False)
454 op.add_option("--vgname", dest="vgname", required=1)
456 (opts, extra) = op.parse_args(args=args)
458 if len(extra) == 0:
459 raise KickstartValueError, formatErrorMsg(self.lineno, msg="Mount point required for logvol")
461 lvd = KickstartLogVolData()
462 for key in filter (lambda k: getattr(opts, k) != None, op.keys()):
463 setattr(lvd, key, getattr(opts, key))
465 lvd.mountpoint = extra[0]
466 self.ksdata.lvList.append(lvd)
468 def doMediaCheck(self, args):
469 if len(args) > 0:
470 raise KickstartValueError, formatErrorMsg(self.lineno, msg="Command does not take any arguments")
472 self.ksdata.mediacheck = True
474 def doMethod(self, args):
475 op = KSOptionParser(lineno=self.lineno)
477 self.ksdata.method["method"] = self.currentCmd
479 if self.currentCmd == "cdrom":
480 pass
481 elif self.currentCmd == "harddrive":
482 op.add_option("--partition", dest="partition", required=1)
483 op.add_option("--dir", dest="dir", required=1)
485 (opts, extra) = op.parse_args(args=args)
486 self.ksdata.method["partition"] = opts.partition
487 self.ksdata.method["dir"] = opts.dir
488 elif self.currentCmd == "nfs":
489 op.add_option("--server", dest="server", required=1)
490 op.add_option("--dir", dest="dir", required=1)
492 (opts, extra) = op.parse_args(args=args)
493 self.ksdata.method["server"] = opts.server
494 self.ksdata.method["dir"] = opts.dir
495 elif self.currentCmd == "url":
496 op.add_option("--url", dest="url", required=1)
498 (opts, extra) = op.parse_args(args=args)
499 self.ksdata.method["url"] = opts.url
501 def doMonitor(self, args):
502 op = KSOptionParser(lineno=self.lineno)
503 op.add_option("--hsync", dest="hsync")
504 op.add_option("--monitor", dest="monitor")
505 op.add_option("--noprobe", dest="probe", action="store_false",
506 default=True)
507 op.add_option("--vsync", dest="vsync")
509 (opts, extra) = op.parse_args(args=args)
511 if extra:
512 raise KickstartValueError, formatErrorMsg(self.lineno, msg="Unexpected arguments to monitor command: %s" % extra)
514 for key in filter (lambda k: getattr(opts, k) != None, op.keys()):
515 self.ksdata.monitor[key] = getattr(opts, key)
517 def doMouse(self, args):
518 self.deprecatedCommand("mouse")
520 def doNetwork(self, args):
521 op = KSOptionParser(lineno=self.lineno)
522 op.add_option("--bootproto", dest="bootProto", default="dhcp")
523 op.add_option("--class", dest="dhcpclass")
524 op.add_option("--device", dest="device")
525 op.add_option("--essid", dest="essid")
526 op.add_option("--ethtool", dest="ethtool")
527 op.add_option("--gateway", dest="gateway")
528 op.add_option("--hostname", dest="hostname")
529 op.add_option("--ip", dest="ip")
530 op.add_option("--nameserver", dest="nameserver")
531 op.add_option("--netmask", dest="netmask")
532 op.add_option("--nodns", dest="nodns", action="store_true",
533 default=False)
534 op.add_option("--notksdevice", dest="notksdevice", action="store_true",
535 default=False)
536 op.add_option("--onboot", dest="onboot", action="store",
537 type="ksboolean")
538 op.add_option("--wepkey", dest="wepkey")
540 (opts, extra) = op.parse_args(args=args)
542 nd = KickstartNetworkData()
543 for key in filter (lambda k: getattr(opts, k) != None, op.keys()):
544 setattr(nd, key, getattr(opts, key))
546 self.ksdata.network.append(nd)
548 def doDmRaid(self, args):
549 op = KSOptionParser(lineno=self.lineno)
550 op.add_option("--name", dest="name", action="store", type="string",
551 required=1)
552 op.add_option("--dev", dest="devices", action="append", type="string",
553 required=1)
555 (opts, extra) = op.parse_args(args=args)
557 dd = KickstartDmRaidData()
558 for key in filter (lambda k: getattr(opts, k) != None, op.keys()):
559 setattr(dd, key, getattr(opts, key))
560 dd.name = dd.name.split('/')[-1]
562 self.ksdata.dmraids.append(dd)
564 def doPartition(self, args):
565 def part_cb (option, opt_str, value, parser):
566 if value.startswith("/dev/"):
567 parser.values.ensure_value(option.dest, value[5:])
568 else:
569 parser.values.ensure_value(option.dest, value)
571 op = KSOptionParser(lineno=self.lineno)
572 op.add_option("--active", dest="active", action="store_true",
573 default=False)
574 op.add_option("--asprimary", dest="primOnly", action="store_true",
575 default=False)
576 op.add_option("--bytes-per-inode", dest="bytesPerInode", action="store",
577 type="int", nargs=1)
578 op.add_option("--end", dest="end", action="store", type="int",
579 nargs=1)
580 op.add_option("--fsoptions", dest="fsopts")
581 op.add_option("--fstype", "--type", dest="fstype")
582 op.add_option("--grow", dest="grow", action="store_true", default=False)
583 op.add_option("--label", dest="label")
584 op.add_option("--maxsize", dest="maxSizeMB", action="store", type="int",
585 nargs=1)
586 op.add_option("--noformat", dest="format", action="store_false",
587 default=True)
588 op.add_option("--onbiosdisk", dest="onbiosdisk")
589 op.add_option("--ondisk", "--ondrive", dest="disk")
590 op.add_option("--onpart", "--usepart", dest="onPart", action="callback",
591 callback=part_cb, nargs=1, type="string")
592 op.add_option("--recommended", dest="recommended", action="store_true",
593 default=False)
594 op.add_option("--size", dest="size", action="store", type="int",
595 nargs=1)
596 op.add_option("--start", dest="start", action="store", type="int",
597 nargs=1)
599 (opts, extra) = op.parse_args(args=args)
601 if len(extra) != 1:
602 raise KickstartValueError, formatErrorMsg(self.lineno, msg="Mount point required for partition")
604 pd = KickstartPartData()
605 for key in filter (lambda k: getattr(opts, k) != None, op.keys()):
606 setattr(pd, key, getattr(opts, key))
608 pd.mountpoint = extra[0]
609 self.ksdata.partitions.append(pd)
611 def doReboot(self, args):
612 if self.currentCmd == "reboot":
613 self.ksdata.reboot["action"] = KS_REBOOT
614 else:
615 self.ksdata.reboot["action"] = KS_SHUTDOWN
617 op = KSOptionParser(lineno=self.lineno)
618 op.add_option("--eject", dest="eject", action="store_true",
619 default=False)
621 (opts, extra) = op.parse_args(args=args)
622 self.ksdata.reboot["eject"] = opts.eject
624 def doRaid(self, args):
625 def raid_cb (option, opt_str, value, parser):
626 parser.values.format = False
627 parser.values.preexist = True
629 def device_cb (option, opt_str, value, parser):
630 if value[0:2] == "md":
631 parser.values.ensure_value(option.dest, value[2:])
632 else:
633 parser.values.ensure_value(option.dest, value)
635 def level_cb (option, opt_str, value, parser):
636 if value == "RAID0" or value == "0":
637 parser.values.ensure_value(option.dest, "RAID0")
638 elif value == "RAID1" or value == "1":
639 parser.values.ensure_value(option.dest, "RAID1")
640 elif value == "RAID5" or value == "5":
641 parser.values.ensure_value(option.dest, "RAID5")
642 elif value == "RAID6" or value == "6":
643 parser.values.ensure_value(option.dest, "RAID6")
645 op = KSOptionParser(lineno=self.lineno)
646 op.add_option("--bytes-per-inode", dest="bytesPerInode", action="store",
647 type="int", nargs=1)
648 op.add_option("--device", action="callback", callback=device_cb,
649 dest="device", type="string", nargs=1, required=1)
650 op.add_option("--fsoptions", dest="fsopts")
651 op.add_option("--fstype", dest="fstype")
652 op.add_option("--level", dest="level", action="callback",
653 callback=level_cb, type="string", nargs=1)
654 op.add_option("--noformat", action="callback", callback=raid_cb,
655 dest="format", default=True, nargs=0)
656 op.add_option("--spares", dest="spares", action="store", type="int",
657 nargs=1, default=0)
658 op.add_option("--useexisting", dest="preexist", action="store_true",
659 default=False)
661 (opts, extra) = op.parse_args(args=args)
663 if len(extra) == 0:
664 raise KickstartValueError, formatErrorMsg(self.lineno, msg="Mount point required for raid")
666 rd = KickstartRaidData()
667 for key in filter (lambda k: getattr(opts, k) != None, op.keys()):
668 setattr(rd, key, getattr(opts, key))
670 rd.mountpoint = extra[0]
671 rd.members = extra[1:]
672 self.ksdata.raidList.append(rd)
674 def doRootPw(self, args):
675 op = KSOptionParser(lineno=self.lineno)
676 op.add_option("--iscrypted", dest="isCrypted", action="store_true",
677 default=False)
679 (opts, extra) = op.parse_args(args=args)
680 self.ksdata.rootpw["isCrypted"] = opts.isCrypted
682 if len(extra) != 1:
683 raise KickstartValueError, formatErrorMsg(self.lineno, msg="A single argument is expected for rootpw")
685 self.ksdata.rootpw["password"] = extra[0]
687 def doSELinux(self, args):
688 op = KSOptionParser(lineno=self.lineno)
689 op.add_option("--disabled", dest="sel", action="store_const",
690 const=SELINUX_DISABLED)
691 op.add_option("--enforcing", dest="sel", action="store_const",
692 const=SELINUX_ENFORCING)
693 op.add_option("--permissive", dest="sel", action="store_const",
694 const=SELINUX_PERMISSIVE)
696 (opts, extra) = op.parse_args(args=args)
697 self.ksdata.selinux = opts.sel
699 def doSkipX(self, args):
700 if len(args) > 0:
701 raise KickstartValueError, formatErrorMsg(self.lineno, msg="Command does not take any arguments")
703 self.ksdata.skipx = True
705 def doTimezone(self, args):
706 op = KSOptionParser(lineno=self.lineno)
707 op.add_option("--utc", dest="isUtc", action="store_true", default=False)
709 (opts, extra) = op.parse_args(args=args)
710 self.ksdata.timezone["isUtc"] = opts.isUtc
712 if len(extra) != 1:
713 raise KickstartValueError, formatErrorMsg(self.lineno, msg="A single argument is expected for timezone")
715 self.ksdata.timezone["timezone"] = extra[0]
717 def doUpgrade(self, args):
718 if len(args) > 0:
719 raise KickstartValueError, formatErrorMsg(self.lineno, msg="Command does not take any arguments")
721 self.ksdata.upgrade = True
723 def doVnc(self, args):
724 def connect_cb (option, opt_str, value, parser):
725 cargs = value.split(":")
726 parser.values.ensure_value("host", cargs[0])
728 if len(cargs) > 1:
729 parser.values.ensure_value("port", cargs[1])
731 op = KSOptionParser(lineno=self.lineno)
732 op.add_option("--connect", action="callback", callback=connect_cb,
733 nargs=1, type="string", deprecated=1)
734 op.add_option("--password", dest="password")
735 op.add_option("--host", dest="host")
736 op.add_option("--port", dest="port")
738 (opts, extra) = op.parse_args(args=args)
740 self.ksdata.vnc["enabled"] = True
742 for key in filter (lambda k: getattr(opts, k) != None, op.keys()):
743 self.ksdata.vnc[key] = getattr(opts, key)
745 def doVolumeGroup(self, args):
746 # Have to be a little more complicated to set two values.
747 def vg_cb (option, opt_str, value, parser):
748 parser.values.format = False
749 parser.values.preexist = True
751 op = KSOptionParser(lineno=self.lineno)
752 op.add_option("--noformat", action="callback", callback=vg_cb,
753 dest="format", default=True, nargs=0)
754 op.add_option("--pesize", dest="pesize", type="int", nargs=1,
755 default=32768)
756 op.add_option("--useexisting", dest="preexist", action="store_true",
757 default=False)
759 (opts, extra) = op.parse_args(args=args)
761 vgd = KickstartVolGroupData()
762 for key in filter (lambda k: getattr(opts, k) != None, op.keys()):
763 setattr(vgd, key, getattr(opts, key))
765 vgd.vgname = extra[0]
766 vgd.physvols = extra[1:]
767 self.ksdata.vgList.append(vgd)
769 def doXConfig(self, args):
770 op = KSOptionParser(lineno=self.lineno)
771 op.add_option("--card", deprecated=1)
772 op.add_option("--driver", dest="driver")
773 op.add_option("--defaultdesktop", dest="defaultdesktop")
774 op.add_option("--depth", dest="depth", action="store", type="int",
775 nargs=1)
776 op.add_option("--hsync", deprecated=1)
777 op.add_option("--monitor", deprecated=1)
778 op.add_option("--noprobe", deprecated=1)
779 op.add_option("--resolution", dest="resolution")
780 op.add_option("--startxonboot", dest="startX", action="store_true",
781 default=False)
782 op.add_option("--videoram", dest="videoRam")
783 op.add_option("--vsync", deprecated=1)
785 (opts, extra) = op.parse_args(args=args)
786 if extra:
787 raise KickstartValueError, formatErrorMsg(self.lineno, msg="Unexpected arguments to xconfig command: %s" % extra)
789 for key in filter (lambda k: getattr(opts, k) != None, op.keys()):
790 self.ksdata.xconfig[key] = getattr(opts, key)
792 def doZeroMbr(self, args):
793 if len(args) > 0:
794 raise KickstartValueError, formatErrorMsg(self.lineno, msg="Command does not take any arguments")
796 self.ksdata.zerombr = True
798 def doZFCP(self, args):
799 op = KSOptionParser(lineno=self.lineno)
800 op.add_option("--devnum", dest="devnum", required=1)
801 op.add_option("--fcplun", dest="fcplun", required=1)
802 op.add_option("--scsiid", dest="scsiid", required=1)
803 op.add_option("--scsilun", dest="scsilun", required=1)
804 op.add_option("--wwpn", dest="wwpn", required=1)
806 (opts, extra) = op.parse_args(args=args)
808 for key in filter (lambda k: getattr(opts, k) != None, op.keys()):
809 self.ksdata.zfcp[key] = getattr(opts, key)
812 ### PARSER
815 # The kickstart file parser. This only transitions between states and calls
816 # handlers at certain points. To create a specialized parser, make a subclass
817 # of this and override the methods you care about. Methods that don't need to
818 # do anything may just pass.
820 # Passing None for kshandlers is valid just in case you don't care about
821 # handling any commands.
822 class KickstartParser:
823 def __init__ (self, ksdata, kshandlers, followIncludes=True,
824 errorsAreFatal=True):
825 self.handler = kshandlers
826 self.ksdata = ksdata
827 self.followIncludes = followIncludes
828 self.state = STATE_COMMANDS
829 self.script = None
830 self.includeDepth = 0
831 self.errorsAreFatal = errorsAreFatal
833 # Functions to be called when we are at certain points in the
834 # kickstart file parsing. Override these if you need special
835 # behavior.
836 def addScript (self):
837 if string.join(self.script["body"]).strip() == "":
838 return
840 s = Script (self.script["body"], self.script["interp"],
841 self.script["chroot"], self.script["log"],
842 self.script["errorOnFail"], self.script["type"])
844 self.ksdata.scripts.append(s)
846 def addPackages (self, line):
847 if line[0] == '@':
848 line = line[1:]
849 self.ksdata.groupList.append(line.lstrip())
850 elif line[0] == '-':
851 line = line[1:]
852 self.ksdata.excludedList.append(line.lstrip())
853 else:
854 self.ksdata.packageList.append(line.lstrip())
856 def handleCommand (self, lineno, args):
857 if not self.handler:
858 return
860 cmd = args[0]
861 cmdArgs = args[1:]
863 if not self.handler.handlers.has_key(cmd):
864 raise KickstartParseError, formatErrorMsg(lineno)
865 else:
866 if self.handler.handlers[cmd] != None:
867 self.handler.currentCmd = cmd
868 self.handler.lineno = lineno
869 self.handler.handlers[cmd](cmdArgs)
871 def handlePackageHdr (self, lineno, args):
872 op = KSOptionParser(lineno=lineno)
873 op.add_option("--excludedocs", dest="excludedocs", action="store_true",
874 default=False)
875 op.add_option("--ignoremissing", dest="ignoremissing",
876 action="store_true", default=False)
877 op.add_option("--nobase", dest="nobase", action="store_true",
878 default=False)
879 op.add_option("--ignoredeps", dest="resolveDeps", action="store_false",
880 deprecated=1)
881 op.add_option("--resolvedeps", dest="resolveDeps", action="store_true",
882 deprecated=1)
884 (opts, extra) = op.parse_args(args=args[1:])
886 self.ksdata.excludeDocs = opts.excludedocs
887 self.ksdata.addBase = not opts.nobase
888 if opts.ignoremissing:
889 self.ksdata.handleMissing = KS_MISSING_IGNORE
890 else:
891 self.ksdata.handleMissing = KS_MISSING_PROMPT
893 def handleScriptHdr (self, lineno, args):
894 op = KSOptionParser(lineno=lineno)
895 op.add_option("--erroronfail", dest="errorOnFail", action="store_true",
896 default=False)
897 op.add_option("--interpreter", dest="interpreter", default="/bin/sh")
898 op.add_option("--log", "--logfile", dest="log")
900 if args[0] == "%pre" or args[0] == "%traceback":
901 self.script["chroot"] = False
902 elif args[0] == "%post":
903 self.script["chroot"] = True
904 op.add_option("--nochroot", dest="nochroot", action="store_true",
905 default=False)
907 (opts, extra) = op.parse_args(args=args[1:])
909 self.script["interp"] = opts.interpreter
910 self.script["log"] = opts.log
911 self.script["errorOnFail"] = opts.errorOnFail
912 if hasattr(opts, "nochroot"):
913 self.script["chroot"] = not opts.nochroot
915 # Parser state machine. Don't override this in a subclass.
916 def readKickstart (self, file):
917 # For error reporting.
918 lineno = 0
920 fh = open(file)
921 needLine = True
923 while True:
924 if needLine:
925 line = fh.readline()
926 lineno += 1
927 needLine = False
929 # At the end of an included file
930 if line == "" and self.includeDepth > 0:
931 fh.close()
932 break
934 # Don't eliminate whitespace or comments from scripts.
935 if line.isspace() or (line != "" and line.lstrip()[0] == '#'):
936 # Save the platform for s-c-kickstart, though.
937 if line[:10] == "#platform=" and self.state == STATE_COMMANDS:
938 self.ksdata.platform = line[11:]
940 if self.state in [STATE_PRE, STATE_POST, STATE_TRACEBACK]:
941 self.script["body"].append(line)
943 needLine = True
944 continue
946 # We only want to split the line if we're outside of a script,
947 # as inside the script might involve some pretty weird quoting
948 # that shlex doesn't understand.
949 if self.state in [STATE_PRE, STATE_POST, STATE_TRACEBACK]:
950 # Have we found a state transition? If so, we still want
951 # to split. Otherwise, args won't be set but we'll fall through
952 # all the way to the last case.
953 if line != "" and string.split(line.lstrip())[0] in \
954 ["%post", "%pre", "%traceback", "%include", "%packages"]:
955 args = shlex.split(line)
956 else:
957 args = None
958 else:
959 args = shlex.split(line)
961 if args and args[0] == "%include":
962 # This case comes up primarily in ksvalidator.
963 if not self.followIncludes:
964 needLine = True
965 continue
967 if not args[1]:
968 raise KickstartParseError, formatErrorMsg(lineno)
969 else:
970 self.includeDepth += 1
971 self.readKickstart (args[1])
972 self.includeDepth -= 1
973 needLine = True
974 continue
976 if self.state == STATE_COMMANDS:
977 if not args and self.includeDepth == 0:
978 self.state = STATE_END
979 elif args[0] in ["%pre", "%post", "%traceback"]:
980 self.state = STATE_SCRIPT_HDR
981 elif args[0] == "%packages":
982 self.state = STATE_PACKAGES
983 elif args[0][0] == '%':
984 # This error is too difficult to continue from, without
985 # lots of resync code. So just print this one and quit.
986 raise KickstartParseError, formatErrorMsg(lineno)
987 else:
988 needLine = True
990 if self.errorsAreFatal:
991 self.handleCommand(lineno, args)
992 else:
993 try:
994 self.handleCommand(lineno, args)
995 except Exception, msg:
996 print msg
998 elif self.state == STATE_PACKAGES:
999 if not args and self.includeDepth == 0:
1000 self.state = STATE_END
1001 elif args[0] in ["%pre", "%post", "%traceback"]:
1002 self.state = STATE_SCRIPT_HDR
1003 elif args[0] == "%packages":
1004 needLine = True
1006 if self.errorsAreFatal:
1007 self.handlePackageHdr (lineno, args)
1008 else:
1009 try:
1010 self.handlePackageHdr (lineno, args)
1011 except Exception, msg:
1012 print msg
1013 elif args[0][0] == '%':
1014 # This error is too difficult to continue from, without
1015 # lots of resync code. So just print this one and quit.
1016 raise KickstartParseError, formatErrorMsg(lineno)
1017 else:
1018 needLine = True
1019 self.addPackages (string.rstrip(line))
1021 elif self.state == STATE_SCRIPT_HDR:
1022 needLine = True
1023 self.script = {"body": [], "interp": "/bin/sh", "log": None,
1024 "errorOnFail": False}
1026 if not args and self.includeDepth == 0:
1027 self.state = STATE_END
1028 elif args[0] == "%pre":
1029 self.state = STATE_PRE
1030 self.script["type"] = KS_SCRIPT_PRE
1031 elif args[0] == "%post":
1032 self.state = STATE_POST
1033 self.script["type"] = KS_SCRIPT_POST
1034 elif args[0] == "%traceback":
1035 self.state = STATE_TRACEBACK
1036 self.script["type"] = KS_SCRIPT_TRACEBACK
1037 elif args[0][0] == '%':
1038 # This error is too difficult to continue from, without
1039 # lots of resync code. So just print this one and quit.
1040 raise KickstartParseError, formatErrorMsg(lineno)
1042 if self.errorsAreFatal:
1043 self.handleScriptHdr (lineno, args)
1044 else:
1045 try:
1046 self.handleScriptHdr (lineno, args)
1047 except Exception, msg:
1048 print msg
1050 elif self.state in [STATE_PRE, STATE_POST, STATE_TRACEBACK]:
1051 if line == "" and self.includeDepth == 0:
1052 # If we're at the end of the kickstart file, add the script.
1053 self.addScript()
1054 self.state = STATE_END
1055 elif args and args[0] in ["%pre", "%post", "%traceback", "%packages"]:
1056 # Otherwise we're now at the start of the next section.
1057 # Figure out what kind of a script we just finished
1058 # reading, add it to the list, and switch to the initial
1059 # state.
1060 self.addScript()
1061 self.state = STATE_COMMANDS
1062 else:
1063 # Otherwise just add to the current script body.
1064 self.script["body"].append(line)
1065 needLine = True
1067 elif self.state == STATE_END:
1068 break