--onboot takes no option.
[pykickstart.git] / pykickstart / parser.py
blob541a637061a07d7c70e2fd368898a8e0d2cb6169
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 def formatErrorMsg(lineno, msg=""):
38 if msg != "":
39 return "The following problem occurred on line %s of the kickstart file:\n\n%s\n" % (lineno, msg)
40 else:
41 return "There was a problem reading from line %s of the kickstart file" % lineno
43 class KickstartError(Exception):
44 def __init__(self, val = ""):
45 self.value = val
47 def __str__ (self):
48 return self.value
50 class KickstartParseError(KickstartError):
51 def __init__(self, msg):
52 self.value = msg
54 def __str__(self):
55 return self.value
57 class KickstartValueError(KickstartError):
58 def __init__(self, msg):
59 self.value = msg
61 def __str__ (self):
62 return self.value
64 ###
65 ### OPTION HANDLING
66 ###
68 # Specialized OptionParser, mainly to handle the MappableOption and to turn
69 # off help.
70 class KSOptionParser(OptionParser):
71 def exit(self, status=0, msg=None):
72 pass
74 def error(self, msg):
75 if self.lineno != None:
76 raise KickstartParseError, formatErrorMsg(self.lineno, msg=msg)
77 else:
78 raise KickstartParseError, msg
80 def keys(self):
81 retval = []
83 for opt in self.option_list:
84 if opt not in retval:
85 retval.append(opt.dest)
87 return retval
89 def _init_parsing_state (self):
90 OptionParser._init_parsing_state(self)
91 self.option_seen = {}
93 def check_values (self, values, args):
94 for option in self.option_list:
95 if (isinstance(option, Option) and option.required and \
96 not self.option_seen.has_key(option)):
97 raise KickstartValueError, formatErrorMsg(self.lineno, "Option %s is required" % option)
98 elif isinstance(option, Option) and option.deprecated and \
99 self.option_seen.has_key(option):
100 warnings.warn("Ignoring deprecated option on line %s: %s" % (self.lineno, option), DeprecationWarning)
102 return (values, args)
104 def __init__(self, map={}, lineno=None):
105 self.map = map
106 self.lineno = lineno
107 OptionParser.__init__(self, option_class=DeprecatedOption,
108 add_help_option=False)
110 # Creates a new Option type that supports a "required" option attribute. Any
111 # option with this attribute must be supplied or an exception is thrown.
112 class RequiredOption (Option):
113 ATTRS = Option.ATTRS + ['required']
115 def _check_required (self):
116 if self.required and not self.takes_value():
117 raise OptionError("Required flag set for option that doesn't take a value", self)
119 # Make sure _check_required() is called from the constructor!
120 CHECK_METHODS = Option.CHECK_METHODS + [_check_required]
122 def process (self, opt, value, values, parser):
123 Option.process(self, opt, value, values, parser)
124 parser.option_seen[self] = 1
126 # Additional OptionParser actions. "map" allows you to define a opt -> val
127 # mapping such that dest gets val when opt is seen. "map_extend" allows you
128 # to define an opt -> [val1, ... valn] mapping such that dest gets a list of
129 # vals build up when opt is seen.
130 class MappableOption(RequiredOption):
131 ACTIONS = RequiredOption.ACTIONS + ("map", "map_extend",)
132 STORE_ACTIONS = RequiredOption.STORE_ACTIONS + ("map", "map_extend",)
134 def take_action(self, action, dest, opt, value, values, parser):
135 if action == "map":
136 values.ensure_value(dest, parser.map[opt.lstrip('-')])
137 elif action == "map_extend":
138 values.ensure_value(dest, []).extend(parser.map[opt.lstrip('-')])
139 else:
140 RequiredOption.take_action(self, action, dest, opt, value, values, parser)
142 # Creates a new Option type that supports a "deprecated" option attribute.
143 # Any option with this attribute will cause a DeprecationWarning to be
144 # thrown if the option is used.
145 class DeprecatedOption(MappableOption):
146 ATTRS = MappableOption.ATTRS + ['deprecated']
148 def process (self, opt, value, values, parser):
149 MappableOption.process(self, opt, value, values, parser)
150 parser.option_seen[self] = 1
153 ### SCRIPT HANDLING
156 # You may make a subclass of Script if you need additional script handling
157 # besides just a data representation. For instance, anaconda may subclass
158 # this to add a run method.
159 class Script:
160 def __repr__(self):
161 str = ("(s: '%s' i: %s c: %d)") % \
162 (self.script, self.interp, self.inChroot)
163 return string.replace(str, "\n", "|")
165 def __init__(self, script, interp = "/bin/sh", inChroot = False,
166 logfile = None, errorOnFail = False, type = KS_SCRIPT_PRE):
167 self.script = string.join(script, "")
168 self.interp = interp
169 self.inChroot = inChroot
170 self.logfile = logfile
171 self.errorOnFail = errorOnFail
172 self.type = type
174 # Produce a string representation of the script suitable for writing
175 # to a kickstart file. Add this to the end of the %whatever header.
176 def write(self):
177 str = ""
178 if self.interp != "/bin/sh" and self.interp != "":
179 str = str + " --interp %s" % self.interp
180 if self.type == KS_SCRIPT_POST and not self.inChroot:
181 str = str + " --nochroot"
182 if self.logfile != None:
183 str = str + " --logfile %s" % self.logfile
184 if self.errorOnFail:
185 str = str + " --erroronfail"
187 str = str + "\n%s\n" % self.script
188 return str
191 ### COMMAND HANDLERS
194 # You may make a subclass of KickstartHandlers if you need to do something
195 # besides just build up the data store. If you need to do additional processing
196 # just make a subclass, define handlers for each command in your subclass, and
197 # make sure to call the same handler in the super class before whatever you
198 # want to do. Also if you need to make a new parser that only takes action
199 # for a subset of commands, make a subclass and define all the handlers to
200 # None except the ones you care about.
201 class KickstartHandlers:
202 def __init__ (self, ksdata):
203 self.ksdata = ksdata
205 self.handlers = { "auth" : self.doAuthconfig,
206 "authconfig" : self.doAuthconfig,
207 "autopart" : self.doAutoPart,
208 "autostep" : self.doAutoStep,
209 "bootloader" : self.doBootloader,
210 "cdrom" : self.doMethod,
211 "clearpart" : self.doClearPart,
212 "cmdline" : self.doDisplayMode,
213 "device" : self.doDevice,
214 "deviceprobe" : self.doDeviceProbe,
215 "driverdisk" : self.doDriverDisk,
216 "firewall" : self.doFirewall,
217 "firstboot" : self.doFirstboot,
218 "graphical" : self.doDisplayMode,
219 "halt" : self.doReboot,
220 "harddrive" : self.doMethod,
221 "ignoredisk" : self.doIgnoreDisk,
222 # implied by lack of "upgrade" command
223 "install" : None,
224 "interactive" : self.doInteractive,
225 "keyboard" : self.doKeyboard,
226 "lang" : self.doLang,
227 "langsupport" : self.doLangSupport,
228 "logvol" : self.doLogicalVolume,
229 "mediacheck" : self.doMediaCheck,
230 "monitor" : self.doMonitor,
231 "mouse" : self.doMouse,
232 "network" : self.doNetwork,
233 "nfs" : self.doMethod,
234 "part" : self.doPartition,
235 "partition" : self.doPartition,
236 "poweroff" : self.doReboot,
237 "raid" : self.doRaid,
238 "reboot" : self.doReboot,
239 "rootpw" : self.doRootPw,
240 "selinux" : self.doSELinux,
241 "shutdown" : self.doReboot,
242 "skipx" : self.doSkipX,
243 "text" : self.doDisplayMode,
244 "timezone" : self.doTimezone,
245 "url" : self.doMethod,
246 "upgrade" : self.doUpgrade,
247 "vnc" : self.doVnc,
248 "volgroup" : self.doVolumeGroup,
249 "xconfig" : self.doXConfig,
250 "zerombr" : self.doZeroMbr,
251 "zfcp" : self.doZFCP,
254 def resetHandlers (self):
255 for key in self.handlers.keys():
256 self.handlers[key] = None
258 def deprecatedCommand(self, cmd):
259 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)
261 def doAuthconfig(self, args):
262 self.ksdata.authconfig = string.join(args)
264 def doAutoPart(self, args):
265 self.ksdata.autopart = True
267 def doAutoStep(self, args):
268 op = KSOptionParser(lineno=self.lineno)
269 op.add_option("--autoscreenshot", dest="autoscreenshot",
270 action="store_true", default=False)
272 (opts, extra) = op.parse_args(args=args)
273 self.ksdata.autostep["autoscreenshot"] = opts.autoscreenshot
275 def doBootloader(self, args):
276 def driveorder_cb (option, opt_str, value, parser):
277 for d in value.split(','):
278 parser.values.ensure_value(option.dest, []).append(d)
280 op = KSOptionParser(lineno=self.lineno)
281 op.add_option("--append", dest="appendLine")
282 op.add_option("--location", dest="location", type="choice",
283 default="mbr",
284 choices=["mbr", "partition", "none", "boot"])
285 op.add_option("--lba32", dest="forceLBA", action="store_true",
286 default=False)
287 op.add_option("--password", dest="password", default="")
288 op.add_option("--md5pass", dest="md5pass", default="")
289 op.add_option("--upgrade", dest="upgrade", action="store_true",
290 default=False)
291 op.add_option("--driveorder", dest="driveorder", action="callback",
292 callback=driveorder_cb, nargs=1, type="string")
294 (opts, extra) = op.parse_args(args=args)
296 for key in filter (lambda k: getattr(opts, k) != None, op.keys()):
297 self.ksdata.bootloader[key] = getattr(opts, key)
299 def doClearPart(self, args):
300 def drive_cb (option, opt_str, value, parser):
301 for d in value.split(','):
302 parser.values.ensure_value(option.dest, []).append(d)
304 op = KSOptionParser(lineno=self.lineno)
305 op.add_option("--all", dest="type", action="store_const",
306 const=CLEARPART_TYPE_ALL)
307 op.add_option("--drives", dest="drives", action="callback",
308 callback=drive_cb, nargs=1, type="string")
309 op.add_option("--initlabel", dest="initAll", action="store_true",
310 default=False)
311 op.add_option("--linux", dest="type", action="store_const",
312 const=CLEARPART_TYPE_LINUX)
313 op.add_option("--none", dest="type", action="store_const",
314 const=CLEARPART_TYPE_NONE)
316 (opts, extra) = op.parse_args(args=args)
318 for key in filter (lambda k: getattr(opts, k) != None, op.keys()):
319 self.ksdata.clearpart[key] = getattr(opts, key)
321 def doDevice(self, args):
322 self.ksdata.device = string.join(args)
324 def doDeviceProbe(self, args):
325 self.ksdata.deviceprobe = string.join(args)
327 def doDisplayMode(self, args):
328 if self.currentCmd == "cmdline":
329 self.ksdata.displayMode = DISPLAY_MODE_CMDLINE
330 elif self.currentCmd == "graphical":
331 self.ksdata.displayMode = DISPLAY_MODE_GRAPHICAL
332 elif self.currentCmd == "text":
333 self.ksdata.displayMode = DISPLAY_MODE_TEXT
335 def doDriverDisk(self, args):
336 self.ksdata.driverdisk = string.join(args)
338 def doFirewall(self, args):
339 def firewall_port_cb (option, opt_str, value, parser):
340 for p in value.split(","):
341 p = p.strip()
342 if p.find(":") == -1:
343 p = "%s:tcp" % p
344 parser.values.ensure_value(option.dest, []).append(p)
346 op = KSOptionParser(map={"ssh":["22:tcp"], "telnet":["23:tcp"],
347 "smtp":["25:tcp"], "http":["80:tcp", "443:tcp"],
348 "ftp":["21:tcp"]}, lineno=self.lineno)
350 op.add_option("--disable", "--disabled", dest="enabled",
351 action="store_false")
352 op.add_option("--enable", "--enabled", dest="enabled",
353 action="store_true", default=True)
354 op.add_option("--ftp", "--http", "--smtp", "--ssh", "--telnet",
355 dest="ports", action="map_extend")
356 op.add_option("--port", dest="ports", action="callback",
357 callback=firewall_port_cb, nargs=1, type="string")
358 op.add_option("--trust", dest="trusts", action="append")
360 (opts, extra) = op.parse_args(args=args)
362 for key in filter (lambda k: getattr(opts, k) != None, op.keys()):
363 self.ksdata.firewall[key] = getattr(opts, key)
365 def doFirstboot(self, args):
366 op = KSOptionParser(lineno=self.lineno)
367 op.add_option("--disable", "--disabled", dest="firstboot",
368 action="store_const", const=FIRSTBOOT_SKIP)
369 op.add_option("--enable", "--enabled", dest="firstboot",
370 action="store_const", const=FIRSTBOOT_DEFAULT)
371 op.add_option("--reconfig", dest="firstboot", action="store_const",
372 const=FIRSTBOOT_RECONFIG)
374 (opts, extra) = op.parse_args(args=args)
375 self.ksdata.firstboot = opts.firstboot
377 def doIgnoreDisk(self, args):
378 def drive_cb (option, opt_str, value, parser):
379 for d in value.split(','):
380 parser.values.ensure_value(option.dest, []).append(d)
382 op = KSOptionParser(lineno=self.lineno)
383 op.add_option("--drives", dest="drives", action=callback,
384 callback=drive_cb, nargs=1, type="string")
386 (opts, extra) = op.parse_args(args=args)
388 self.ksdata.ignoredisk = opt.ignoredisk
390 def doInteractive(self, args):
391 self.ksdata.interactive = True
393 def doKeyboard(self, args):
394 self.ksdata.keyboard = args[0]
396 def doLang(self, args):
397 self.ksdata.lang = args[0]
399 def doLangSupport(self, args):
400 self.deprecatedCommand("langsupport")
402 def doLogicalVolume(self, args):
403 def lv_cb (option, opt_str, value, parser):
404 parser.values.ensure_value(option.dest, False)
405 parser.values.ensure_value("preexist", True)
407 op = KSOptionParser(lineno=self.lineno)
408 op.add_option("--bytes-per-inode", dest="bytesPerInode", action="store",
409 type="int", nargs=1)
410 op.add_option("--fsoptions", dest="fsopts")
411 op.add_option("--fstype", dest="fstype")
412 op.add_option("--grow", dest="grow", action="store_true",
413 default=False)
414 op.add_option("--maxsize", dest="maxSizeMB", action="store", type="int",
415 nargs=1)
416 op.add_option("--name", dest="name", required=1)
417 op.add_option("--noformat", action="callback", callback=lv_cb,
418 dest="format", default=True, nargs=0)
419 op.add_option("--percent", dest="percent", action="store", type="int",
420 nargs=1)
421 op.add_option("--recommended", dest="recommended", action="store_true",
422 default=False)
423 op.add_option("--size", dest="size", action="store", type="int",
424 nargs=1)
425 op.add_option("--useexisting", dest="preexist", action="store_true",
426 default=False)
427 op.add_option("--vgname", dest="vgname", required=1)
429 (opts, extra) = op.parse_args(args=args)
431 if len(extra) == 0:
432 raise KickstartValueError, formatErrorMsg(self.lineno, msg="Mount point required for logvol")
434 lvd = KickstartLogVolData()
435 for key in filter (lambda k: getattr(opts, k) != None, op.keys()):
436 setattr(lvd, key, getattr(opts, key))
438 lvd.mountpoint = extra[0]
439 self.ksdata.lvList.append(lvd)
441 def doMediaCheck(self, args):
442 self.ksdata.mediacheck = True
444 def doMethod(self, args):
445 op = KSOptionParser(lineno=self.lineno)
447 self.ksdata.method["method"] = self.currentCmd
449 if self.currentCmd == "cdrom":
450 pass
451 elif self.currentCmd == "harddrive":
452 op.add_option("--partition", dest="partition", required=1)
453 op.add_option("--dir", dest="dir", required=1)
455 (opts, extra) = op.parse_args(args=args)
456 self.ksdata.method["partition"] = opts.partition
457 self.ksdata.method["dir"] = opts.dir
458 elif self.currentCmd == "nfs":
459 op.add_option("--server", dest="server", required=1)
460 op.add_option("--dir", dest="dir", required=1)
462 (opts, extra) = op.parse_args(args=args)
463 self.ksdata.method["server"] = opts.server
464 self.ksdata.method["dir"] = opts.dir
465 elif self.currentCmd == "url":
466 op.add_option("--url", dest="url", required=1)
468 (opts, extra) = op.parse_args(args=args)
469 self.ksdata.method["url"] = opts.url
471 def doMonitor(self, args):
472 op = KSOptionParser(lineno=self.lineno)
473 op.add_option("--hsync", dest="hsync")
474 op.add_option("--monitor", dest="monitor")
475 op.add_option("--vsync", dest="vsync")
477 (opts, extra) = op.parse_args(args=args)
479 if extra:
480 raise KickstartValueError, formatErrorMsg(self.lineno, msg="Unexpected arguments to monitor command: %s" % extra)
482 for key in filter (lambda k: getattr(opts, k) != None, op.keys()):
483 self.ksdata.monitor[key] = getattr(opts, key)
485 def doMouse(self, args):
486 self.deprecatedCommand("mouse")
488 def doNetwork(self, args):
489 op = KSOptionParser(lineno=self.lineno)
490 op.add_option("--bootproto", dest="bootProto", default="dhcp")
491 op.add_option("--class", dest="dhcpclass")
492 op.add_option("--device", dest="device")
493 op.add_option("--essid", dest="essid")
494 op.add_option("--ethtool", dest="ethtool")
495 op.add_option("--gateway", dest="gateway")
496 op.add_option("--hostname", dest="hostname")
497 op.add_option("--ip", dest="ip")
498 op.add_option("--nameserver", dest="nameserver")
499 op.add_option("--netmask", dest="netmask")
500 op.add_option("--nodns", dest="nodns", action="store_true",
501 default=False)
502 op.add_option("--notksdevice", dest="notksdevice", action="store_true",
503 default=False)
504 op.add_option("--onboot", dest="onboot", action="store_true",
505 default=False)
506 op.add_option("--wepkey", dest="wepkey")
508 (opts, extra) = op.parse_args(args=args)
510 nd = KickstartNetworkData()
511 for key in filter (lambda k: getattr(opts, k) != None, op.keys()):
512 setattr(nd, key, getattr(opts, key))
514 self.ksdata.network.append(nd)
516 def doPartition(self, args):
517 def part_cb (option, opt_str, value, parser):
518 if value.startswith("/dev/"):
519 parser.values.ensure_value(option.dest, value[5:])
520 else:
521 parser.values.ensure_value(option.dest, value)
523 op = KSOptionParser(lineno=self.lineno)
524 op.add_option("--active", dest="active", action="store_true",
525 default=False)
526 op.add_option("--asprimary", dest="primOnly", action="store_true",
527 default=False)
528 op.add_option("--bytes-per-inode", dest="bytesPerInode", action="store",
529 type="int", nargs=1)
530 op.add_option("--end", dest="end", action="store", type="int",
531 nargs=1)
532 op.add_option("--fsoptions", dest="fsopts")
533 op.add_option("--fstype", "--type", dest="fstype")
534 op.add_option("--grow", dest="grow", action="store_true", default=False)
535 op.add_option("--label", dest="label")
536 op.add_option("--maxsize", dest="maxSizeMB", action="store", type="int",
537 nargs=1)
538 op.add_option("--noformat", dest="format", action="store_false",
539 default=True)
540 op.add_option("--onbiosdisk", dest="onbiosdisk")
541 op.add_option("--ondisk", "--ondrive", dest="disk")
542 op.add_option("--onpart", "--usepart", dest="onPart", action="callback",
543 callback=part_cb, nargs=1, type="string")
544 op.add_option("--recommended", dest="recommended", action="store_true",
545 default=False)
546 op.add_option("--size", dest="size", action="store", type="int",
547 nargs=1)
548 op.add_option("--start", dest="start", action="store", type="int",
549 nargs=1)
551 (opts, extra) = op.parse_args(args=args)
553 if len(extra) != 1:
554 raise KickstartValueError, formatErrorMsg(self.lineno, msg="Mount point required for partition")
556 pd = KickstartPartData()
557 for key in filter (lambda k: getattr(opts, k) != None, op.keys()):
558 setattr(pd, key, getattr(opts, key))
560 pd.mountpoint = extra[0]
561 self.ksdata.partitions.append(pd)
563 def doReboot(self, args):
564 self.ksdata.reboot = True
566 def doRaid(self, args):
567 def raid_cb (option, opt_str, value, parser):
568 parser.values.ensure_value(option.dest, False)
569 parser.values.ensure_value("preexist", True)
571 def device_cb (option, opt_str, value, parser):
572 if value[0:2] == "md":
573 parser.values.ensure_value(option.dest, value[2:])
574 else:
575 parser.values.ensure_value(option.dest, value)
577 def level_cb (option, opt_str, value, parser):
578 if value == "RAID0" or value == "0":
579 parser.values.ensure_value(option.dest, "RAID0")
580 elif value == "RAID1" or value == "1":
581 parser.values.ensure_value(option.dest, "RAID1")
582 elif value == "RAID5" or value == "5":
583 parser.values.ensure_value(option.dest, "RAID5")
584 elif value == "RAID6" or value == "6":
585 parser.values.ensure_value(option.dest, "RAID6")
587 op = KSOptionParser(lineno=self.lineno)
588 op.add_option("--device", action="callback", callback=device_cb,
589 dest="device", type="string", nargs=1, required=1)
590 op.add_option("--fsoptions", dest="fsopts")
591 op.add_option("--fstype", dest="fstype")
592 op.add_option("--level", dest="level", action="callback",
593 callback=level_cb, type="string", nargs=1, required=1)
594 op.add_option("--noformat", action="callback", callback=raid_cb,
595 dest="format", default=True, nargs=0)
596 op.add_option("--spares", dest="spares", action="store", type="int",
597 nargs=1, default=0)
598 op.add_option("--useexisting", dest="preexist", action="store_true",
599 default=False)
601 (opts, extra) = op.parse_args(args=args)
603 if len(extra) == 0:
604 raise KickstartValueError, formatErrorMsg(self.lineno, msg="Mount point required for raid")
606 rd = KickstartRaidData()
607 for key in filter (lambda k: getattr(opts, k) != None, op.keys()):
608 setattr(rd, key, getattr(opts, key))
610 rd.mountpoint = extra[0]
611 rd.members = extra[1:]
612 self.ksdata.raidList.append(rd)
614 def doRootPw(self, args):
615 op = KSOptionParser(lineno=self.lineno)
616 op.add_option("--iscrypted", dest="isCrypted", action="store_true",
617 default=False)
619 (opts, extra) = op.parse_args(args=args)
620 self.ksdata.rootpw["isCrypted"] = opts.isCrypted
622 if len(extra) != 1:
623 raise KickstartValueError, formatErrorMsg(self.lineno, msg="A single argument is expected for rootpw")
625 self.ksdata.rootpw["password"] = extra[0]
627 def doSELinux(self, args):
628 op = KSOptionParser(lineno=self.lineno)
629 op.add_option("--disabled", dest="sel", action="store_const",
630 const=SELINUX_DISABLED)
631 op.add_option("--enforcing", dest="sel", action="store_const",
632 const=SELINUX_ENFORCING)
633 op.add_option("--permissive", dest="sel", action="store_const",
634 const=SELINUX_PERMISSIVE)
636 (opts, extra) = op.parse_args(args=args)
637 self.ksdata.selinux = opts.sel
639 def doSkipX(self, args):
640 self.ksdata.skipx = True
642 def doTimezone(self, args):
643 op = KSOptionParser(lineno=self.lineno)
644 op.add_option("--utc", dest="isUtc", action="store_true", default=False)
646 (opts, extra) = op.parse_args(args=args)
647 self.ksdata.timezone["isUtc"] = opts.isUtc
649 if len(extra) != 1:
650 raise KickstartValueError, formatErrorMsg(self.lineno, msg="A single argument is expected for timezone")
652 self.ksdata.timezone["timezone"] = extra[0]
654 def doUpgrade(self, args):
655 self.ksdata.upgrade = True
657 def doVnc(self, args):
658 def connect_cb (option, opt_str, value, parser):
659 cargs = opt_str.split(":")
660 parser.values.ensure_value("host", cargs[0])
662 if len(cargs) > 1:
663 parser.values.ensure_value("port", cargs[1])
665 op = KSOptionParser(lineno=self.lineno)
666 op.add_option("--connect", action="callback", callback=connect_cb,
667 nargs=1, type="string")
668 op.add_option("--password", dest="password")
670 (opts, extra) = op.parse_args(args=args)
672 self.ksdata.vnc["enabled"] = True
674 for key in filter (lambda k: getattr(opts, k) != None, op.keys()):
675 self.ksdata.vnc[key] = getattr(opts, key)
677 def doVolumeGroup(self, args):
678 # Have to be a little more complicated to set two values.
679 def vg_cb (option, opt_str, value, parser):
680 parser.values.ensure_value(option.dest, False)
681 parser.values.ensure_value("preexist", True)
683 op = KSOptionParser(lineno=self.lineno)
684 op.add_option("--noformat", action="callback", callback=vg_cb,
685 dest="format", default=True, nargs=0)
686 op.add_option("--pesize", dest="pesize", type="int", nargs=1,
687 default=32768)
688 op.add_option("--useexisting", dest="preexist", action="store_true",
689 default=False)
691 (opts, extra) = op.parse_args(args=args)
693 vgd = KickstartVolGroupData()
694 for key in filter (lambda k: getattr(opts, k) != None, op.keys()):
695 setattr(vgd, key, getattr(opts, key))
697 vgd.vgname = extra[0]
698 vgd.physvols = extra[1:]
699 self.ksdata.vgList.append(vgd)
701 def doXConfig(self, args):
702 op = KSOptionParser(lineno=self.lineno)
703 op.add_option("--card", deprecated=1)
704 op.add_option("--driver", dest="driver")
705 op.add_option("--defaultdesktop", dest="defaultdesktop")
706 op.add_option("--depth", dest="depth", action="store", type="int",
707 nargs=1)
708 op.add_option("--hsync", dest="hsync")
709 op.add_option("--monitor", dest="monitor")
710 op.add_option("--noprobe", dest="probe", action="store_false",
711 default=True)
712 op.add_option("--resolution", dest="resolution")
713 op.add_option("--startxonboot", dest="startX", action="store_true",
714 default=False)
715 op.add_option("--videoram", dest="videoRam")
716 op.add_option("--vsync", dest="vsync")
718 (opts, extra) = op.parse_args(args=args)
719 if extra:
720 raise KickstartValueError, formatErrorMsg(self.lineno, msg="Unexpected arguments to xconfig command: %s" % extra)
722 for key in filter (lambda k: getattr(opts, k) != None, op.keys()):
723 self.ksdata.xconfig[key] = getattr(opts, key)
725 def doZeroMbr(self, args):
726 self.ksdata.zerombr = True
728 def doZFCP(self, args):
729 op = KSOptionParser(lineno=self.lineno)
730 op.add_option("--devnum", dest="devnum", required=1)
731 op.add_option("--fcplun", dest="fcplun", required=1)
732 op.add_option("--scsiid", dest="scsiid", required=1)
733 op.add_option("--scsilun", dest="scsilun", required=1)
734 op.add_option("--wwpn", dest="wwpn", required=1)
736 (opts, extra) = op.parse_args(args=args)
738 for key in filter (lambda k: getattr(opts, k) != None, op.keys()):
739 self.ksdata.zfcp[key] = getattr(opts, key)
742 ### PARSER
745 # The kickstart file parser. This only transitions between states and calls
746 # handlers at certain points. To create a specialized parser, make a subclass
747 # of this and override the methods you care about. Methods that don't need to
748 # do anything may just pass.
750 # Passing None for kshandlers is valid just in case you don't care about
751 # handling any commands.
752 class KickstartParser:
753 def __init__ (self, ksdata, kshandlers):
754 self.handler = kshandlers
755 self.ksdata = ksdata
756 self.followIncludes = True
757 self.state = STATE_COMMANDS
758 self.script = None
759 self.includeDepth = 0
761 # Functions to be called when we are at certain points in the
762 # kickstart file parsing. Override these if you need special
763 # behavior.
764 def addScript (self):
765 if string.join(self.script["body"]).strip() == "":
766 return
768 s = Script (self.script["body"], self.script["interp"],
769 self.script["chroot"], self.script["log"],
770 self.script["errorOnFail"], self.script["type"])
772 self.ksdata.scripts.append(s)
774 def addPackages (self, line):
775 if line[0] == '@':
776 line = line[1:]
777 self.ksdata.groupList.append(line.lstrip())
778 elif line[0] == '-':
779 line = line[1:]
780 self.ksdata.excludedList.append(line.lstrip())
781 else:
782 self.ksdata.packageList.append(line.lstrip())
784 def handleCommand (self, lineno, args):
785 if not self.handler:
786 return
788 cmd = args[0]
789 cmdArgs = args[1:]
791 if not self.handler.handlers.has_key(cmd):
792 raise KickstartParseError, formatErrorMsg(lineno)
793 else:
794 if self.handler.handlers[cmd] != None:
795 setattr(self.handler, "currentCmd", cmd)
796 setattr(self.handler, "lineno", lineno)
797 self.handler.handlers[cmd](cmdArgs)
799 def handlePackageHdr (self, lineno, args):
800 op = KSOptionParser(lineno=lineno)
801 op.add_option("--excludedocs", dest="excludedocs", action="store_true",
802 default=False)
803 op.add_option("--ignoremissing", dest="ignoremissing",
804 action="store_true", default=False)
805 op.add_option("--nobase", dest="nobase", action="store_true",
806 default=False)
807 op.add_option("--ignoredeps", dest="resolveDeps", action="store_false",
808 deprecated=1)
809 op.add_option("--resolvedeps", dest="resolveDeps", action="store_true",
810 deprecated=1)
812 (opts, extra) = op.parse_args(args=args[1:])
814 self.ksdata.excludeDocs = opts.excludedocs
815 self.ksdata.addBase = not opts.nobase
816 if opts.ignoremissing:
817 self.ksdata.handleMissing = KS_MISSING_IGNORE
818 else:
819 self.ksdata.handleMissing = KS_MISSING_PROMPT
821 def handleScriptHdr (self, lineno, args):
822 op = KSOptionParser(lineno=lineno)
823 op.add_option("--erroronfail", dest="errorOnFail", action="store_true",
824 default=False)
825 op.add_option("--interpreter", dest="interpreter", default="/bin/sh")
826 op.add_option("--log", "--logfile", dest="log")
828 if args[0] == "%pre" or args[0] == "%traceback":
829 self.script["chroot"] = False
830 elif args[0] == "%post":
831 self.script["chroot"] = True
832 op.add_option("--nochroot", dest="nochroot", action="store_true",
833 default=False)
835 (opts, extra) = op.parse_args(args=args[1:])
837 self.script["interp"] = opts.interpreter
838 self.script["log"] = opts.log
839 self.script["errorOnFail"] = opts.errorOnFail
840 if hasattr(opts, "nochroot"):
841 self.script["chroot"] = not opts.nochroot
843 def readKickstart (self, file):
844 packages = []
845 groups = []
846 excludedPackages = []
848 # For error reporting.
849 lineno = 0
851 fh = open(file)
852 needLine = True
854 while True:
855 if needLine:
856 line = fh.readline()
857 lineno += 1
858 needLine = False
860 if line == "" and self.includeDepth > 0:
861 fh.close()
862 break
864 # Don't eliminate whitespace or comments from scripts.
865 if line.isspace() or (line != "" and line[0] == '#'):
866 # Save the platform for s-c-kickstart, though.
867 if line[:10] == "#platform=" and self.state == STATE_COMMANDS:
868 self.ksdata.platform = line[11:]
870 if self.state in [STATE_PRE, STATE_POST, STATE_TRACEBACK]:
871 self.script["body"].append(line)
873 needLine = True
874 continue
876 args = shlex.split(line)
878 if args and args[0] == "%include" and self.followIncludes:
879 if not args[1]:
880 raise KickstartParseError, formatErrorMsg(lineno)
881 else:
882 self.includeDepth += 1
883 self.readKickstart (args[1])
884 self.includeDepth -= 1
885 needLine = True
886 continue
888 if self.state == STATE_COMMANDS:
889 if not args and self.includeDepth == 0:
890 self.state = STATE_END
891 elif args[0] in ["%pre", "%post", "%traceback"]:
892 self.state = STATE_SCRIPT_HDR
893 elif args[0] == "%packages":
894 self.state = STATE_PACKAGES
895 elif args[0][0] == '%':
896 raise KickstartParseError, formatErrorMsg(lineno)
897 else:
898 needLine = True
899 self.handleCommand(lineno, args)
901 elif self.state == STATE_PACKAGES:
902 if not args and self.includeDepth == 0:
903 self.state = STATE_END
904 elif args[0] in ["%pre", "%post", "%traceback"]:
905 self.state = STATE_SCRIPT_HDR
906 elif args[0] == "%packages":
907 needLine = True
908 self.handlePackageHdr (lineno, args)
909 elif args[0][0] == '%':
910 raise KickstartParseError, formatErrorMsg(lineno)
911 else:
912 needLine = True
913 self.addPackages (string.rstrip(line))
915 elif self.state == STATE_SCRIPT_HDR:
916 needLine = True
917 self.script = {"body": [], "interp": "/bin/sh", "log": None,
918 "errorOnFail": False}
920 if not args and self.includeDepth == 0:
921 self.state = STATE_END
922 elif args[0] == "%pre":
923 self.state = STATE_PRE
924 self.script["type"] = KS_SCRIPT_PRE
925 elif args[0] == "%post":
926 self.state = STATE_POST
927 self.script["type"] = KS_SCRIPT_POST
928 elif args[0] == "%traceback":
929 self.state = STATE_TRACEBACK
930 self.script["type"] = KS_SCRIPT_TRACEBACK
931 elif args[0][0] == '%':
932 raise KickstartParseError, formatErrorMsg(lineno)
934 self.handleScriptHdr (lineno, args)
936 elif self.state in [STATE_PRE, STATE_POST, STATE_TRACEBACK]:
937 # If this is part of a script, append to it.
938 if not args and self.includeDepth == 0:
939 self.addScript()
940 self.state = STATE_END
941 elif args[0] in ["%pre", "%post", "%traceback", "%packages"]:
942 # Otherwise, figure out what kind of a script we just
943 # finished reading, add it to the list, and switch to
944 # the initial state.
945 self.addScript()
946 self.state = STATE_COMMANDS
947 else:
948 self.script["body"].append(line)
949 needLine = True
951 elif self.state == STATE_END:
952 break