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