Add support for --eject on reboot/shutdown.
[pykickstart.git] / pykickstart / parser.py
blobd0b37e659924efd342fbe323a6977c274cc84156
2 # parser.py: Kickstart file parser.
4 # Chris Lumens <clumens@redhat.com>
6 # Copyright 2005 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 OptionParser, Option
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 self.value = val
48 def __str__ (self):
49 return self.value
51 class KickstartParseError(KickstartError):
52 def __init__(self, msg):
53 self.value = msg
55 def __str__(self):
56 return self.value
58 class KickstartValueError(KickstartError):
59 def __init__(self, msg):
60 self.value = msg
62 def __str__ (self):
63 return self.value
65 ###
66 ### OPTION HANDLING
67 ###
69 # Specialized OptionParser, mainly to handle the MappableOption and to turn
70 # off help.
71 class KSOptionParser(OptionParser):
72 def exit(self, status=0, msg=None):
73 pass
75 def error(self, msg):
76 if self.lineno != None:
77 raise KickstartParseError, formatErrorMsg(self.lineno, msg=msg)
78 else:
79 raise KickstartParseError, msg
81 def keys(self):
82 retval = []
84 for opt in self.option_list:
85 if opt not in retval:
86 retval.append(opt.dest)
88 return retval
90 def _init_parsing_state (self):
91 OptionParser._init_parsing_state(self)
92 self.option_seen = {}
94 def check_values (self, values, args):
95 for option in self.option_list:
96 if (isinstance(option, Option) and option.required and \
97 not self.option_seen.has_key(option)):
98 raise KickstartValueError, formatErrorMsg(self.lineno, "Option %s is required" % option)
99 elif isinstance(option, Option) and option.deprecated and \
100 self.option_seen.has_key(option):
101 warnings.warn("Ignoring deprecated option on line %s: %s" % (self.lineno, option), DeprecationWarning)
103 return (values, args)
105 def __init__(self, map={}, lineno=None):
106 self.map = map
107 self.lineno = lineno
108 OptionParser.__init__(self, option_class=KSBooleanOption,
109 add_help_option=False)
111 # Creates a new Option type that supports a "required" option attribute. Any
112 # option with this attribute must be supplied or an exception is thrown.
113 class RequiredOption (Option):
114 ATTRS = Option.ATTRS + ['required']
116 def _check_required (self):
117 if self.required and not self.takes_value():
118 raise OptionError("Required flag set for option that doesn't take a value", self)
120 # Make sure _check_required() is called from the constructor!
121 CHECK_METHODS = Option.CHECK_METHODS + [_check_required]
123 def process (self, opt, value, values, parser):
124 Option.process(self, opt, value, values, parser)
125 parser.option_seen[self] = 1
127 # Additional OptionParser actions. "map" allows you to define a opt -> val
128 # mapping such that dest gets val when opt is seen. "map_extend" allows you
129 # to define an opt -> [val1, ... valn] mapping such that dest gets a list of
130 # vals build up when opt is seen.
131 class MappableOption(RequiredOption):
132 ACTIONS = RequiredOption.ACTIONS + ("map", "map_extend",)
133 STORE_ACTIONS = RequiredOption.STORE_ACTIONS + ("map", "map_extend",)
135 def take_action(self, action, dest, opt, value, values, parser):
136 if action == "map":
137 values.ensure_value(dest, parser.map[opt.lstrip('-')])
138 elif action == "map_extend":
139 values.ensure_value(dest, []).extend(parser.map[opt.lstrip('-')])
140 else:
141 RequiredOption.take_action(self, action, dest, opt, value, values, parser)
143 # Creates a new Option type that supports a "deprecated" option attribute.
144 # Any option with this attribute will cause a DeprecationWarning to be
145 # thrown if the option is used.
146 class DeprecatedOption(MappableOption):
147 ATTRS = MappableOption.ATTRS + ['deprecated']
149 def process (self, opt, value, values, parser):
150 MappableOption.process(self, opt, value, values, parser)
151 parser.option_seen[self] = 1
153 # Creates a new Option type that supports various booleans for values
154 class KSBooleanOption(DeprecatedOption):
155 def _check_ksboolean(option, opt, value):
156 if value in ("on", "yes", "1"):
157 return True
158 elif value in ("off", "no", "0"):
159 return False
160 else:
161 raise OptionValueError("option %s: invalid boolean "
162 "value: %r" % (opt, value))
164 TYPES = Option.TYPES + ("ksboolean",)
165 TYPE_CHECKER = copy(Option.TYPE_CHECKER)
166 TYPE_CHECKER["ksboolean"] = _check_ksboolean
169 ### SCRIPT HANDLING
172 # You may make a subclass of Script if you need additional script handling
173 # besides just a data representation. For instance, anaconda may subclass
174 # this to add a run method.
175 class Script:
176 def __repr__(self):
177 str = ("(s: '%s' i: %s c: %d)") % \
178 (self.script, self.interp, self.inChroot)
179 return string.replace(str, "\n", "|")
181 def __init__(self, script, interp = "/bin/sh", inChroot = False,
182 logfile = None, errorOnFail = False, type = KS_SCRIPT_PRE):
183 self.script = string.join(script, "")
184 self.interp = interp
185 self.inChroot = inChroot
186 self.logfile = logfile
187 self.errorOnFail = errorOnFail
188 self.type = type
190 # Produce a string representation of the script suitable for writing
191 # to a kickstart file. Add this to the end of the %whatever header.
192 def write(self):
193 str = ""
194 if self.interp != "/bin/sh" and self.interp != "":
195 str = str + " --interp %s" % self.interp
196 if self.type == KS_SCRIPT_POST and not self.inChroot:
197 str = str + " --nochroot"
198 if self.logfile != None:
199 str = str + " --logfile %s" % self.logfile
200 if self.errorOnFail:
201 str = str + " --erroronfail"
203 str = str + "\n%s\n" % self.script
204 return str
207 ### COMMAND HANDLERS
210 # You may make a subclass of KickstartHandlers if you need to do something
211 # besides just build up the data store. If you need to do additional processing
212 # just make a subclass, define handlers for each command in your subclass, and
213 # make sure to call the same handler in the super class before whatever you
214 # want to do. Also if you need to make a new parser that only takes action
215 # for a subset of commands, make a subclass and define all the handlers to
216 # None except the ones you care about.
217 class KickstartHandlers:
218 def __init__ (self, ksdata):
219 self.ksdata = ksdata
221 self.handlers = { "auth" : self.doAuthconfig,
222 "authconfig" : self.doAuthconfig,
223 "autopart" : self.doAutoPart,
224 "autostep" : self.doAutoStep,
225 "bootloader" : self.doBootloader,
226 "cdrom" : self.doMethod,
227 "clearpart" : self.doClearPart,
228 "cmdline" : self.doDisplayMode,
229 "device" : self.doDevice,
230 "deviceprobe" : self.doDeviceProbe,
231 "driverdisk" : self.doDriverDisk,
232 "firewall" : self.doFirewall,
233 "firstboot" : self.doFirstboot,
234 "graphical" : self.doDisplayMode,
235 "halt" : self.doReboot,
236 "harddrive" : self.doMethod,
237 "ignoredisk" : self.doIgnoreDisk,
238 # implied by lack of "upgrade" command
239 "install" : None,
240 "interactive" : self.doInteractive,
241 "keyboard" : self.doKeyboard,
242 "lang" : self.doLang,
243 "langsupport" : self.doLangSupport,
244 "logvol" : self.doLogicalVolume,
245 "mediacheck" : self.doMediaCheck,
246 "monitor" : self.doMonitor,
247 "mouse" : self.doMouse,
248 "network" : self.doNetwork,
249 "nfs" : self.doMethod,
250 "part" : self.doPartition,
251 "partition" : self.doPartition,
252 "poweroff" : self.doReboot,
253 "raid" : self.doRaid,
254 "reboot" : self.doReboot,
255 "rootpw" : self.doRootPw,
256 "selinux" : self.doSELinux,
257 "shutdown" : self.doReboot,
258 "skipx" : self.doSkipX,
259 "text" : self.doDisplayMode,
260 "timezone" : self.doTimezone,
261 "url" : self.doMethod,
262 "upgrade" : self.doUpgrade,
263 "vnc" : self.doVnc,
264 "volgroup" : self.doVolumeGroup,
265 "xconfig" : self.doXConfig,
266 "zerombr" : self.doZeroMbr,
267 "zfcp" : self.doZFCP,
270 def resetHandlers (self):
271 for key in self.handlers.keys():
272 self.handlers[key] = None
274 def deprecatedCommand(self, cmd):
275 warnings.warn("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." % cmd, DeprecationWarning)
277 def doAuthconfig(self, args):
278 self.ksdata.authconfig = string.join(args)
280 def doAutoPart(self, args):
281 self.ksdata.autopart = True
283 def doAutoStep(self, args):
284 op = KSOptionParser(lineno=self.lineno)
285 op.add_option("--autoscreenshot", dest="autoscreenshot",
286 action="store_true", default=False)
288 (opts, extra) = op.parse_args(args=args)
289 self.ksdata.autostep["autoscreenshot"] = opts.autoscreenshot
291 def doBootloader(self, args):
292 def driveorder_cb (option, opt_str, value, parser):
293 for d in value.split(','):
294 parser.values.ensure_value(option.dest, []).append(d)
296 op = KSOptionParser(lineno=self.lineno)
297 op.add_option("--append", dest="appendLine")
298 op.add_option("--location", dest="location", type="choice",
299 default="mbr",
300 choices=["mbr", "partition", "none", "boot"])
301 op.add_option("--lba32", dest="forceLBA", action="store_true",
302 default=False)
303 op.add_option("--password", dest="password", default="")
304 op.add_option("--md5pass", dest="md5pass", default="")
305 op.add_option("--upgrade", dest="upgrade", action="store_true",
306 default=False)
307 op.add_option("--driveorder", dest="driveorder", action="callback",
308 callback=driveorder_cb, nargs=1, type="string")
310 (opts, extra) = op.parse_args(args=args)
312 for key in filter (lambda k: getattr(opts, k) != None, op.keys()):
313 self.ksdata.bootloader[key] = getattr(opts, key)
315 def doClearPart(self, args):
316 def drive_cb (option, opt_str, value, parser):
317 for d in value.split(','):
318 parser.values.ensure_value(option.dest, []).append(d)
320 op = KSOptionParser(lineno=self.lineno)
321 op.add_option("--all", dest="type", action="store_const",
322 const=CLEARPART_TYPE_ALL)
323 op.add_option("--drives", dest="drives", action="callback",
324 callback=drive_cb, nargs=1, type="string")
325 op.add_option("--initlabel", dest="initAll", action="store_true",
326 default=False)
327 op.add_option("--linux", dest="type", action="store_const",
328 const=CLEARPART_TYPE_LINUX)
329 op.add_option("--none", dest="type", action="store_const",
330 const=CLEARPART_TYPE_NONE)
332 (opts, extra) = op.parse_args(args=args)
334 for key in filter (lambda k: getattr(opts, k) != None, op.keys()):
335 self.ksdata.clearpart[key] = getattr(opts, key)
337 def doDevice(self, args):
338 self.ksdata.device = string.join(args)
340 def doDeviceProbe(self, args):
341 self.ksdata.deviceprobe = string.join(args)
343 def doDisplayMode(self, args):
344 if self.currentCmd == "cmdline":
345 self.ksdata.displayMode = DISPLAY_MODE_CMDLINE
346 elif self.currentCmd == "graphical":
347 self.ksdata.displayMode = DISPLAY_MODE_GRAPHICAL
348 elif self.currentCmd == "text":
349 self.ksdata.displayMode = DISPLAY_MODE_TEXT
351 def doDriverDisk(self, args):
352 self.ksdata.driverdisk = string.join(args)
354 def doFirewall(self, args):
355 def firewall_port_cb (option, opt_str, value, parser):
356 for p in value.split(","):
357 p = p.strip()
358 if p.find(":") == -1:
359 p = "%s:tcp" % p
360 parser.values.ensure_value(option.dest, []).append(p)
362 op = KSOptionParser(map={"ssh":["22:tcp"], "telnet":["23:tcp"],
363 "smtp":["25:tcp"], "http":["80:tcp", "443:tcp"],
364 "ftp":["21:tcp"]}, lineno=self.lineno)
366 op.add_option("--disable", "--disabled", dest="enabled",
367 action="store_false")
368 op.add_option("--enable", "--enabled", dest="enabled",
369 action="store_true", default=True)
370 op.add_option("--ftp", "--http", "--smtp", "--ssh", "--telnet",
371 dest="ports", action="map_extend")
372 op.add_option("--port", dest="ports", action="callback",
373 callback=firewall_port_cb, nargs=1, type="string")
374 op.add_option("--trust", dest="trusts", action="append")
376 (opts, extra) = op.parse_args(args=args)
378 for key in filter (lambda k: getattr(opts, k) != None, op.keys()):
379 self.ksdata.firewall[key] = getattr(opts, key)
381 def doFirstboot(self, args):
382 op = KSOptionParser(lineno=self.lineno)
383 op.add_option("--disable", "--disabled", dest="firstboot",
384 action="store_const", const=FIRSTBOOT_SKIP)
385 op.add_option("--enable", "--enabled", dest="firstboot",
386 action="store_const", const=FIRSTBOOT_DEFAULT)
387 op.add_option("--reconfig", dest="firstboot", action="store_const",
388 const=FIRSTBOOT_RECONFIG)
390 (opts, extra) = op.parse_args(args=args)
391 self.ksdata.firstboot = opts.firstboot
393 def doIgnoreDisk(self, args):
394 def drive_cb (option, opt_str, value, parser):
395 for d in value.split(','):
396 parser.values.ensure_value(option.dest, []).append(d)
398 op = KSOptionParser(lineno=self.lineno)
399 op.add_option("--drives", dest="drives", action=callback,
400 callback=drive_cb, nargs=1, type="string")
402 (opts, extra) = op.parse_args(args=args)
404 self.ksdata.ignoredisk = opt.ignoredisk
406 def doInteractive(self, args):
407 self.ksdata.interactive = True
409 def doKeyboard(self, args):
410 self.ksdata.keyboard = args[0]
412 def doLang(self, args):
413 self.ksdata.lang = args[0]
415 def doLangSupport(self, args):
416 self.deprecatedCommand("langsupport")
418 def doLogicalVolume(self, args):
419 def lv_cb (option, opt_str, value, parser):
420 parser.values.ensure_value(option.dest, False)
421 parser.values.ensure_value("preexist", True)
423 op = KSOptionParser(lineno=self.lineno)
424 op.add_option("--bytes-per-inode", dest="bytesPerInode", action="store",
425 type="int", nargs=1)
426 op.add_option("--fsoptions", dest="fsopts")
427 op.add_option("--fstype", dest="fstype")
428 op.add_option("--grow", dest="grow", action="store_true",
429 default=False)
430 op.add_option("--maxsize", dest="maxSizeMB", action="store", type="int",
431 nargs=1)
432 op.add_option("--name", dest="name", required=1)
433 op.add_option("--noformat", action="callback", callback=lv_cb,
434 dest="format", default=True, nargs=0)
435 op.add_option("--percent", dest="percent", action="store", type="int",
436 nargs=1)
437 op.add_option("--recommended", dest="recommended", action="store_true",
438 default=False)
439 op.add_option("--size", dest="size", action="store", type="int",
440 nargs=1)
441 op.add_option("--useexisting", dest="preexist", action="store_true",
442 default=False)
443 op.add_option("--vgname", dest="vgname", required=1)
445 (opts, extra) = op.parse_args(args=args)
447 if len(extra) == 0:
448 raise KickstartValueError, formatErrorMsg(self.lineno, msg="Mount point required for logvol")
450 lvd = KickstartLogVolData()
451 for key in filter (lambda k: getattr(opts, k) != None, op.keys()):
452 setattr(lvd, key, getattr(opts, key))
454 lvd.mountpoint = extra[0]
455 self.ksdata.lvList.append(lvd)
457 def doMediaCheck(self, args):
458 self.ksdata.mediacheck = True
460 def doMethod(self, args):
461 op = KSOptionParser(lineno=self.lineno)
463 self.ksdata.method["method"] = self.currentCmd
465 if self.currentCmd == "cdrom":
466 pass
467 elif self.currentCmd == "harddrive":
468 op.add_option("--partition", dest="partition", required=1)
469 op.add_option("--dir", dest="dir", required=1)
471 (opts, extra) = op.parse_args(args=args)
472 self.ksdata.method["partition"] = opts.partition
473 self.ksdata.method["dir"] = opts.dir
474 elif self.currentCmd == "nfs":
475 op.add_option("--server", dest="server", required=1)
476 op.add_option("--dir", dest="dir", required=1)
478 (opts, extra) = op.parse_args(args=args)
479 self.ksdata.method["server"] = opts.server
480 self.ksdata.method["dir"] = opts.dir
481 elif self.currentCmd == "url":
482 op.add_option("--url", dest="url", required=1)
484 (opts, extra) = op.parse_args(args=args)
485 self.ksdata.method["url"] = opts.url
487 def doMonitor(self, args):
488 op = KSOptionParser(lineno=self.lineno)
489 op.add_option("--hsync", dest="hsync")
490 op.add_option("--monitor", dest="monitor")
491 op.add_option("--noprobe", dest="probe", action="store_false",
492 default=True)
493 op.add_option("--vsync", dest="vsync")
495 (opts, extra) = op.parse_args(args=args)
497 if extra:
498 raise KickstartValueError, formatErrorMsg(self.lineno, msg="Unexpected arguments to monitor command: %s" % extra)
500 for key in filter (lambda k: getattr(opts, k) != None, op.keys()):
501 self.ksdata.monitor[key] = getattr(opts, key)
503 def doMouse(self, args):
504 self.deprecatedCommand("mouse")
506 def doNetwork(self, args):
507 op = KSOptionParser(lineno=self.lineno)
508 op.add_option("--bootproto", dest="bootProto", default="dhcp")
509 op.add_option("--class", dest="dhcpclass")
510 op.add_option("--device", dest="device")
511 op.add_option("--essid", dest="essid")
512 op.add_option("--ethtool", dest="ethtool")
513 op.add_option("--gateway", dest="gateway")
514 op.add_option("--hostname", dest="hostname")
515 op.add_option("--ip", dest="ip")
516 op.add_option("--nameserver", dest="nameserver")
517 op.add_option("--netmask", dest="netmask")
518 op.add_option("--nodns", dest="nodns", action="store_true",
519 default=False)
520 op.add_option("--notksdevice", dest="notksdevice", action="store_true",
521 default=False)
522 op.add_option("--onboot", dest="onboot", action="store",
523 type="ksboolean")
524 op.add_option("--wepkey", dest="wepkey")
526 (opts, extra) = op.parse_args(args=args)
528 nd = KickstartNetworkData()
529 for key in filter (lambda k: getattr(opts, k) != None, op.keys()):
530 setattr(nd, key, getattr(opts, key))
532 self.ksdata.network.append(nd)
534 def doPartition(self, args):
535 def part_cb (option, opt_str, value, parser):
536 if value.startswith("/dev/"):
537 parser.values.ensure_value(option.dest, value[5:])
538 else:
539 parser.values.ensure_value(option.dest, value)
541 op = KSOptionParser(lineno=self.lineno)
542 op.add_option("--active", dest="active", action="store_true",
543 default=False)
544 op.add_option("--asprimary", dest="primOnly", action="store_true",
545 default=False)
546 op.add_option("--bytes-per-inode", dest="bytesPerInode", action="store",
547 type="int", nargs=1)
548 op.add_option("--end", dest="end", action="store", type="int",
549 nargs=1)
550 op.add_option("--fsoptions", dest="fsopts")
551 op.add_option("--fstype", "--type", dest="fstype")
552 op.add_option("--grow", dest="grow", action="store_true", default=False)
553 op.add_option("--label", dest="label")
554 op.add_option("--maxsize", dest="maxSizeMB", action="store", type="int",
555 nargs=1)
556 op.add_option("--noformat", dest="format", action="store_false",
557 default=True)
558 op.add_option("--onbiosdisk", dest="onbiosdisk")
559 op.add_option("--ondisk", "--ondrive", dest="disk")
560 op.add_option("--onpart", "--usepart", dest="onPart", action="callback",
561 callback=part_cb, nargs=1, type="string")
562 op.add_option("--recommended", dest="recommended", action="store_true",
563 default=False)
564 op.add_option("--size", dest="size", action="store", type="int",
565 nargs=1)
566 op.add_option("--start", dest="start", action="store", type="int",
567 nargs=1)
569 (opts, extra) = op.parse_args(args=args)
571 if len(extra) != 1:
572 raise KickstartValueError, formatErrorMsg(self.lineno, msg="Mount point required for partition")
574 pd = KickstartPartData()
575 for key in filter (lambda k: getattr(opts, k) != None, op.keys()):
576 setattr(pd, key, getattr(opts, key))
578 pd.mountpoint = extra[0]
579 self.ksdata.partitions.append(pd)
581 def doReboot(self, args):
582 if self.currentCmd == "reboot":
583 self.ksdata.reboot["action"] = KS_REBOOT
584 else:
585 self.ksdata.reboot["action"] = KS_SHUTDOWN
587 op = KSOptionParser(lineno=self.lineno)
588 op.add_option("--eject", dest="eject", action="store_true",
589 default=False)
591 (opts, extra) = op.parse_args(args=args)
592 self.ksdata.reboot["eject"] = opts.eject
594 def doRaid(self, args):
595 def raid_cb (option, opt_str, value, parser):
596 parser.values.ensure_value(option.dest, False)
597 parser.values.ensure_value("preexist", True)
599 def device_cb (option, opt_str, value, parser):
600 if value[0:2] == "md":
601 parser.values.ensure_value(option.dest, value[2:])
602 else:
603 parser.values.ensure_value(option.dest, value)
605 def level_cb (option, opt_str, value, parser):
606 if value == "RAID0" or value == "0":
607 parser.values.ensure_value(option.dest, "RAID0")
608 elif value == "RAID1" or value == "1":
609 parser.values.ensure_value(option.dest, "RAID1")
610 elif value == "RAID5" or value == "5":
611 parser.values.ensure_value(option.dest, "RAID5")
612 elif value == "RAID6" or value == "6":
613 parser.values.ensure_value(option.dest, "RAID6")
615 op = KSOptionParser(lineno=self.lineno)
616 op.add_option("--bytes-per-inode", dest="bytesPerInode", action="store",
617 type="int", nargs=1)
618 op.add_option("--device", action="callback", callback=device_cb,
619 dest="device", type="string", nargs=1, required=1)
620 op.add_option("--fsoptions", dest="fsopts")
621 op.add_option("--fstype", dest="fstype")
622 op.add_option("--level", dest="level", action="callback",
623 callback=level_cb, type="string", nargs=1, required=1)
624 op.add_option("--noformat", action="callback", callback=raid_cb,
625 dest="format", default=True, nargs=0)
626 op.add_option("--spares", dest="spares", action="store", type="int",
627 nargs=1, default=0)
628 op.add_option("--useexisting", dest="preexist", action="store_true",
629 default=False)
631 (opts, extra) = op.parse_args(args=args)
633 if len(extra) == 0:
634 raise KickstartValueError, formatErrorMsg(self.lineno, msg="Mount point required for raid")
636 rd = KickstartRaidData()
637 for key in filter (lambda k: getattr(opts, k) != None, op.keys()):
638 setattr(rd, key, getattr(opts, key))
640 rd.mountpoint = extra[0]
641 rd.members = extra[1:]
642 self.ksdata.raidList.append(rd)
644 def doRootPw(self, args):
645 op = KSOptionParser(lineno=self.lineno)
646 op.add_option("--iscrypted", dest="isCrypted", action="store_true",
647 default=False)
649 (opts, extra) = op.parse_args(args=args)
650 self.ksdata.rootpw["isCrypted"] = opts.isCrypted
652 if len(extra) != 1:
653 raise KickstartValueError, formatErrorMsg(self.lineno, msg="A single argument is expected for rootpw")
655 self.ksdata.rootpw["password"] = extra[0]
657 def doSELinux(self, args):
658 op = KSOptionParser(lineno=self.lineno)
659 op.add_option("--disabled", dest="sel", action="store_const",
660 const=SELINUX_DISABLED)
661 op.add_option("--enforcing", dest="sel", action="store_const",
662 const=SELINUX_ENFORCING)
663 op.add_option("--permissive", dest="sel", action="store_const",
664 const=SELINUX_PERMISSIVE)
666 (opts, extra) = op.parse_args(args=args)
667 self.ksdata.selinux = opts.sel
669 def doSkipX(self, args):
670 self.ksdata.skipx = True
672 def doTimezone(self, args):
673 op = KSOptionParser(lineno=self.lineno)
674 op.add_option("--utc", dest="isUtc", action="store_true", default=False)
676 (opts, extra) = op.parse_args(args=args)
677 self.ksdata.timezone["isUtc"] = opts.isUtc
679 if len(extra) != 1:
680 raise KickstartValueError, formatErrorMsg(self.lineno, msg="A single argument is expected for timezone")
682 self.ksdata.timezone["timezone"] = extra[0]
684 def doUpgrade(self, args):
685 self.ksdata.upgrade = True
687 def doVnc(self, args):
688 def connect_cb (option, opt_str, value, parser):
689 cargs = opt_str.split(":")
690 parser.values.ensure_value("host", cargs[0])
692 if len(cargs) > 1:
693 parser.values.ensure_value("port", cargs[1])
695 op = KSOptionParser(lineno=self.lineno)
696 op.add_option("--connect", action="callback", callback=connect_cb,
697 nargs=1, type="string")
698 op.add_option("--password", dest="password")
700 (opts, extra) = op.parse_args(args=args)
702 self.ksdata.vnc["enabled"] = True
704 for key in filter (lambda k: getattr(opts, k) != None, op.keys()):
705 self.ksdata.vnc[key] = getattr(opts, key)
707 def doVolumeGroup(self, args):
708 # Have to be a little more complicated to set two values.
709 def vg_cb (option, opt_str, value, parser):
710 parser.values.ensure_value(option.dest, False)
711 parser.values.ensure_value("preexist", True)
713 op = KSOptionParser(lineno=self.lineno)
714 op.add_option("--noformat", action="callback", callback=vg_cb,
715 dest="format", default=True, nargs=0)
716 op.add_option("--pesize", dest="pesize", type="int", nargs=1,
717 default=32768)
718 op.add_option("--useexisting", dest="preexist", action="store_true",
719 default=False)
721 (opts, extra) = op.parse_args(args=args)
723 vgd = KickstartVolGroupData()
724 for key in filter (lambda k: getattr(opts, k) != None, op.keys()):
725 setattr(vgd, key, getattr(opts, key))
727 vgd.vgname = extra[0]
728 vgd.physvols = extra[1:]
729 self.ksdata.vgList.append(vgd)
731 def doXConfig(self, args):
732 op = KSOptionParser(lineno=self.lineno)
733 op.add_option("--card", deprecated=1)
734 op.add_option("--driver", dest="driver")
735 op.add_option("--defaultdesktop", dest="defaultdesktop")
736 op.add_option("--depth", dest="depth", action="store", type="int",
737 nargs=1)
738 op.add_option("--hsync", deprecated=1)
739 op.add_option("--monitor", deprecated=1)
740 op.add_option("--noprobe", deprecated=1)
741 op.add_option("--resolution", dest="resolution")
742 op.add_option("--startxonboot", dest="startX", action="store_true",
743 default=False)
744 op.add_option("--videoram", dest="videoRam")
745 op.add_option("--vsync", deprecated=1)
747 (opts, extra) = op.parse_args(args=args)
748 if extra:
749 raise KickstartValueError, formatErrorMsg(self.lineno, msg="Unexpected arguments to xconfig command: %s" % extra)
751 for key in filter (lambda k: getattr(opts, k) != None, op.keys()):
752 self.ksdata.xconfig[key] = getattr(opts, key)
754 def doZeroMbr(self, args):
755 self.ksdata.zerombr = True
757 def doZFCP(self, args):
758 op = KSOptionParser(lineno=self.lineno)
759 op.add_option("--devnum", dest="devnum", required=1)
760 op.add_option("--fcplun", dest="fcplun", required=1)
761 op.add_option("--scsiid", dest="scsiid", required=1)
762 op.add_option("--scsilun", dest="scsilun", required=1)
763 op.add_option("--wwpn", dest="wwpn", required=1)
765 (opts, extra) = op.parse_args(args=args)
767 for key in filter (lambda k: getattr(opts, k) != None, op.keys()):
768 self.ksdata.zfcp[key] = getattr(opts, key)
771 ### PARSER
774 # The kickstart file parser. This only transitions between states and calls
775 # handlers at certain points. To create a specialized parser, make a subclass
776 # of this and override the methods you care about. Methods that don't need to
777 # do anything may just pass.
779 # Passing None for kshandlers is valid just in case you don't care about
780 # handling any commands.
781 class KickstartParser:
782 def __init__ (self, ksdata, kshandlers):
783 self.handler = kshandlers
784 self.ksdata = ksdata
785 self.followIncludes = True
786 self.state = STATE_COMMANDS
787 self.script = None
788 self.includeDepth = 0
790 # Functions to be called when we are at certain points in the
791 # kickstart file parsing. Override these if you need special
792 # behavior.
793 def addScript (self):
794 if string.join(self.script["body"]).strip() == "":
795 return
797 s = Script (self.script["body"], self.script["interp"],
798 self.script["chroot"], self.script["log"],
799 self.script["errorOnFail"], self.script["type"])
801 self.ksdata.scripts.append(s)
803 def addPackages (self, line):
804 if line[0] == '@':
805 line = line[1:]
806 self.ksdata.groupList.append(line.lstrip())
807 elif line[0] == '-':
808 line = line[1:]
809 self.ksdata.excludedList.append(line.lstrip())
810 else:
811 self.ksdata.packageList.append(line.lstrip())
813 def handleCommand (self, lineno, args):
814 if not self.handler:
815 return
817 cmd = args[0]
818 cmdArgs = args[1:]
820 if not self.handler.handlers.has_key(cmd):
821 raise KickstartParseError, formatErrorMsg(lineno)
822 else:
823 if self.handler.handlers[cmd] != None:
824 setattr(self.handler, "currentCmd", cmd)
825 setattr(self.handler, "lineno", lineno)
826 self.handler.handlers[cmd](cmdArgs)
828 def handlePackageHdr (self, lineno, args):
829 op = KSOptionParser(lineno=lineno)
830 op.add_option("--excludedocs", dest="excludedocs", action="store_true",
831 default=False)
832 op.add_option("--ignoremissing", dest="ignoremissing",
833 action="store_true", default=False)
834 op.add_option("--nobase", dest="nobase", action="store_true",
835 default=False)
836 op.add_option("--ignoredeps", dest="resolveDeps", action="store_false",
837 deprecated=1)
838 op.add_option("--resolvedeps", dest="resolveDeps", action="store_true",
839 deprecated=1)
841 (opts, extra) = op.parse_args(args=args[1:])
843 self.ksdata.excludeDocs = opts.excludedocs
844 self.ksdata.addBase = not opts.nobase
845 if opts.ignoremissing:
846 self.ksdata.handleMissing = KS_MISSING_IGNORE
847 else:
848 self.ksdata.handleMissing = KS_MISSING_PROMPT
850 def handleScriptHdr (self, lineno, args):
851 op = KSOptionParser(lineno=lineno)
852 op.add_option("--erroronfail", dest="errorOnFail", action="store_true",
853 default=False)
854 op.add_option("--interpreter", dest="interpreter", default="/bin/sh")
855 op.add_option("--log", "--logfile", dest="log")
857 if args[0] == "%pre" or args[0] == "%traceback":
858 self.script["chroot"] = False
859 elif args[0] == "%post":
860 self.script["chroot"] = True
861 op.add_option("--nochroot", dest="nochroot", action="store_true",
862 default=False)
864 (opts, extra) = op.parse_args(args=args[1:])
866 self.script["interp"] = opts.interpreter
867 self.script["log"] = opts.log
868 self.script["errorOnFail"] = opts.errorOnFail
869 if hasattr(opts, "nochroot"):
870 self.script["chroot"] = not opts.nochroot
872 def readKickstart (self, file):
873 # For error reporting.
874 lineno = 0
876 fh = open(file)
877 needLine = True
879 while True:
880 if needLine:
881 line = fh.readline()
882 lineno += 1
883 needLine = False
885 # At the end of an included file
886 if line == "" and self.includeDepth > 0:
887 fh.close()
888 break
890 # Don't eliminate whitespace or comments from scripts.
891 if line.isspace() or (line != "" and line.lstrip()[0] == '#'):
892 # Save the platform for s-c-kickstart, though.
893 if line[:10] == "#platform=" and self.state == STATE_COMMANDS:
894 self.ksdata.platform = line[11:]
896 if self.state in [STATE_PRE, STATE_POST, STATE_TRACEBACK]:
897 self.script["body"].append(line)
899 needLine = True
900 continue
902 # We only want to split the line if we're outside of a script,
903 # as inside the script might involve some pretty weird quoting
904 # that shlex doesn't understand.
905 if self.state in [STATE_PRE, STATE_POST, STATE_TRACEBACK]:
906 # Have we found a state transition? If so, we still want
907 # to split. Otherwise, args won't be set but we'll fall through
908 # all the way to the last case.
909 if line != "" and string.split(line.lstrip())[0] in \
910 ["%post", "%pre", "%traceback", "%include", "%packages"]:
911 args = shlex.split(line)
912 else:
913 args = None
914 else:
915 args = shlex.split(line)
917 if args and args[0] == "%include" and self.followIncludes:
918 if not args[1]:
919 raise KickstartParseError, formatErrorMsg(lineno)
920 else:
921 self.includeDepth += 1
922 self.readKickstart (args[1])
923 self.includeDepth -= 1
924 needLine = True
925 continue
927 if self.state == STATE_COMMANDS:
928 if not args and self.includeDepth == 0:
929 self.state = STATE_END
930 elif args[0] in ["%pre", "%post", "%traceback"]:
931 self.state = STATE_SCRIPT_HDR
932 elif args[0] == "%packages":
933 self.state = STATE_PACKAGES
934 elif args[0][0] == '%':
935 raise KickstartParseError, formatErrorMsg(lineno)
936 else:
937 needLine = True
938 self.handleCommand(lineno, args)
940 elif self.state == STATE_PACKAGES:
941 if not args and self.includeDepth == 0:
942 self.state = STATE_END
943 elif args[0] in ["%pre", "%post", "%traceback"]:
944 self.state = STATE_SCRIPT_HDR
945 elif args[0] == "%packages":
946 needLine = True
947 self.handlePackageHdr (lineno, args)
948 elif args[0][0] == '%':
949 raise KickstartParseError, formatErrorMsg(lineno)
950 else:
951 needLine = True
952 self.addPackages (string.rstrip(line))
954 elif self.state == STATE_SCRIPT_HDR:
955 needLine = True
956 self.script = {"body": [], "interp": "/bin/sh", "log": None,
957 "errorOnFail": False}
959 if not args and self.includeDepth == 0:
960 self.state = STATE_END
961 elif args[0] == "%pre":
962 self.state = STATE_PRE
963 self.script["type"] = KS_SCRIPT_PRE
964 elif args[0] == "%post":
965 self.state = STATE_POST
966 self.script["type"] = KS_SCRIPT_POST
967 elif args[0] == "%traceback":
968 self.state = STATE_TRACEBACK
969 self.script["type"] = KS_SCRIPT_TRACEBACK
970 elif args[0][0] == '%':
971 raise KickstartParseError, formatErrorMsg(lineno)
973 self.handleScriptHdr (lineno, args)
975 elif self.state in [STATE_PRE, STATE_POST, STATE_TRACEBACK]:
976 if line == "" and self.includeDepth == 0:
977 # If we're at the end of the kickstart file, add the script.
978 self.addScript()
979 self.state = STATE_END
980 elif args and args[0] in ["%pre", "%post", "%traceback", "%packages"]:
981 # Otherwise we're now at the start of the next section.
982 # Figure out what kind of a script we just finished
983 # reading, add it to the list, and switch to the initial
984 # state.
985 self.addScript()
986 self.state = STATE_COMMANDS
987 else:
988 # Otherwise just add to the current script body.
989 self.script["body"].append(line)
990 needLine = True
992 elif self.state == STATE_END:
993 break