Correct deprecated attribute so warnings are thrown when a deprecated
[pykickstart.git] / pykickstart / parser.py
blob8988de7326d2a852b2aa4c937c12c00579062833
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 optparse import OptionParser, Option
22 from constants import *
23 from data import *
25 STATE_END = 0
26 STATE_COMMANDS = 1
27 STATE_PACKAGES = 2
28 STATE_SCRIPT_HDR = 3
29 STATE_PRE = 4
30 STATE_POST = 5
31 STATE_TRACEBACK = 6
33 ###
34 ### ERROR HANDLING
35 ###
37 class KickstartError(Exception):
38 def __init__(self, val = ""):
39 self.value = val
41 def __str__ (self):
42 return self.value
44 class KickstartParseError(KickstartError):
45 def __init__(self, line = ""):
46 self.value = "There was a problem reading the following line from the kickstart file. This could be due to an error on the line or using a keyword that no longer exists.\n\n%s" % line
48 def __str__(self):
49 return self.value
51 class KickstartValueError(KickstartError):
52 def __init__(self, val = ""):
53 self.value = val
55 def __str__ (self):
56 return self.value
58 ###
59 ### OPTION HANDLING
60 ###
62 # Specialized OptionParser, mainly to handle the MappableOption and to turn
63 # off help.
64 class KSOptionParser(OptionParser):
65 def exit(self, status=0, msg=None):
66 pass
68 def error(self, msg):
69 raise KickstartParseError, msg
71 def keys(self):
72 retval = []
74 for opt in self.option_list:
75 if opt not in retval:
76 retval.append(opt.dest)
78 return retval
80 def _init_parsing_state (self):
81 OptionParser._init_parsing_state(self)
82 self.option_seen = {}
84 def check_values (self, values, args):
85 for option in self.option_list:
86 if (isinstance(option, Option) and option.required and \
87 not self.option_seen.has_key(option)):
88 raise KickstartError, "Option %s is required" % option
89 elif isinstance(option, Option) and option.deprecated and \
90 self.option_seen.has_key(option):
91 warnings.warn("Ignoring deprecated option: %s" % option,
92 DeprecationWarning)
94 return (values, args)
96 def __init__(self, map={}):
97 self.map = map
98 OptionParser.__init__(self, option_class=DeprecatedOption,
99 add_help_option=False)
101 # Creates a new Option type that supports a "required" option attribute. Any
102 # option with this attribute must be supplied or an exception is thrown.
103 class RequiredOption (Option):
104 ATTRS = Option.ATTRS + ['required']
106 def _check_required (self):
107 if self.required and not self.takes_value():
108 raise OptionError("Required flag set for option that doesn't take a value", self)
110 # Make sure _check_required() is called from the constructor!
111 CHECK_METHODS = Option.CHECK_METHODS + [_check_required]
113 def process (self, opt, value, values, parser):
114 Option.process(self, opt, value, values, parser)
115 parser.option_seen[self] = 1
117 # Additional OptionParser actions. "map" allows you to define a opt -> val
118 # mapping such that dest gets val when opt is seen. "map_extend" allows you
119 # to define an opt -> [val1, ... valn] mapping such that dest gets a list of
120 # vals build up when opt is seen.
121 class MappableOption(RequiredOption):
122 ACTIONS = RequiredOption.ACTIONS + ("map", "map_extend",)
123 STORE_ACTIONS = RequiredOption.STORE_ACTIONS + ("map", "map_extend",)
125 def take_action(self, action, dest, opt, value, values, parser):
126 if action == "map":
127 values.ensure_value(dest, parser.map[opt.lstrip('-')])
128 elif action == "map_extend":
129 values.ensure_value(dest, []).extend(parser.map[opt.lstrip('-')])
130 else:
131 RequiredOption.take_action(self, action, dest, opt, value, values, parser)
133 # Creates a new Option type that supports a "deprecated" option attribute.
134 # Any option with this attribute will cause a DeprecationWarning to be
135 # thrown if the option is used.
136 class DeprecatedOption(MappableOption):
137 ATTRS = MappableOption.ATTRS + ['deprecated']
139 def process (self, opt, value, values, parser):
140 MappableOption.process(self, opt, value, values, parser)
141 parser.option_seen[self] = 1
144 ### SCRIPT HANDLING
147 # You may make a subclass of Script if you need additional script handling
148 # besides just a data representation. For instance, anaconda may subclass
149 # this to add a run method.
150 class Script:
151 def __repr__(self):
152 str = ("(s: '%s' i: %s c: %d)") % \
153 (self.script, self.interp, self.inChroot)
154 return string.replace(str, "\n", "|")
156 def __init__(self, script, interp = "/bin/sh", inChroot = False,
157 logfile = None, errorOnFail = False, type = KS_SCRIPT_PRE):
158 self.script = string.join(script, "")
159 self.interp = interp
160 self.inChroot = inChroot
161 self.logfile = logfile
162 self.errorOnFail = errorOnFail
163 self.type = type
165 # Produce a string representation of the script suitable for writing
166 # to a kickstart file. Add this to the end of the %whatever header.
167 def write(self):
168 str = ""
169 if self.interp != "/bin/sh" and self.interp != "":
170 str = str + " --interp %s" % self.interp
171 if self.type == KS_SCRIPT_POST and not self.inChroot:
172 str = str + " --nochroot"
173 if self.logfile != None:
174 str = str + " --logfile %s" % self.logfile
175 if self.errorOnFail:
176 str = str + " --erroronfail"
178 str = str + "\n%s\n" % self.script
179 return str
182 ### COMMAND HANDLERS
185 # You may make a subclass of KickstartHandlers if you need to do something
186 # besides just build up the data store. If you need to do additional processing
187 # just make a subclass, define handlers for each command in your subclass, and
188 # make sure to call the same handler in the super class before whatever you
189 # want to do. Also if you need to make a new parser that only takes action
190 # for a subset of commands, make a subclass and define all the handlers to
191 # None except the ones you care about.
192 class KickstartHandlers:
193 def __init__ (self, ksdata):
194 self.ksdata = ksdata
196 self.handlers = { "auth" : self.doAuthconfig,
197 "authconfig" : self.doAuthconfig,
198 "autopart" : self.doAutoPart,
199 "autostep" : self.doAutoStep,
200 "bootloader" : self.doBootloader,
201 "cdrom" : self.doMethod,
202 "clearpart" : self.doClearPart,
203 "cmdline" : self.doDisplayMode,
204 "device" : self.doDevice,
205 "deviceprobe" : self.doDeviceProbe,
206 "driverdisk" : self.doDriverDisk,
207 "firewall" : self.doFirewall,
208 "firstboot" : self.doFirstboot,
209 "graphical" : self.doDisplayMode,
210 "halt" : self.doReboot,
211 "harddrive" : self.doMethod,
212 "ignoredisk" : self.doIgnoreDisk,
213 # implied by lack of "upgrade" command
214 "install" : None,
215 "interactive" : self.doInteractive,
216 "keyboard" : self.doKeyboard,
217 "lang" : self.doLang,
218 "langsupport" : self.doLangSupport,
219 "logvol" : self.doLogicalVolume,
220 "mediacheck" : self.doMediaCheck,
221 "monitor" : self.doMonitor,
222 "mouse" : self.doMouse,
223 "network" : self.doNetwork,
224 "nfs" : self.doMethod,
225 "part" : self.doPartition,
226 "partition" : self.doPartition,
227 "poweroff" : self.doReboot,
228 "raid" : self.doRaid,
229 "reboot" : self.doReboot,
230 "rootpw" : self.doRootPw,
231 "selinux" : self.doSELinux,
232 "shutdown" : self.doReboot,
233 "skipx" : self.doSkipX,
234 "text" : self.doDisplayMode,
235 "timezone" : self.doTimezone,
236 "url" : self.doMethod,
237 "upgrade" : self.doUpgrade,
238 "vnc" : self.doVnc,
239 "volgroup" : self.doVolumeGroup,
240 "xconfig" : self.doXConfig,
241 "zerombr" : self.doZeroMbr,
242 "zfcp" : self.doZFCP,
245 def resetHandlers (self):
246 for key in self.handlers.keys():
247 self.handlers[key] = None
249 def deprecatedCommand(self, cmd):
250 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)
252 def doAuthconfig(self, args):
253 self.ksdata.authconfig = string.join(args)
255 def doAutoPart(self, args):
256 self.ksdata.autopart = True
258 def doAutoStep(self, args):
259 op = KSOptionParser()
260 op.add_option("--autoscreenshot", dest="autoscreenshot",
261 action="store_true", default=False)
263 (opts, extra) = op.parse_args(args=args)
264 self.ksdata.autostep["autoscreenshot"] = opts.autoscreenshot
266 def doBootloader(self, args):
267 def driveorder_cb (option, opt_str, value, parser):
268 for d in value.split(','):
269 parser.values.ensure_value(option.dest, []).append(d)
271 op = KSOptionParser()
272 op.add_option("--append", dest="appendLine")
273 op.add_option("--location", dest="location", type="choice",
274 default="mbr",
275 choices=["mbr", "partition", "none", "boot"])
276 op.add_option("--lba32", dest="forceLBA", action="store_true",
277 default=False)
278 op.add_option("--password", dest="password", default="")
279 op.add_option("--md5pass", dest="md5pass", default="")
280 op.add_option("--upgrade", dest="upgrade", action="store_true",
281 default=False)
282 op.add_option("--driveorder", dest="driveorder", action="callback",
283 callback=driveorder_cb, nargs=1, type="string")
285 (opts, extra) = op.parse_args(args=args)
287 for key in filter (lambda k: getattr(opts, k) != None, op.keys()):
288 self.ksdata.bootloader[key] = getattr(opts, key)
290 def doClearPart(self, args):
291 def drive_cb (option, opt_str, value, parser):
292 for d in value.split(','):
293 parser.values.ensure_value(option.dest, []).append(d)
295 op = KSOptionParser()
296 op.add_option("--all", dest="type", action="store_const",
297 const=CLEARPART_TYPE_ALL)
298 op.add_option("--drives", dest="drives", action="callback",
299 callback=drive_cb, nargs=1, type="string")
300 op.add_option("--initlabel", dest="initAll", action="store_true",
301 default=False)
302 op.add_option("--linux", dest="type", action="store_const",
303 const=CLEARPART_TYPE_LINUX)
304 op.add_option("--none", dest="type", action="store_const",
305 const=CLEARPART_TYPE_NONE)
307 (opts, extra) = op.parse_args(args=args)
309 for key in filter (lambda k: getattr(opts, k) != None, op.keys()):
310 self.ksdata.clearpart[key] = getattr(opts, key)
312 def doDevice(self, args):
313 self.ksdata.device = string.join(args)
315 def doDeviceProbe(self, args):
316 self.ksdata.deviceprobe = string.join(args)
318 def doDisplayMode(self, args):
319 if self.currentCmd == "cmdline":
320 self.ksdata.displayMode = DISPLAY_MODE_CMDLINE
321 elif self.currentCmd == "graphical":
322 self.ksdata.displayMode = DISPLAY_MODE_GRAPHICAL
323 elif self.currentCmd == "text":
324 self.ksdata.displayMode = DISPLAY_MODE_TEXT
326 def doDriverDisk(self, args):
327 self.ksdata.driverdisk = string.join(args)
329 def doFirewall(self, args):
330 def firewall_port_cb (option, opt_str, value, parser):
331 for p in value.split(","):
332 p = p.strip()
333 if p.find(":") == -1:
334 p = "%s:tcp" % p
335 parser.values.ensure_value(option.dest, []).append(p)
337 op = KSOptionParser({"ssh":["22:tcp"], "telnet":["23:tcp"],
338 "smtp":["25:tcp"], "http":["80:tcp", "443:tcp"],
339 "ftp":["21:tcp"]})
341 op.add_option("--disable", "--disabled", dest="enabled",
342 action="store_false")
343 op.add_option("--enable", "--enabled", dest="enabled",
344 action="store_true", default=True)
345 op.add_option("--ftp", "--http", "--smtp", "--ssh", "--telnet",
346 dest="ports", action="map_extend")
347 op.add_option("--port", dest="ports", action="callback",
348 callback=firewall_port_cb, nargs=1, type="string")
349 op.add_option("--trust", dest="trusts", action="append")
351 (opts, extra) = op.parse_args(args=args)
353 for key in filter (lambda k: getattr(opts, k) != None, op.keys()):
354 self.ksdata.firewall[key] = getattr(opts, key)
356 def doFirstboot(self, args):
357 op = KSOptionParser()
358 op.add_option("--disable", "--disabled", dest="firstboot",
359 action="store_const", const=FIRSTBOOT_SKIP)
360 op.add_option("--enable", "--enabled", dest="firstboot",
361 action="store_const", const=FIRSTBOOT_DEFAULT)
362 op.add_option("--reconfig", dest="firstboot", action="store_const",
363 const=FIRSTBOOT_RECONFIG)
365 (opts, extra) = op.parse_args(args=args)
366 self.ksdata.firstboot = opts.firstboot
368 def doIgnoreDisk(self, args):
369 def drive_cb (option, opt_str, value, parser):
370 for d in value.split(','):
371 parser.values.ensure_value(option.dest, []).append(d)
373 op = KSOptionParser()
374 op.add_option("--drives", dest="drives", action=callback,
375 callback=drive_cb, nargs=1, type="string")
377 (opts, extra) = op.parse_args(args=args)
379 self.ksdata.ignoredisk = opt.ignoredisk
381 def doInteractive(self, args):
382 self.ksdata.interactive = True
384 def doKeyboard(self, args):
385 self.ksdata.keyboard = args[0]
387 def doLang(self, args):
388 self.ksdata.lang = args[0]
390 def doLangSupport(self, args):
391 self.deprecatedCommand("langsupport")
393 def doLogicalVolume(self, args):
394 def lv_cb (option, opt_str, value, parser):
395 parser.values.ensure_value(option.dest, False)
396 parser.values.ensure_value("preexist", True)
398 op = KSOptionParser()
399 op.add_option("--bytes-per-inode", dest="bytesPerInode", action="store",
400 type="int", nargs=1)
401 op.add_option("--fsoptions", dest="fsopts")
402 op.add_option("--fstype", dest="fstype")
403 op.add_option("--grow", dest="grow", action="store_true",
404 default=False)
405 op.add_option("--maxsize", dest="maxSizeMB", action="store", type="int",
406 nargs=1)
407 op.add_option("--name", dest="name", required=1)
408 op.add_option("--noformat", action="callback", callback=lv_cb,
409 dest="format", default=True, nargs=0)
410 op.add_option("--percent", dest="percent", action="store", type="int",
411 nargs=1)
412 op.add_option("--recommended", dest="recommended", action="store_true",
413 default=False)
414 op.add_option("--size", dest="size", action="store", type="int",
415 nargs=1)
416 op.add_option("--useexisting", dest="preexist", action="store_true",
417 default=False)
418 op.add_option("--vgname", dest="vgname", required=1)
420 (opts, extra) = op.parse_args(args=args)
422 if len(extra) == 0:
423 raise KickstartValueError, "Mount point required on line:\n\nlogvol %s" % string.join (args)
425 lvd = KickstartLogVolData()
426 for key in filter (lambda k: getattr(opts, k) != None, op.keys()):
427 setattr(lvd, key, getattr(opts, key))
429 lvd.mountpoint = extra[0]
430 self.ksdata.lvList.append(lvd)
432 def doMediaCheck(self, args):
433 self.ksdata.mediacheck = True
435 def doMethod(self, args):
436 op = KSOptionParser()
438 self.ksdata.method["method"] = self.currentCmd
440 if self.currentCmd == "cdrom":
441 pass
442 elif self.currentCmd == "harddrive":
443 op.add_option("--partition", dest="partition", required=1)
444 op.add_option("--dir", dest="dir", required=1)
446 (opts, extra) = op.parse_args(args=args)
447 self.ksdata.method["partition"] = opts.partition
448 self.ksdata.method["dir"] = opts.dir
449 elif self.currentCmd == "nfs":
450 op.add_option("--server", dest="server", required=1)
451 op.add_option("--dir", dest="dir", required=1)
453 (opts, extra) = op.parse_args(args=args)
454 self.ksdata.method["server"] = opts.server
455 self.ksdata.method["dir"] = opts.dir
456 elif self.currentCmd == "url":
457 op.add_option("--url", dest="url", required=1)
459 (opts, extra) = op.parse_args(args=args)
460 self.ksdata.method["url"] = opts.url
462 def doMonitor(self, args):
463 op = KSOptionParser()
464 op.add_option("--hsync", dest="hsync")
465 op.add_option("--monitor", dest="monitor")
466 op.add_option("--vsync", dest="vsync")
468 (opts, extra) = op.parse_args(args=args)
470 if extra:
471 raise KickstartValueError, "Unexpected arguments to monitor: %s" % string.join(args)
473 for key in filter (lambda k: getattr(opts, k) != None, op.keys()):
474 self.ksdata.monitor[key] = getattr(opts, key)
476 def doMouse(self, args):
477 self.deprecatedCommand("mouse")
479 def doNetwork(self, args):
480 def onboot_cb (option, opt_str, value, parser):
481 if value == "no":
482 parser.values.ensure_value(option.dest, False)
483 else:
484 parser.values.ensure_value(option.dest, True)
486 op = KSOptionParser()
487 op.add_option("--bootproto", dest="bootProto", default="dhcp")
488 op.add_option("--class", dest="dhcpclass")
489 op.add_option("--device", dest="device")
490 op.add_option("--essid", dest="essid")
491 op.add_option("--ethtool", dest="ethtool")
492 op.add_option("--gateway", dest="gateway")
493 op.add_option("--hostname", dest="hostname")
494 op.add_option("--ip", dest="ip")
495 op.add_option("--nameserver", dest="nameserver")
496 op.add_option("--netmask", dest="netmask")
497 op.add_option("--nodns", dest="nodns", action="store_true",
498 default=False)
499 op.add_option("--notksdevice", dest="notksdevice", action="store_true",
500 default=False)
501 op.add_option("--onboot", dest="onboot", action="callback",
502 callback=onboot_cb, nargs=1, type="string")
503 op.add_option("--wepkey", dest="wepkey")
505 (opts, extra) = op.parse_args(args=args)
507 nd = KickstartNetworkData()
508 for key in filter (lambda k: getattr(opts, k) != None, op.keys()):
509 setattr(nd, key, getattr(opts, key))
511 self.ksdata.network.append(nd)
513 def doPartition(self, args):
514 def part_cb (option, opt_str, value, parser):
515 if value.startswith("/dev/"):
516 parser.values.ensure_value(option.dest, value[5:])
517 else:
518 parser.values.ensure_value(option.dest, value)
520 op = KSOptionParser()
521 op.add_option("--active", dest="active", action="store_true",
522 default=False)
523 op.add_option("--asprimary", dest="primOnly", action="store_true",
524 default=False)
525 op.add_option("--bytes-per-inode", dest="bytesPerInode", action="store",
526 type="int", nargs=1)
527 op.add_option("--end", dest="end", action="store", type="int",
528 nargs=1)
529 op.add_option("--fsoptions", dest="fsopts")
530 op.add_option("--fstype", "--type", dest="fstype")
531 op.add_option("--grow", dest="grow", action="store_true", default=False)
532 op.add_option("--label", dest="label")
533 op.add_option("--maxsize", dest="maxSizeMB", action="store", type="int",
534 nargs=1)
535 op.add_option("--noformat", dest="format", action="store_false",
536 default=True)
537 op.add_option("--onbiosdisk", dest="onbiosdisk")
538 op.add_option("--ondisk", "--ondrive", dest="disk")
539 op.add_option("--onpart", "--usepart", dest="onPart", action="callback",
540 callback=part_cb, nargs=1, type="string")
541 op.add_option("--recommended", dest="recommended", action="store_true",
542 default=False)
543 op.add_option("--size", dest="size", action="store", type="int",
544 nargs=1)
545 op.add_option("--start", dest="start", action="store", type="int",
546 nargs=1)
548 (opts, extra) = op.parse_args(args=args)
550 if len(extra) != 1:
551 raise KickstartValueError, "Mount point required on line:\n\npartition %s" % string.join (args)
553 pd = KickstartPartData()
554 for key in filter (lambda k: getattr(opts, k) != None, op.keys()):
555 setattr(pd, key, getattr(opts, key))
557 pd.mountpoint = extra[0]
558 self.ksdata.partitions.append(pd)
560 def doReboot(self, args):
561 self.ksdata.reboot = True
563 def doRaid(self, args):
564 def raid_cb (option, opt_str, value, parser):
565 parser.values.ensure_value(option.dest, False)
566 parser.values.ensure_value("preexist", True)
568 def device_cb (option, opt_str, value, parser):
569 if value[0:2] == "md":
570 parser.values.ensure_value(option.dest, value[2:])
571 else:
572 parser.values.ensure_value(option.dest, value)
574 def level_cb (option, opt_str, value, parser):
575 if value == "RAID0" or value == "0":
576 parser.values.ensure_value(option.dest, "RAID0")
577 elif value == "RAID1" or value == "1":
578 parser.values.ensure_value(option.dest, "RAID1")
579 elif value == "RAID5" or value == "5":
580 parser.values.ensure_value(option.dest, "RAID5")
581 elif value == "RAID6" or value == "6":
582 parser.values.ensure_value(option.dest, "RAID6")
584 op = KSOptionParser()
585 op.add_option("--device", action="callback", callback=device_cb,
586 dest="device", type="string", nargs=1, required=1)
587 op.add_option("--fsoptions", dest="fsopts")
588 op.add_option("--fstype", dest="fstype")
589 op.add_option("--level", dest="level", action="callback",
590 callback=level_cb, type="string", nargs=1, required=1)
591 op.add_option("--noformat", action="callback", callback=raid_cb,
592 dest="format", default=True, nargs=0)
593 op.add_option("--spares", dest="spares", action="store", type="int",
594 nargs=1, default=0)
595 op.add_option("--useexisting", dest="preexist", action="store_true",
596 default=False)
598 (opts, extra) = op.parse_args(args=args)
600 if len(extra) == 0:
601 raise KickstartValueError, "Mount point required on line:\n\nraid %s" % string.join (args)
603 rd = KickstartRaidData()
604 for key in filter (lambda k: getattr(opts, k) != None, op.keys()):
605 setattr(rd, key, getattr(opts, key))
607 rd.mountpoint = extra[0]
608 rd.members = extra[1:]
609 self.ksdata.raidList.append(rd)
611 def doRootPw(self, args):
612 op = KSOptionParser()
613 op.add_option("--iscrypted", dest="isCrypted", action="store_true",
614 default=False)
616 (opts, extra) = op.parse_args(args=args)
617 self.ksdata.rootpw["isCrypted"] = opts.isCrypted
619 if len(extra) != 1:
620 raise KickstartValueError, "A single argument is expected for rootpw"
622 self.ksdata.rootpw["password"] = extra[0]
624 def doSELinux(self, args):
625 op = KSOptionParser()
626 op.add_option("--disabled", dest="sel", action="store_const",
627 const=SELINUX_DISABLED)
628 op.add_option("--enforcing", dest="sel", action="store_const",
629 const=SELINUX_ENFORCING)
630 op.add_option("--permissive", dest="sel", action="store_const",
631 const=SELINUX_PERMISSIVE)
633 (opts, extra) = op.parse_args(args=args)
634 self.ksdata.selinux = opts.sel
636 def doSkipX(self, args):
637 self.ksdata.skipx = True
639 def doTimezone(self, args):
640 op = KSOptionParser()
641 op.add_option("--utc", dest="isUtc", action="store_true", default=False)
643 (opts, extra) = op.parse_args(args=args)
644 self.ksdata.timezone["isUtc"] = opts.isUtc
646 if len(extra) != 1:
647 raise KickstartValueError, "A single argument is expected for timezone"
649 self.ksdata.timezone["timezone"] = extra[0]
651 def doUpgrade(self, args):
652 self.ksdata.upgrade = True
654 def doVnc(self, args):
655 def connect_cb (option, opt_str, value, parser):
656 cargs = opt_str.split(":")
657 parser.values.ensure_value("host", cargs[0])
659 if len(cargs) > 1:
660 parser.values.ensure_value("port", cargs[1])
662 op = KSOptionParser()
663 op.add_option("--connect", action="callback", callback=connect_cb,
664 nargs=1, type="string", required=1)
665 op.add_option("--password", dest="password")
667 (opts, extra) = op.parse_args(args=args)
669 self.ksdata.vnc["enabled"] = True
671 for key in filter (lambda k: getattr(opts, k) != None, op.keys()):
672 self.ksdata.vnc[key] = getattr(opts, key)
674 def doVolumeGroup(self, args):
675 # Have to be a little more complicated to set two values.
676 def vg_cb (option, opt_str, value, parser):
677 parser.values.ensure_value(option.dest, False)
678 parser.values.ensure_value("preexist", True)
680 op = KSOptionParser()
681 op.add_option("--noformat", action="callback", callback=vg_cb,
682 dest="format", default=True, nargs=0)
683 op.add_option("--pesize", dest="pesize", type="int", nargs=1,
684 default=32768)
685 op.add_option("--useexisting", dest="preexist", action="store_true",
686 default=False)
688 (opts, extra) = op.parse_args(args=args)
690 vgd = KickstartVolGroupData()
691 for key in filter (lambda k: getattr(opts, k) != None, op.keys()):
692 setattr(vgd, key, getattr(opts, key))
694 vgd.vgname = extra[0]
695 vgd.physvols = extra[1:]
696 self.ksdata.vgList.append(vgd)
698 def doXConfig(self, args):
699 op = KSOptionParser()
700 op.add_option("--card", deprecated=1)
701 op.add_option("--driver", dest="driver")
702 op.add_option("--defaultdesktop", dest="defaultdesktop")
703 op.add_option("--depth", dest="depth", action="store", type="int",
704 nargs=1)
705 op.add_option("--hsync", dest="hsync")
706 op.add_option("--monitor", dest="monitor")
707 op.add_option("--noprobe", dest="probe", action="store_false",
708 default=True)
709 op.add_option("--resolution", dest="resolution")
710 op.add_option("--startxonboot", dest="startX", action="store_true",
711 default=False)
712 op.add_option("--videoram", dest="videoRam")
713 op.add_option("--vsync", dest="vsync")
715 (opts, extra) = op.parse_args(args=args)
716 if extra:
717 raise KickstartValueError, "Unexpected arguments to xconfig: %s" % string.join (args)
719 for key in filter (lambda k: getattr(opts, k) != None, op.keys()):
720 self.ksdata.xconfig[key] = getattr(opts, key)
722 def doZeroMbr(self, args):
723 self.ksdata.zerombr = True
725 def doZFCP(self, args):
726 op = KSOptionParser()
727 op.add_option("--devnum", dest="devnum", required=1)
728 op.add_option("--fcplun", dest="fcplun", required=1)
729 op.add_option("--scsiid", dest="scsiid", required=1)
730 op.add_option("--scsilun", dest="scsilun", required=1)
731 op.add_option("--wwpn", dest="wwpn", required=1)
733 (opts, extra) = op.parse_args(args=args)
735 for key in filter (lambda k: getattr(opts, k) != None, op.keys()):
736 self.ksdata.zfcp[key] = getattr(opts, key)
739 ### PARSER
742 # The kickstart file parser. This only transitions between states and calls
743 # handlers at certain points. To create a specialized parser, make a subclass
744 # of this and override the methods you care about. Methods that don't need to
745 # do anything may just pass.
747 # Passing None for kshandlers is valid just in case you don't care about
748 # handling any commands.
749 class KickstartParser:
750 def __init__ (self, ksdata, kshandlers):
751 self.handler = kshandlers
752 self.ksdata = ksdata
753 self.followIncludes = True
754 self.state = STATE_COMMANDS
755 self.script = None
756 self.includeDepth = 0
758 # Functions to be called when we are at certain points in the
759 # kickstart file parsing. Override these if you need special
760 # behavior.
761 def addScript (self):
762 if string.join(self.script["body"]).strip() == "":
763 return
765 s = Script (self.script["body"], self.script["interp"],
766 self.script["chroot"], self.script["log"],
767 self.script["errorOnFail"], self.script["type"])
769 self.ksdata.scripts.append(s)
771 def addPackages (self, line):
772 if line[0] == '@':
773 line = line[1:]
774 self.ksdata.groupList.append(line.lstrip())
775 elif line[0] == '-':
776 line = line[1:]
777 self.ksdata.excludedList.append(line.lstrip())
778 else:
779 self.ksdata.packageList.append(line.lstrip())
781 def handleCommand (self, cmd, args):
782 if not self.handler:
783 return
785 if not self.handler.handlers.has_key(cmd):
786 raise KickstartParseError, (cmd + " " + string.join (args))
787 else:
788 if self.handler.handlers[cmd] != None:
789 setattr(self.handler, "currentCmd", cmd)
790 self.handler.handlers[cmd](args)
792 def handlePackageHdr (self, args):
793 op = KSOptionParser()
794 op.add_option("--excludedocs", dest="excludedocs", action="store_true",
795 default=False)
796 op.add_option("--ignoremissing", dest="ignoremissing",
797 action="store_true", default=False)
798 op.add_option("--nobase", dest="nobase", action="store_true",
799 default=False)
801 (opts, extra) = op.parse_args(args=args[1:])
803 self.ksdata.excludeDocs = opts.excludedocs
804 self.ksdata.addBase = not opts.nobase
805 if opts.ignoremissing:
806 self.ksdata.handleMissing = KS_MISSING_IGNORE
807 else:
808 self.ksdata.handleMissing = KS_MISSING_PROMPT
810 def handleScriptHdr (self, args):
811 op = KSOptionParser()
812 op.add_option("--erroronfail", dest="errorOnFail", action="store_true",
813 default=False)
814 op.add_option("--interpreter", dest="interpreter", default="/bin/sh")
815 op.add_option("--log", "--logfile", dest="log")
817 if args[0] == "%pre" or args[0] == "%traceback":
818 self.script["chroot"] = False
819 elif args[0] == "%post":
820 self.script["chroot"] = True
821 op.add_option("--nochroot", dest="nochroot", action="store_true",
822 default=False)
824 (opts, extra) = op.parse_args(args=args[1:])
826 self.script["interp"] = opts.interpreter
827 self.script["log"] = opts.log
828 self.script["errorOnFail"] = opts.errorOnFail
829 if hasattr(opts, "nochroot"):
830 self.script["chroot"] = not opts.nochroot
832 def readKickstart (self, file):
833 packages = []
834 groups = []
835 excludedPackages = []
837 fh = open(file)
838 needLine = True
840 while True:
841 if needLine:
842 line = fh.readline()
843 needLine = False
845 if line == "" and self.includeDepth > 0:
846 fh.close()
847 break
849 # Don't eliminate whitespace or comments from scripts.
850 if line.isspace() or (line != "" and line[0] == '#'):
851 # Save the platform for s-c-kickstart, though.
852 if line[:10] == "#platform=" and self.state == STATE_COMMANDS:
853 self.ksdata.platform = line[11:]
855 if self.state in [STATE_PRE, STATE_POST, STATE_TRACEBACK]:
856 self.script["body"].append(line)
858 needLine = True
859 continue
861 args = shlex.split(line)
863 if args and args[0] == "%include" and self.followIncludes:
864 if not args[1]:
865 raise KickstartParseError, line
866 else:
867 self.includeDepth += 1
868 self.readKickstart (args[1])
869 self.includeDepth -= 1
870 needLine = True
871 continue
873 if self.state == STATE_COMMANDS:
874 if not args and self.includeDepth == 0:
875 self.state = STATE_END
876 elif args[0] in ["%pre", "%post", "%traceback"]:
877 self.state = STATE_SCRIPT_HDR
878 elif args[0] == "%packages":
879 self.state = STATE_PACKAGES
880 elif args[0][0] == '%':
881 raise KickstartParseError, line
882 else:
883 needLine = True
884 self.handleCommand(args[0], args[1:])
886 elif self.state == STATE_PACKAGES:
887 if not args and self.includeDepth == 0:
888 self.state = STATE_END
889 elif args[0] in ["%pre", "%post", "%traceback"]:
890 self.state = STATE_SCRIPT_HDR
891 elif args[0] == "%packages":
892 needLine = True
893 self.handlePackageHdr (args)
894 elif args[0][0] == '%':
895 raise KickstartParseError, line
896 else:
897 needLine = True
898 self.addPackages (string.rstrip(line))
900 elif self.state == STATE_SCRIPT_HDR:
901 needLine = True
902 self.script = {"body": [], "interp": "/bin/sh", "log": None,
903 "errorOnFail": False}
905 if not args and self.includeDepth == 0:
906 self.state = STATE_END
907 elif args[0] == "%pre":
908 self.state = STATE_PRE
909 self.script["type"] = KS_SCRIPT_PRE
910 elif args[0] == "%post":
911 self.state = STATE_POST
912 self.script["type"] = KS_SCRIPT_POST
913 elif args[0] == "%traceback":
914 self.state = STATE_TRACEBACK
915 self.script["type"] = KS_SCRIPT_TRACEBACK
916 elif args[0][0] == '%':
917 raise KickstartParseError, line
919 self.handleScriptHdr (args)
921 elif self.state in [STATE_PRE, STATE_POST, STATE_TRACEBACK]:
922 # If this is part of a script, append to it.
923 if not args and self.includeDepth == 0:
924 self.addScript()
925 self.state = STATE_END
926 elif args[0] in ["%pre", "%post", "%traceback", "%packages"]:
927 # Otherwise, figure out what kind of a script we just
928 # finished reading, add it to the list, and switch to
929 # the initial state.
930 self.addScript()
931 self.state = STATE_COMMANDS
932 else:
933 self.script["body"].append(line)
934 needLine = True
936 elif self.state == STATE_END:
937 break