Add --resolvedeps and --includedeps as deprecated options to %packages.
[pykickstart.git] / pykickstart / parser.py
blobe2493b1dcffa2d7b095e6b7c62d1039aa54cf549
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 def onboot_cb (option, opt_str, value, parser):
490 if value == "no":
491 parser.values.ensure_value(option.dest, False)
492 else:
493 parser.values.ensure_value(option.dest, True)
495 op = KSOptionParser(lineno=self.lineno)
496 op.add_option("--bootproto", dest="bootProto", default="dhcp")
497 op.add_option("--class", dest="dhcpclass")
498 op.add_option("--device", dest="device")
499 op.add_option("--essid", dest="essid")
500 op.add_option("--ethtool", dest="ethtool")
501 op.add_option("--gateway", dest="gateway")
502 op.add_option("--hostname", dest="hostname")
503 op.add_option("--ip", dest="ip")
504 op.add_option("--nameserver", dest="nameserver")
505 op.add_option("--netmask", dest="netmask")
506 op.add_option("--nodns", dest="nodns", action="store_true",
507 default=False)
508 op.add_option("--notksdevice", dest="notksdevice", action="store_true",
509 default=False)
510 op.add_option("--onboot", dest="onboot", action="callback",
511 callback=onboot_cb, nargs=1, type="string")
512 op.add_option("--wepkey", dest="wepkey")
514 (opts, extra) = op.parse_args(args=args)
516 nd = KickstartNetworkData()
517 for key in filter (lambda k: getattr(opts, k) != None, op.keys()):
518 setattr(nd, key, getattr(opts, key))
520 self.ksdata.network.append(nd)
522 def doPartition(self, args):
523 def part_cb (option, opt_str, value, parser):
524 if value.startswith("/dev/"):
525 parser.values.ensure_value(option.dest, value[5:])
526 else:
527 parser.values.ensure_value(option.dest, value)
529 op = KSOptionParser(lineno=self.lineno)
530 op.add_option("--active", dest="active", action="store_true",
531 default=False)
532 op.add_option("--asprimary", dest="primOnly", action="store_true",
533 default=False)
534 op.add_option("--bytes-per-inode", dest="bytesPerInode", action="store",
535 type="int", nargs=1)
536 op.add_option("--end", dest="end", action="store", type="int",
537 nargs=1)
538 op.add_option("--fsoptions", dest="fsopts")
539 op.add_option("--fstype", "--type", dest="fstype")
540 op.add_option("--grow", dest="grow", action="store_true", default=False)
541 op.add_option("--label", dest="label")
542 op.add_option("--maxsize", dest="maxSizeMB", action="store", type="int",
543 nargs=1)
544 op.add_option("--noformat", dest="format", action="store_false",
545 default=True)
546 op.add_option("--onbiosdisk", dest="onbiosdisk")
547 op.add_option("--ondisk", "--ondrive", dest="disk")
548 op.add_option("--onpart", "--usepart", dest="onPart", action="callback",
549 callback=part_cb, nargs=1, type="string")
550 op.add_option("--recommended", dest="recommended", action="store_true",
551 default=False)
552 op.add_option("--size", dest="size", action="store", type="int",
553 nargs=1)
554 op.add_option("--start", dest="start", action="store", type="int",
555 nargs=1)
557 (opts, extra) = op.parse_args(args=args)
559 if len(extra) != 1:
560 raise KickstartValueError, formatErrorMsg(self.lineno, msg="Mount point required for partition")
562 pd = KickstartPartData()
563 for key in filter (lambda k: getattr(opts, k) != None, op.keys()):
564 setattr(pd, key, getattr(opts, key))
566 pd.mountpoint = extra[0]
567 self.ksdata.partitions.append(pd)
569 def doReboot(self, args):
570 self.ksdata.reboot = True
572 def doRaid(self, args):
573 def raid_cb (option, opt_str, value, parser):
574 parser.values.ensure_value(option.dest, False)
575 parser.values.ensure_value("preexist", True)
577 def device_cb (option, opt_str, value, parser):
578 if value[0:2] == "md":
579 parser.values.ensure_value(option.dest, value[2:])
580 else:
581 parser.values.ensure_value(option.dest, value)
583 def level_cb (option, opt_str, value, parser):
584 if value == "RAID0" or value == "0":
585 parser.values.ensure_value(option.dest, "RAID0")
586 elif value == "RAID1" or value == "1":
587 parser.values.ensure_value(option.dest, "RAID1")
588 elif value == "RAID5" or value == "5":
589 parser.values.ensure_value(option.dest, "RAID5")
590 elif value == "RAID6" or value == "6":
591 parser.values.ensure_value(option.dest, "RAID6")
593 op = KSOptionParser(lineno=self.lineno)
594 op.add_option("--device", action="callback", callback=device_cb,
595 dest="device", type="string", nargs=1, required=1)
596 op.add_option("--fsoptions", dest="fsopts")
597 op.add_option("--fstype", dest="fstype")
598 op.add_option("--level", dest="level", action="callback",
599 callback=level_cb, type="string", nargs=1, required=1)
600 op.add_option("--noformat", action="callback", callback=raid_cb,
601 dest="format", default=True, nargs=0)
602 op.add_option("--spares", dest="spares", action="store", type="int",
603 nargs=1, default=0)
604 op.add_option("--useexisting", dest="preexist", action="store_true",
605 default=False)
607 (opts, extra) = op.parse_args(args=args)
609 if len(extra) == 0:
610 raise KickstartValueError, formatErrorMsg(self.lineno, msg="Mount point required for raid")
612 rd = KickstartRaidData()
613 for key in filter (lambda k: getattr(opts, k) != None, op.keys()):
614 setattr(rd, key, getattr(opts, key))
616 rd.mountpoint = extra[0]
617 rd.members = extra[1:]
618 self.ksdata.raidList.append(rd)
620 def doRootPw(self, args):
621 op = KSOptionParser(lineno=self.lineno)
622 op.add_option("--iscrypted", dest="isCrypted", action="store_true",
623 default=False)
625 (opts, extra) = op.parse_args(args=args)
626 self.ksdata.rootpw["isCrypted"] = opts.isCrypted
628 if len(extra) != 1:
629 raise KickstartValueError, formatErrorMsg(self.lineno, msg="A single argument is expected for rootpw")
631 self.ksdata.rootpw["password"] = extra[0]
633 def doSELinux(self, args):
634 op = KSOptionParser(lineno=self.lineno)
635 op.add_option("--disabled", dest="sel", action="store_const",
636 const=SELINUX_DISABLED)
637 op.add_option("--enforcing", dest="sel", action="store_const",
638 const=SELINUX_ENFORCING)
639 op.add_option("--permissive", dest="sel", action="store_const",
640 const=SELINUX_PERMISSIVE)
642 (opts, extra) = op.parse_args(args=args)
643 self.ksdata.selinux = opts.sel
645 def doSkipX(self, args):
646 self.ksdata.skipx = True
648 def doTimezone(self, args):
649 op = KSOptionParser(lineno=self.lineno)
650 op.add_option("--utc", dest="isUtc", action="store_true", default=False)
652 (opts, extra) = op.parse_args(args=args)
653 self.ksdata.timezone["isUtc"] = opts.isUtc
655 if len(extra) != 1:
656 raise KickstartValueError, formatErrorMsg(self.lineno, msg="A single argument is expected for timezone")
658 self.ksdata.timezone["timezone"] = extra[0]
660 def doUpgrade(self, args):
661 self.ksdata.upgrade = True
663 def doVnc(self, args):
664 def connect_cb (option, opt_str, value, parser):
665 cargs = opt_str.split(":")
666 parser.values.ensure_value("host", cargs[0])
668 if len(cargs) > 1:
669 parser.values.ensure_value("port", cargs[1])
671 op = KSOptionParser(lineno=self.lineno)
672 op.add_option("--connect", action="callback", callback=connect_cb,
673 nargs=1, type="string", required=1)
674 op.add_option("--password", dest="password")
676 (opts, extra) = op.parse_args(args=args)
678 self.ksdata.vnc["enabled"] = True
680 for key in filter (lambda k: getattr(opts, k) != None, op.keys()):
681 self.ksdata.vnc[key] = getattr(opts, key)
683 def doVolumeGroup(self, args):
684 # Have to be a little more complicated to set two values.
685 def vg_cb (option, opt_str, value, parser):
686 parser.values.ensure_value(option.dest, False)
687 parser.values.ensure_value("preexist", True)
689 op = KSOptionParser(lineno=self.lineno)
690 op.add_option("--noformat", action="callback", callback=vg_cb,
691 dest="format", default=True, nargs=0)
692 op.add_option("--pesize", dest="pesize", type="int", nargs=1,
693 default=32768)
694 op.add_option("--useexisting", dest="preexist", action="store_true",
695 default=False)
697 (opts, extra) = op.parse_args(args=args)
699 vgd = KickstartVolGroupData()
700 for key in filter (lambda k: getattr(opts, k) != None, op.keys()):
701 setattr(vgd, key, getattr(opts, key))
703 vgd.vgname = extra[0]
704 vgd.physvols = extra[1:]
705 self.ksdata.vgList.append(vgd)
707 def doXConfig(self, args):
708 op = KSOptionParser(lineno=self.lineno)
709 op.add_option("--card", deprecated=1)
710 op.add_option("--driver", dest="driver")
711 op.add_option("--defaultdesktop", dest="defaultdesktop")
712 op.add_option("--depth", dest="depth", action="store", type="int",
713 nargs=1)
714 op.add_option("--hsync", dest="hsync")
715 op.add_option("--monitor", dest="monitor")
716 op.add_option("--noprobe", dest="probe", action="store_false",
717 default=True)
718 op.add_option("--resolution", dest="resolution")
719 op.add_option("--startxonboot", dest="startX", action="store_true",
720 default=False)
721 op.add_option("--videoram", dest="videoRam")
722 op.add_option("--vsync", dest="vsync")
724 (opts, extra) = op.parse_args(args=args)
725 if extra:
726 raise KickstartValueError, formatErrorMsg(self.lineno, msg="Unexpected arguments to xconfig command: %s" % extra)
728 for key in filter (lambda k: getattr(opts, k) != None, op.keys()):
729 self.ksdata.xconfig[key] = getattr(opts, key)
731 def doZeroMbr(self, args):
732 self.ksdata.zerombr = True
734 def doZFCP(self, args):
735 op = KSOptionParser(lineno=self.lineno)
736 op.add_option("--devnum", dest="devnum", required=1)
737 op.add_option("--fcplun", dest="fcplun", required=1)
738 op.add_option("--scsiid", dest="scsiid", required=1)
739 op.add_option("--scsilun", dest="scsilun", required=1)
740 op.add_option("--wwpn", dest="wwpn", required=1)
742 (opts, extra) = op.parse_args(args=args)
744 for key in filter (lambda k: getattr(opts, k) != None, op.keys()):
745 self.ksdata.zfcp[key] = getattr(opts, key)
748 ### PARSER
751 # The kickstart file parser. This only transitions between states and calls
752 # handlers at certain points. To create a specialized parser, make a subclass
753 # of this and override the methods you care about. Methods that don't need to
754 # do anything may just pass.
756 # Passing None for kshandlers is valid just in case you don't care about
757 # handling any commands.
758 class KickstartParser:
759 def __init__ (self, ksdata, kshandlers):
760 self.handler = kshandlers
761 self.ksdata = ksdata
762 self.followIncludes = True
763 self.state = STATE_COMMANDS
764 self.script = None
765 self.includeDepth = 0
767 # Functions to be called when we are at certain points in the
768 # kickstart file parsing. Override these if you need special
769 # behavior.
770 def addScript (self):
771 if string.join(self.script["body"]).strip() == "":
772 return
774 s = Script (self.script["body"], self.script["interp"],
775 self.script["chroot"], self.script["log"],
776 self.script["errorOnFail"], self.script["type"])
778 self.ksdata.scripts.append(s)
780 def addPackages (self, line):
781 if line[0] == '@':
782 line = line[1:]
783 self.ksdata.groupList.append(line.lstrip())
784 elif line[0] == '-':
785 line = line[1:]
786 self.ksdata.excludedList.append(line.lstrip())
787 else:
788 self.ksdata.packageList.append(line.lstrip())
790 def handleCommand (self, lineno, args):
791 if not self.handler:
792 return
794 cmd = args[0]
795 cmdArgs = args[1:]
797 if not self.handler.handlers.has_key(cmd):
798 raise KickstartParseError, formatErrorMsg(lineno)
799 else:
800 if self.handler.handlers[cmd] != None:
801 setattr(self.handler, "currentCmd", cmd)
802 setattr(self.handler, "lineno", lineno)
803 self.handler.handlers[cmd](cmdArgs)
805 def handlePackageHdr (self, lineno, args):
806 op = KSOptionParser(lineno=lineno)
807 op.add_option("--excludedocs", dest="excludedocs", action="store_true",
808 default=False)
809 op.add_option("--ignoremissing", dest="ignoremissing",
810 action="store_true", default=False)
811 op.add_option("--nobase", dest="nobase", action="store_true",
812 default=False)
813 op.add_option("--ignoredeps", dest="resolveDeps", action="store_false",
814 deprecated=1)
815 op.add_option("--resolvedeps", dest="resolveDeps", action="store_true",
816 deprecated=1)
818 (opts, extra) = op.parse_args(args=args[1:])
820 self.ksdata.excludeDocs = opts.excludedocs
821 self.ksdata.addBase = not opts.nobase
822 if opts.ignoremissing:
823 self.ksdata.handleMissing = KS_MISSING_IGNORE
824 else:
825 self.ksdata.handleMissing = KS_MISSING_PROMPT
827 def handleScriptHdr (self, lineno, args):
828 op = KSOptionParser(lineno=lineno)
829 op.add_option("--erroronfail", dest="errorOnFail", action="store_true",
830 default=False)
831 op.add_option("--interpreter", dest="interpreter", default="/bin/sh")
832 op.add_option("--log", "--logfile", dest="log")
834 if args[0] == "%pre" or args[0] == "%traceback":
835 self.script["chroot"] = False
836 elif args[0] == "%post":
837 self.script["chroot"] = True
838 op.add_option("--nochroot", dest="nochroot", action="store_true",
839 default=False)
841 (opts, extra) = op.parse_args(args=args[1:])
843 self.script["interp"] = opts.interpreter
844 self.script["log"] = opts.log
845 self.script["errorOnFail"] = opts.errorOnFail
846 if hasattr(opts, "nochroot"):
847 self.script["chroot"] = not opts.nochroot
849 def readKickstart (self, file):
850 packages = []
851 groups = []
852 excludedPackages = []
854 # For error reporting.
855 lineno = 0
857 fh = open(file)
858 needLine = True
860 while True:
861 if needLine:
862 line = fh.readline()
863 lineno += 1
864 needLine = False
866 if line == "" and self.includeDepth > 0:
867 fh.close()
868 break
870 # Don't eliminate whitespace or comments from scripts.
871 if line.isspace() or (line != "" and line[0] == '#'):
872 # Save the platform for s-c-kickstart, though.
873 if line[:10] == "#platform=" and self.state == STATE_COMMANDS:
874 self.ksdata.platform = line[11:]
876 if self.state in [STATE_PRE, STATE_POST, STATE_TRACEBACK]:
877 self.script["body"].append(line)
879 needLine = True
880 continue
882 args = shlex.split(line)
884 if args and args[0] == "%include" and self.followIncludes:
885 if not args[1]:
886 raise KickstartParseError, formatErrorMsg(lineno)
887 else:
888 self.includeDepth += 1
889 self.readKickstart (args[1])
890 self.includeDepth -= 1
891 needLine = True
892 continue
894 if self.state == STATE_COMMANDS:
895 if not args and self.includeDepth == 0:
896 self.state = STATE_END
897 elif args[0] in ["%pre", "%post", "%traceback"]:
898 self.state = STATE_SCRIPT_HDR
899 elif args[0] == "%packages":
900 self.state = STATE_PACKAGES
901 elif args[0][0] == '%':
902 raise KickstartParseError, formatErrorMsg(lineno)
903 else:
904 needLine = True
905 self.handleCommand(lineno, args)
907 elif self.state == STATE_PACKAGES:
908 if not args and self.includeDepth == 0:
909 self.state = STATE_END
910 elif args[0] in ["%pre", "%post", "%traceback"]:
911 self.state = STATE_SCRIPT_HDR
912 elif args[0] == "%packages":
913 needLine = True
914 self.handlePackageHdr (lineno, args)
915 elif args[0][0] == '%':
916 raise KickstartParseError, formatErrorMsg(lineno)
917 else:
918 needLine = True
919 self.addPackages (string.rstrip(line))
921 elif self.state == STATE_SCRIPT_HDR:
922 needLine = True
923 self.script = {"body": [], "interp": "/bin/sh", "log": None,
924 "errorOnFail": False}
926 if not args and self.includeDepth == 0:
927 self.state = STATE_END
928 elif args[0] == "%pre":
929 self.state = STATE_PRE
930 self.script["type"] = KS_SCRIPT_PRE
931 elif args[0] == "%post":
932 self.state = STATE_POST
933 self.script["type"] = KS_SCRIPT_POST
934 elif args[0] == "%traceback":
935 self.state = STATE_TRACEBACK
936 self.script["type"] = KS_SCRIPT_TRACEBACK
937 elif args[0][0] == '%':
938 raise KickstartParseError, formatErrorMsg(lineno)
940 self.handleScriptHdr (lineno, args)
942 elif self.state in [STATE_PRE, STATE_POST, STATE_TRACEBACK]:
943 # If this is part of a script, append to it.
944 if not args and self.includeDepth == 0:
945 self.addScript()
946 self.state = STATE_END
947 elif args[0] in ["%pre", "%post", "%traceback", "%packages"]:
948 # Otherwise, figure out what kind of a script we just
949 # finished reading, add it to the list, and switch to
950 # the initial state.
951 self.addScript()
952 self.state = STATE_COMMANDS
953 else:
954 self.script["body"].append(line)
955 needLine = True
957 elif self.state == STATE_END:
958 break