2 # parser.py: Kickstart file parser.
4 # Chris Lumens <clumens@redhat.com>
6 # Copyright 2005, 2006 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 *
23 from constants
import *
26 from rhpl
.translate
import _
27 import rhpl
.translate
as translate
29 translate
.textdomain("pykickstart")
43 def formatErrorMsg(lineno
, msg
=""):
45 return _("The following problem occurred on line %s of the kickstart file:\n\n%s\n") % (lineno
, msg
)
47 return _("There was a problem reading from line %s of the kickstart file") % lineno
49 class KickstartError(Exception):
50 def __init__(self
, val
= ""):
51 Exception.__init
__(self
)
57 class KickstartParseError(KickstartError
):
58 def __init__(self
, msg
):
59 KickstartError
.__init
__(self
, msg
)
64 class KickstartValueError(KickstartError
):
65 def __init__(self
, msg
):
66 KickstartError
.__init
__(self
, msg
)
75 # Specialized OptionParser, mainly to handle the MappableOption and to turn
77 class KSOptionParser(OptionParser
):
78 def exit(self
, status
=0, msg
=None):
82 if self
.lineno
!= None:
83 raise KickstartParseError
, formatErrorMsg(self
.lineno
, msg
=msg
)
85 raise KickstartParseError
, msg
90 for opt
in self
.option_list
:
92 retval
.append(opt
.dest
)
96 def _init_parsing_state (self
):
97 OptionParser
._init
_parsing
_state
(self
)
100 def check_values (self
, values
, args
):
101 for option
in self
.option_list
:
102 if (isinstance(option
, Option
) and option
.required
and \
103 not self
.option_seen
.has_key(option
)):
104 raise KickstartValueError
, formatErrorMsg(self
.lineno
, _("Option %s is required") % option
)
105 elif isinstance(option
, Option
) and option
.deprecated
and \
106 self
.option_seen
.has_key(option
):
107 warnings
.warn(_("Ignoring deprecated option on line %s: The %s option 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 option.") % (self
.lineno
, option
), DeprecationWarning)
109 return (values
, args
)
111 def __init__(self
, map={}, lineno
=None):
114 OptionParser
.__init
__(self
, option_class
=KSOption
,
115 add_help_option
=False)
117 # Creates a new Option class that supports two new attributes:
118 # - required: any option with this attribute must be supplied or an exception
120 # - deprecated: any option with this attribute will cause a DeprecationWarning
121 # to be thrown if the option is used
122 # Also creates a new type:
123 # - ksboolean: support various kinds of boolean values on an option
124 # And two new actions:
125 # - map : allows you to define an opt -> val mapping such that dest gets val
127 # - map_extend: allows you to define an opt -> [val1, ... valn] mapping such
128 # that dest gets a list of vals built up when opt is seen
129 class KSOption (Option
):
130 ATTRS
= Option
.ATTRS
+ ['deprecated', 'required']
131 ACTIONS
= Option
.ACTIONS
+ ("map", "map_extend",)
132 STORE_ACTIONS
= Option
.STORE_ACTIONS
+ ("map", "map_extend",)
134 TYPES
= Option
.TYPES
+ ("ksboolean",)
135 TYPE_CHECKER
= copy(Option
.TYPE_CHECKER
)
137 def _check_required(self
):
138 if self
.required
and not self
.takes_value():
139 raise OptionError(_("Required flag set for option that doesn't take a value"), self
)
141 def _check_ksboolean(option
, opt
, value
):
142 if value
.lower() in ("on", "yes", "true", "1"):
144 elif value
.lower() in ("off", "no", "false", "0"):
147 raise OptionValueError(_("Option %s: invalid boolean value: %r") % (opt
, value
))
149 # Make sure _check_required() is called from the constructor!
150 CHECK_METHODS
= Option
.CHECK_METHODS
+ [_check_required
]
151 TYPE_CHECKER
["ksboolean"] = _check_ksboolean
153 def process (self
, opt
, value
, values
, parser
):
154 Option
.process(self
, opt
, value
, values
, parser
)
155 parser
.option_seen
[self
] = 1
157 # Override default take_action method to handle our custom actions.
158 def take_action(self
, action
, dest
, opt
, value
, values
, parser
):
160 values
.ensure_value(dest
, parser
.map[opt
.lstrip('-')])
161 elif action
== "map_extend":
162 values
.ensure_value(dest
, []).extend(parser
.map[opt
.lstrip('-')])
164 Option
.take_action(self
, action
, dest
, opt
, value
, values
, parser
)
170 # You may make a subclass of Script if you need additional script handling
171 # besides just a data representation. For instance, anaconda may subclass
172 # this to add a run method.
175 retval
= ("(s: '%s' i: %s c: %d)") % \
176 (self
.script
, self
.interp
, self
.inChroot
)
177 return string
.replace(retval
, "\n", "|")
179 def __init__(self
, script
, interp
= "/bin/sh", inChroot
= False,
180 logfile
= None, errorOnFail
= False, type = KS_SCRIPT_PRE
):
181 self
.script
= string
.join(script
, "")
183 self
.inChroot
= inChroot
184 self
.logfile
= logfile
185 self
.errorOnFail
= errorOnFail
188 # Produce a string representation of the script suitable for writing
189 # to a kickstart file. Add this to the end of the %whatever header.
192 if self
.interp
!= "/bin/sh" and self
.interp
!= "":
193 retval
= retval
+ " --interp %s" % self
.interp
194 if self
.type == KS_SCRIPT_POST
and not self
.inChroot
:
195 retval
= retval
+ " --nochroot"
196 if self
.logfile
!= None:
197 retval
= retval
+ " --logfile %s" % self
.logfile
199 retval
= retval
+ " --erroronfail"
201 retval
= retval
+ "\n%s\n" % self
.script
208 # You may make a subclass of KickstartHandlers if you need to do something
209 # besides just build up the data store. If you need to do additional processing
210 # just make a subclass, define handlers for each command in your subclass, and
211 # make sure to call the same handler in the super class before whatever you
212 # want to do. Also if you need to make a new parser that only takes action
213 # for a subset of commands, make a subclass and define all the handlers to
214 # None except the ones you care about.
215 class KickstartHandlers
:
216 def __init__ (self
, ksdata
):
219 # These will get set by the handleCommand method in the parser.
223 self
.handlers
= { "auth" : self
.doAuthconfig
,
224 "authconfig" : self
.doAuthconfig
,
225 "autopart" : self
.doAutoPart
,
226 "autostep" : self
.doAutoStep
,
227 "bootloader" : self
.doBootloader
,
228 "cdrom" : self
.doMethod
,
229 "clearpart" : self
.doClearPart
,
230 "cmdline" : self
.doDisplayMode
,
231 "device" : self
.doDevice
,
232 "deviceprobe" : self
.doDeviceProbe
,
233 "driverdisk" : self
.doDriverDisk
,
234 "firewall" : self
.doFirewall
,
235 "firstboot" : self
.doFirstboot
,
236 "graphical" : self
.doDisplayMode
,
237 "halt" : self
.doReboot
,
238 "harddrive" : self
.doMethod
,
239 "ignoredisk" : self
.doIgnoreDisk
,
240 # implied by lack of "upgrade" command
242 "interactive" : self
.doInteractive
,
243 "iscsi" : self
.doIscsi
,
244 "iscsiname" : self
.doIscsiName
,
245 "keyboard" : self
.doKeyboard
,
246 "lang" : self
.doLang
,
247 "langsupport" : self
.doLangSupport
,
248 "logvol" : self
.doLogicalVolume
,
249 "logging" : self
.doLogging
,
250 "mediacheck" : self
.doMediaCheck
,
251 "monitor" : self
.doMonitor
,
252 "mouse" : self
.doMouse
,
253 "network" : self
.doNetwork
,
254 "nfs" : self
.doMethod
,
255 "multipath" : self
.doMultiPath
,
256 "dmraid" : self
.doDmRaid
,
257 "part" : self
.doPartition
,
258 "partition" : self
.doPartition
,
259 "poweroff" : self
.doReboot
,
260 "raid" : self
.doRaid
,
261 "reboot" : self
.doReboot
,
262 "repo" : self
.doRepo
,
263 "rootpw" : self
.doRootPw
,
264 "selinux" : self
.doSELinux
,
265 "services" : self
.doServices
,
266 "shutdown" : self
.doReboot
,
267 "skipx" : self
.doSkipX
,
268 "text" : self
.doDisplayMode
,
269 "timezone" : self
.doTimezone
,
270 "url" : self
.doMethod
,
271 "user" : self
.doUser
,
272 "upgrade" : self
.doUpgrade
,
274 "volgroup" : self
.doVolumeGroup
,
275 "xconfig" : self
.doXConfig
,
276 "zerombr" : self
.doZeroMbr
,
277 "zfcp" : self
.doZFCP
,
280 def _setToDict(self
, optParser
, opts
, dict):
281 for key
in filter (lambda k
: getattr(opts
, k
) != None, optParser
.keys()):
282 dict[key
] = getattr(opts
, key
)
284 def _setToObj(self
, optParser
, opts
, obj
):
285 for key
in filter (lambda k
: getattr(opts
, k
) != None, optParser
.keys()):
286 setattr(obj
, key
, getattr(opts
, key
))
288 def resetHandlers (self
):
289 for key
in self
.handlers
.keys():
290 self
.handlers
[key
] = None
292 def deprecatedCommand(self
, cmd
):
293 warnings
.warn(_("Ignoring deprecated command on line %s: 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.") % (self
.lineno
, cmd
), DeprecationWarning)
295 def doAuthconfig(self
, args
):
296 self
.ksdata
.authconfig
= string
.join(args
)
298 def doAutoPart(self
, args
):
300 raise KickstartValueError
, formatErrorMsg(self
.lineno
, msg
=_("Command %s does not take any arguments") % "autopart")
302 self
.ksdata
.autopart
= True
304 def doAutoStep(self
, args
):
305 op
= KSOptionParser(lineno
=self
.lineno
)
306 op
.add_option("--autoscreenshot", dest
="autoscreenshot",
307 action
="store_true", default
=False)
309 (opts
, extra
) = op
.parse_args(args
=args
)
310 self
._setToDict
(op
, opts
, self
.ksdata
.autostep
)
312 def doBootloader(self
, args
):
313 def driveorder_cb (option
, opt_str
, value
, parser
):
314 for d
in value
.split(','):
315 parser
.values
.ensure_value(option
.dest
, []).append(d
)
317 op
= KSOptionParser(lineno
=self
.lineno
)
318 op
.add_option("--append", dest
="appendLine")
319 op
.add_option("--location", dest
="location", type="choice",
321 choices
=["mbr", "partition", "none", "boot"])
322 op
.add_option("--lba32", dest
="forceLBA", action
="store_true",
324 op
.add_option("--password", dest
="password", default
="")
325 op
.add_option("--md5pass", dest
="md5pass", default
="")
326 op
.add_option("--upgrade", dest
="upgrade", action
="store_true",
328 op
.add_option("--driveorder", dest
="driveorder", action
="callback",
329 callback
=driveorder_cb
, nargs
=1, type="string")
331 (opts
, extra
) = op
.parse_args(args
=args
)
332 self
._setToDict
(op
, opts
, self
.ksdata
.bootloader
)
334 def doClearPart(self
, args
):
335 def drive_cb (option
, opt_str
, value
, parser
):
336 for d
in value
.split(','):
337 parser
.values
.ensure_value(option
.dest
, []).append(d
)
339 op
= KSOptionParser(lineno
=self
.lineno
)
340 op
.add_option("--all", dest
="type", action
="store_const",
341 const
=CLEARPART_TYPE_ALL
)
342 op
.add_option("--drives", dest
="drives", action
="callback",
343 callback
=drive_cb
, nargs
=1, type="string")
344 op
.add_option("--initlabel", dest
="initAll", action
="store_true",
346 op
.add_option("--linux", dest
="type", action
="store_const",
347 const
=CLEARPART_TYPE_LINUX
)
348 op
.add_option("--none", dest
="type", action
="store_const",
349 const
=CLEARPART_TYPE_NONE
)
351 (opts
, extra
) = op
.parse_args(args
=args
)
352 self
._setToDict
(op
, opts
, self
.ksdata
.clearpart
)
354 def doDevice(self
, args
):
355 self
.ksdata
.device
= string
.join(args
)
357 def doDeviceProbe(self
, args
):
358 self
.ksdata
.deviceprobe
= string
.join(args
)
360 def doDisplayMode(self
, args
):
361 if self
.currentCmd
== "cmdline":
362 self
.ksdata
.displayMode
= DISPLAY_MODE_CMDLINE
363 elif self
.currentCmd
== "graphical":
364 self
.ksdata
.displayMode
= DISPLAY_MODE_GRAPHICAL
365 elif self
.currentCmd
== "text":
366 self
.ksdata
.displayMode
= DISPLAY_MODE_TEXT
368 def doDriverDisk(self
, args
):
369 self
.ksdata
.driverdisk
= string
.join(args
)
371 def doFirewall(self
, args
):
372 def firewall_port_cb (option
, opt_str
, value
, parser
):
373 for p
in value
.split(","):
375 if p
.find(":") == -1:
377 parser
.values
.ensure_value(option
.dest
, []).append(p
)
379 op
= KSOptionParser(map={"ssh":["22:tcp"], "telnet":["23:tcp"],
380 "smtp":["25:tcp"], "http":["80:tcp", "443:tcp"],
381 "ftp":["21:tcp"]}, lineno
=self
.lineno
)
383 op
.add_option("--disable", "--disabled", dest
="enabled",
384 action
="store_false")
385 op
.add_option("--enable", "--enabled", dest
="enabled",
386 action
="store_true", default
=True)
387 op
.add_option("--ftp", "--http", "--smtp", "--ssh", "--telnet",
388 dest
="ports", action
="map_extend")
389 op
.add_option("--port", dest
="ports", action
="callback",
390 callback
=firewall_port_cb
, nargs
=1, type="string")
391 op
.add_option("--trust", dest
="trusts", action
="append")
393 (opts
, extra
) = op
.parse_args(args
=args
)
394 self
._setToDict
(op
, opts
, self
.ksdata
.firewall
)
396 def doFirstboot(self
, args
):
397 op
= KSOptionParser(lineno
=self
.lineno
)
398 op
.add_option("--disable", "--disabled", dest
="firstboot",
399 action
="store_const", const
=FIRSTBOOT_SKIP
)
400 op
.add_option("--enable", "--enabled", dest
="firstboot",
401 action
="store_const", const
=FIRSTBOOT_DEFAULT
)
402 op
.add_option("--reconfig", dest
="firstboot", action
="store_const",
403 const
=FIRSTBOOT_RECONFIG
)
405 (opts
, extra
) = op
.parse_args(args
=args
)
406 self
.ksdata
.firstboot
= opts
.firstboot
408 def doIgnoreDisk(self
, args
):
409 def drive_cb (option
, opt_str
, value
, parser
):
410 for d
in value
.split(','):
411 parser
.values
.ensure_value(option
.dest
, []).append(d
)
413 op
= KSOptionParser(lineno
=self
.lineno
)
414 op
.add_option("--drives", dest
="drives", action
="callback",
415 callback
=drive_cb
, nargs
=1, type="string")
417 (opts
, extra
) = op
.parse_args(args
=args
)
419 self
.ksdata
.ignoredisk
= opts
.drives
421 def doInteractive(self
, args
):
423 raise KickstartValueError
, formatErrorMsg(self
.lineno
, msg
=_("Command %s does not take any arguments") % "interactive")
425 self
.ksdata
.interactive
= True
427 def doIscsi(self
, args
):
428 op
= KSOptionParser(lineno
=self
.lineno
)
429 op
.add_option("--target", dest
="ipaddr", action
="store", type="string")
430 op
.add_option("--ipaddr", dest
="ipaddr", action
="store", type="string")
431 op
.add_option("--port", dest
="port", action
="store", type="string")
432 op
.add_option("--user", dest
="user", action
="store", type="string")
433 op
.add_option("--password", dest
="password", action
="store",
436 (opts
, extra
) = op
.parse_args(args
=args
)
438 raise KickstartValueError
, formatErrorMsg(self
.lineno
, msg
=_("IP Address of iSCSI target required"))
440 raise KickstartValueError
, formatErrorMsg(self
.lineno
, msg
=_("Unexpected arguments for iscsi command"))
442 dd
= KickstartIscsiData()
443 self
._setToObj
(op
, opts
, dd
)
444 self
.ksdata
.iscsi
.append(dd
)
446 def doIscsiName(self
, args
):
448 raise KickstartValueError
, formatErrorMsg(self
.lineno
, msg
=_("Command %s only takes one argument") % "iscsiname")
449 self
.ksdata
.iscsiname
= args
[0]
451 def doKeyboard(self
, args
):
453 raise KickstartValueError
, formatErrorMsg(self
.lineno
, msg
=_("Command %s only takes one argument") % "keyboard")
455 self
.ksdata
.keyboard
= args
[0]
457 def doLang(self
, args
):
459 raise KickstartValueError
, formatErrorMsg(self
.lineno
, msg
=_("Command %s only takes one argument") % "lang")
461 self
.ksdata
.lang
= args
[0]
463 def doLangSupport(self
, args
):
464 self
.deprecatedCommand("langsupport")
466 def doLogicalVolume(self
, args
):
467 def lv_cb (option
, opt_str
, value
, parser
):
468 parser
.values
.format
= False
469 parser
.values
.preexist
= True
471 op
= KSOptionParser(lineno
=self
.lineno
)
472 op
.add_option("--bytes-per-inode", dest
="bytesPerInode", action
="store",
474 op
.add_option("--fsoptions", dest
="fsopts")
475 op
.add_option("--fstype", dest
="fstype")
476 op
.add_option("--grow", dest
="grow", action
="store_true",
478 op
.add_option("--maxsize", dest
="maxSizeMB", action
="store", type="int",
480 op
.add_option("--name", dest
="name", required
=1)
481 op
.add_option("--noformat", action
="callback", callback
=lv_cb
,
482 dest
="format", default
=True, nargs
=0)
483 op
.add_option("--percent", dest
="percent", action
="store", type="int",
485 op
.add_option("--recommended", dest
="recommended", action
="store_true",
487 op
.add_option("--size", dest
="size", action
="store", type="int",
489 op
.add_option("--useexisting", dest
="preexist", action
="store_true",
491 op
.add_option("--vgname", dest
="vgname", required
=1)
493 (opts
, extra
) = op
.parse_args(args
=args
)
496 raise KickstartValueError
, formatErrorMsg(self
.lineno
, msg
=_("Mount point required for %s") % "logvol")
498 lvd
= KickstartLogVolData()
499 self
._setToObj
(op
, opts
, lvd
)
500 lvd
.mountpoint
= extra
[0]
501 self
.ksdata
.lvList
.append(lvd
)
503 def doLogging(self
, args
):
504 op
= KSOptionParser(lineno
=self
.lineno
)
505 op
.add_option("--host")
506 op
.add_option("--level", type="choice",
507 choices
=["debug", "info", "warning", "error", "critical"])
508 op
.add_option("--port")
510 (opts
, extra
) = op
.parse_args(args
=args
)
511 self
._setToDict
(op
, opts
, self
.ksdata
.logging
)
513 def doMediaCheck(self
, args
):
515 raise KickstartValueError
, formatErrorMsg(self
.lineno
, msg
=_("Command %s does not take any arguments") % "mediacheck")
517 self
.ksdata
.mediacheck
= True
519 def doMethod(self
, args
):
520 op
= KSOptionParser(lineno
=self
.lineno
)
522 self
.ksdata
.method
["method"] = self
.currentCmd
524 if self
.currentCmd
== "cdrom":
526 elif self
.currentCmd
== "harddrive":
527 op
.add_option("--partition", dest
="partition", required
=1)
528 op
.add_option("--dir", dest
="dir", required
=1)
529 elif self
.currentCmd
== "nfs":
530 op
.add_option("--server", dest
="server", required
=1)
531 op
.add_option("--dir", dest
="dir", required
=1)
532 op
.add_option("--opts", dest
="opts", required
=0)
533 elif self
.currentCmd
== "url":
534 op
.add_option("--url", dest
="url", required
=1)
536 (opts
, extra
) = op
.parse_args(args
=args
)
537 self
._setToDict
(op
, opts
, self
.ksdata
.method
)
539 def doMonitor(self
, args
):
540 op
= KSOptionParser(lineno
=self
.lineno
)
541 op
.add_option("--hsync", dest
="hsync")
542 op
.add_option("--monitor", dest
="monitor")
543 op
.add_option("--noprobe", dest
="probe", action
="store_false",
545 op
.add_option("--vsync", dest
="vsync")
547 (opts
, extra
) = op
.parse_args(args
=args
)
550 raise KickstartValueError
, formatErrorMsg(self
.lineno
, msg
=_("Unexpected arguments to %s command: %s") % ("monitor", extra
))
552 self
._setToDict
(op
, opts
, self
.ksdata
.monitor
)
554 def doMouse(self
, args
):
555 self
.deprecatedCommand("mouse")
557 def doNetwork(self
, args
):
558 op
= KSOptionParser(lineno
=self
.lineno
)
559 op
.add_option("--bootproto", dest
="bootProto", default
="dhcp",
560 choices
=["dhcp", "bootp", "static"])
561 op
.add_option("--class", dest
="dhcpclass")
562 op
.add_option("--device", dest
="device")
563 op
.add_option("--essid", dest
="essid")
564 op
.add_option("--ethtool", dest
="ethtool")
565 op
.add_option("--gateway", dest
="gateway")
566 op
.add_option("--hostname", dest
="hostname")
567 op
.add_option("--ip", dest
="ip")
568 op
.add_option("--mtu", dest
="mtu")
569 op
.add_option("--nameserver", dest
="nameserver")
570 op
.add_option("--netmask", dest
="netmask")
571 op
.add_option("--nodns", dest
="nodns", action
="store_true",
573 op
.add_option("--noipv4", dest
="ipv4", action
="store_false",
575 op
.add_option("--noipv6", dest
="ipv6", action
="store_false",
577 op
.add_option("--notksdevice", dest
="notksdevice", action
="store_true",
579 op
.add_option("--onboot", dest
="onboot", action
="store",
581 op
.add_option("--wepkey", dest
="wepkey")
583 (opts
, extra
) = op
.parse_args(args
=args
)
585 nd
= KickstartNetworkData()
586 self
._setToObj
(op
, opts
, nd
)
587 self
.ksdata
.network
.append(nd
)
589 def doMultiPath(self
, args
):
590 op
= KSOptionParser(lineno
=self
.lineno
)
591 op
.add_option("--name", dest
="name", action
="store", type="string",
593 op
.add_option("--device", dest
="device", action
="store", type="string",
595 op
.add_option("--rule", dest
="rule", action
="store", type="string",
598 (opts
, extra
) = op
.parse_args(args
=args
)
600 dd
= KickstartMpPathData()
601 self
._setToObj
(op
, opts
, dd
)
602 dd
.mpdev
= dd
.mpdev
.split('/')[-1]
605 for x
in range(0, len(self
.ksdata
.mpaths
)):
606 mpath
= self
.ksdata
.mpaths
[x
]
607 for path
in mpath
.paths
:
608 if path
.device
== dd
.device
:
609 raise KickstartValueError
, formatErrorMsg(self
.lineno
, msg
=_("Device '%s' is already used in multipath '%s'") % (path
.device
, path
.mpdev
))
610 if mpath
.name
== dd
.mpdev
:
614 mpath
= KickstartMultiPathData()
615 self
.ksdata
.mpaths
.append(mpath
)
617 mpath
= self
.ksdata
.mpaths
[x
]
619 mpath
.paths
.append(dd
)
621 def doDmRaid(self
, args
):
622 op
= KSOptionParser(lineno
=self
.lineno
)
623 op
.add_option("--name", dest
="name", action
="store", type="string",
625 op
.add_option("--dev", dest
="devices", action
="append", type="string",
628 (opts
, extra
) = op
.parse_args(args
=args
)
630 dd
= KickstartDmRaidData()
631 self
._setToObj
(op
, opts
, dd
)
632 dd
.name
= dd
.name
.split('/')[-1]
633 self
.ksdata
.dmraids
.append(dd
)
635 def doPartition(self
, args
):
636 def part_cb (option
, opt_str
, value
, parser
):
637 if value
.startswith("/dev/"):
638 parser
.values
.ensure_value(option
.dest
, value
[5:])
640 parser
.values
.ensure_value(option
.dest
, value
)
642 op
= KSOptionParser(lineno
=self
.lineno
)
643 op
.add_option("--active", dest
="active", action
="store_true",
645 op
.add_option("--asprimary", dest
="primOnly", action
="store_true",
647 op
.add_option("--bytes-per-inode", dest
="bytesPerInode", action
="store",
649 op
.add_option("--end", dest
="end", action
="store", type="int",
651 op
.add_option("--fsoptions", dest
="fsopts")
652 op
.add_option("--fstype", "--type", dest
="fstype")
653 op
.add_option("--grow", dest
="grow", action
="store_true", default
=False)
654 op
.add_option("--label", dest
="label")
655 op
.add_option("--maxsize", dest
="maxSizeMB", action
="store", type="int",
657 op
.add_option("--noformat", dest
="format", action
="store_false",
659 op
.add_option("--onbiosdisk", dest
="onbiosdisk")
660 op
.add_option("--ondisk", "--ondrive", dest
="disk")
661 op
.add_option("--onpart", "--usepart", dest
="onPart", action
="callback",
662 callback
=part_cb
, nargs
=1, type="string")
663 op
.add_option("--recommended", dest
="recommended", action
="store_true",
665 op
.add_option("--size", dest
="size", action
="store", type="int",
667 op
.add_option("--start", dest
="start", action
="store", type="int",
670 (opts
, extra
) = op
.parse_args(args
=args
)
673 raise KickstartValueError
, formatErrorMsg(self
.lineno
, msg
=_("Mount point required for %s") % "partition")
675 pd
= KickstartPartData()
676 self
._setToObj
(op
, opts
, pd
)
677 pd
.mountpoint
= extra
[0]
678 self
.ksdata
.partitions
.append(pd
)
680 def doReboot(self
, args
):
681 if self
.currentCmd
== "reboot":
682 self
.ksdata
.reboot
["action"] = KS_REBOOT
684 self
.ksdata
.reboot
["action"] = KS_SHUTDOWN
686 op
= KSOptionParser(lineno
=self
.lineno
)
687 op
.add_option("--eject", dest
="eject", action
="store_true",
690 (opts
, extra
) = op
.parse_args(args
=args
)
691 self
._setToDict
(op
, opts
, self
.ksdata
.reboot
)
693 def doRepo(self
, args
):
694 op
= KSOptionParser(lineno
=self
.lineno
)
695 op
.add_option("--name", dest
="name", required
=1)
696 op
.add_option("--baseurl")
697 op
.add_option("--mirrorlist")
699 (opts
, extra
) = op
.parse_args(args
=args
)
701 # This is lame, but I can't think of a better way to make sure only
702 # one of these two is specified.
703 if opts
.baseurl
and opts
.mirrorlist
:
704 raise KickstartValueError
, formatErrorMsg(self
.lineno
, msg
=_("Only one of --baseurl and --mirrorlist may be specified for repo command."))
706 if not opts
.baseurl
and not opts
.mirrorlist
:
707 raise KickstartValueError
, formatErrorMsg(self
.lineno
, msg
=_("One of --baseurl or --mirrorlist must be specified for repo command."))
709 rd
= KickstartRepoData()
710 self
._setToObj
(op
, opts
, rd
)
711 self
.ksdata
.repoList
.append(rd
)
713 def doRaid(self
, args
):
714 def raid_cb (option
, opt_str
, value
, parser
):
715 parser
.values
.format
= False
716 parser
.values
.preexist
= True
718 def device_cb (option
, opt_str
, value
, parser
):
719 if value
[0:2] == "md":
720 parser
.values
.ensure_value(option
.dest
, value
[2:])
722 parser
.values
.ensure_value(option
.dest
, value
)
724 def level_cb (option
, opt_str
, value
, parser
):
725 if value
== "RAID0" or value
== "0":
726 parser
.values
.ensure_value(option
.dest
, "RAID0")
727 elif value
== "RAID1" or value
== "1":
728 parser
.values
.ensure_value(option
.dest
, "RAID1")
729 elif value
== "RAID5" or value
== "5":
730 parser
.values
.ensure_value(option
.dest
, "RAID5")
731 elif value
== "RAID6" or value
== "6":
732 parser
.values
.ensure_value(option
.dest
, "RAID6")
734 op
= KSOptionParser(lineno
=self
.lineno
)
735 op
.add_option("--bytes-per-inode", dest
="bytesPerInode", action
="store",
737 op
.add_option("--device", action
="callback", callback
=device_cb
,
738 dest
="device", type="string", nargs
=1, required
=1)
739 op
.add_option("--fsoptions", dest
="fsopts")
740 op
.add_option("--fstype", dest
="fstype")
741 op
.add_option("--level", dest
="level", action
="callback",
742 callback
=level_cb
, type="string", nargs
=1)
743 op
.add_option("--noformat", action
="callback", callback
=raid_cb
,
744 dest
="format", default
=True, nargs
=0)
745 op
.add_option("--spares", dest
="spares", action
="store", type="int",
747 op
.add_option("--useexisting", dest
="preexist", action
="store_true",
750 (opts
, extra
) = op
.parse_args(args
=args
)
753 raise KickstartValueError
, formatErrorMsg(self
.lineno
, msg
=_("Mount point required for %s") % "raid")
755 rd
= KickstartRaidData()
756 self
._setToObj
(op
, opts
, rd
)
758 # --device can't just take an int in the callback above, because it
759 # could be specificed as "mdX", which causes optparse to error when
761 rd
.device
= int(rd
.device
)
762 rd
.mountpoint
= extra
[0]
763 rd
.members
= extra
[1:]
764 self
.ksdata
.raidList
.append(rd
)
766 def doRootPw(self
, args
):
767 op
= KSOptionParser(lineno
=self
.lineno
)
768 op
.add_option("--iscrypted", dest
="isCrypted", action
="store_true",
771 (opts
, extra
) = op
.parse_args(args
=args
)
772 self
._setToDict
(op
, opts
, self
.ksdata
.rootpw
)
775 raise KickstartValueError
, formatErrorMsg(self
.lineno
, msg
=_("A single argument is expected for the %s command") % "rootpw")
777 self
.ksdata
.rootpw
["password"] = extra
[0]
779 def doSELinux(self
, args
):
780 op
= KSOptionParser(lineno
=self
.lineno
)
781 op
.add_option("--disabled", dest
="sel", action
="store_const",
782 const
=SELINUX_DISABLED
)
783 op
.add_option("--enforcing", dest
="sel", action
="store_const",
784 const
=SELINUX_ENFORCING
)
785 op
.add_option("--permissive", dest
="sel", action
="store_const",
786 const
=SELINUX_PERMISSIVE
)
788 (opts
, extra
) = op
.parse_args(args
=args
)
789 self
.ksdata
.selinux
= opts
.sel
791 def doServices(self
, args
):
792 def services_cb (option
, opt_str
, value
, parser
):
793 for d
in value
.split(','):
794 parser
.values
.ensure_value(option
.dest
, []).append(d
)
796 op
= KSOptionParser(lineno
=self
.lineno
)
797 op
.add_option("--disabled", dest
="disabled", action
="callback",
798 callback
=services_cb
, nargs
=1, type="string")
799 op
.add_option("--enabled", dest
="enabled", action
="callback",
800 callback
=services_cb
, nargs
=1, type="string")
802 (opts
, extra
) = op
.parse_args(args
=args
)
803 self
._setToDict
(op
, opts
, self
.ksdata
.services
)
805 def doSkipX(self
, args
):
807 raise KickstartValueError
, formatErrorMsg(self
.lineno
, msg
=_("Command %s does not take any arguments") % "skipx")
809 self
.ksdata
.skipx
= True
811 def doTimezone(self
, args
):
812 op
= KSOptionParser(lineno
=self
.lineno
)
813 op
.add_option("--utc", "--isUtc", dest
="isUtc", action
="store_true", default
=False)
815 (opts
, extra
) = op
.parse_args(args
=args
)
816 self
._setToDict
(op
, opts
, self
.ksdata
.timezone
)
819 raise KickstartValueError
, formatErrorMsg(self
.lineno
, msg
=_("A single argument is expected for the %s command") % "timezone")
821 self
.ksdata
.timezone
["timezone"] = extra
[0]
823 def doUpgrade(self
, args
):
825 raise KickstartValueError
, formatErrorMsg(self
.lineno
, msg
=_("Command %s does not take any arguments") % "upgrade")
827 self
.ksdata
.upgrade
= True
829 def doUser(self
, args
):
830 def groups_cb (option
, opt_str
, value
, parser
):
831 for d
in value
.split(','):
832 parser
.values
.ensure_value(option
.dest
, []).append(d
)
834 op
= KSOptionParser(lineno
=self
.lineno
)
835 op
.add_option("--groups", dest
="groups", action
="callback",
836 callback
=groups_cb
, nargs
=1, type="string")
837 op
.add_option("--homedir")
838 op
.add_option("--iscrypted", dest
="isCrypted", action
="store_true",
840 op
.add_option("--name", required
=1)
841 op
.add_option("--password")
842 op
.add_option("--shell")
843 op
.add_option("--uid", type="int")
845 (opts
, extra
) = op
.parse_args(args
=args
)
847 user
= KickstartUserData()
848 self
._setToObj
(op
, opts
, user
)
849 self
.ksdata
.userList
.append(user
)
851 def doVnc(self
, args
):
852 def connect_cb (option
, opt_str
, value
, parser
):
853 cargs
= value
.split(":")
854 parser
.values
.ensure_value("host", cargs
[0])
857 parser
.values
.ensure_value("port", cargs
[1])
859 op
= KSOptionParser(lineno
=self
.lineno
)
860 op
.add_option("--connect", action
="callback", callback
=connect_cb
,
861 nargs
=1, type="string", deprecated
=1)
862 op
.add_option("--password", dest
="password")
863 op
.add_option("--host", dest
="host")
864 op
.add_option("--port", dest
="port")
866 self
.ksdata
.vnc
["enabled"] = True
868 (opts
, extra
) = op
.parse_args(args
=args
)
869 self
._setToDict
(op
, opts
, self
.ksdata
.vnc
)
871 def doVolumeGroup(self
, args
):
872 # Have to be a little more complicated to set two values.
873 def vg_cb (option
, opt_str
, value
, parser
):
874 parser
.values
.format
= False
875 parser
.values
.preexist
= True
877 op
= KSOptionParser(lineno
=self
.lineno
)
878 op
.add_option("--noformat", action
="callback", callback
=vg_cb
,
879 dest
="format", default
=True, nargs
=0)
880 op
.add_option("--pesize", dest
="pesize", type="int", nargs
=1,
882 op
.add_option("--useexisting", dest
="preexist", action
="store_true",
885 (opts
, extra
) = op
.parse_args(args
=args
)
887 vgd
= KickstartVolGroupData()
888 self
._setToObj
(op
, opts
, vgd
)
889 vgd
.vgname
= extra
[0]
890 vgd
.physvols
= extra
[1:]
891 self
.ksdata
.vgList
.append(vgd
)
893 def doXConfig(self
, args
):
894 op
= KSOptionParser(lineno
=self
.lineno
)
895 op
.add_option("--card", deprecated
=1)
896 op
.add_option("--driver", dest
="driver")
897 op
.add_option("--defaultdesktop", dest
="defaultdesktop")
898 op
.add_option("--depth", dest
="depth", action
="store", type="int",
900 op
.add_option("--hsync", deprecated
=1)
901 op
.add_option("--monitor", deprecated
=1)
902 op
.add_option("--noprobe", deprecated
=1)
903 op
.add_option("--resolution", dest
="resolution")
904 op
.add_option("--startxonboot", dest
="startX", action
="store_true",
906 op
.add_option("--videoram", dest
="videoRam")
907 op
.add_option("--vsync", deprecated
=1)
909 (opts
, extra
) = op
.parse_args(args
=args
)
911 raise KickstartValueError
, formatErrorMsg(self
.lineno
, msg
=_("Unexpected arguments to %s command: %s" % ("xconfig", extra
)))
913 self
._setToDict
(op
, opts
, self
.ksdata
.xconfig
)
915 def doZeroMbr(self
, args
):
917 warnings
.warn(_("Ignoring deprecated option on line %s: The zerombr command no longer takes any options. In future releases, this will result in a fatal error from kickstart. Please modify your kickstart file to remove any options.") % self
.lineno
, DeprecationWarning)
919 self
.ksdata
.zerombr
= True
921 def doZFCP(self
, args
):
922 op
= KSOptionParser(lineno
=self
.lineno
)
923 op
.add_option("--devnum", dest
="devnum", required
=1)
924 op
.add_option("--fcplun", dest
="fcplun", required
=1)
925 op
.add_option("--scsiid", dest
="scsiid")
926 op
.add_option("--scsilun", dest
="scsilun")
927 op
.add_option("--wwpn", dest
="wwpn", required
=1)
929 (opts
, extra
) = op
.parse_args(args
=args
)
931 dd
= KickstartZFCPData()
932 self
._setToObj
(op
, opts
, dd
)
933 self
.ksdata
.zfcp
.append(dd
)
939 # The kickstart file parser. This only transitions between states and calls
940 # handlers at certain points. To create a specialized parser, make a subclass
941 # of this and override the methods you care about. Methods that don't need to
942 # do anything may just pass.
944 # Passing None for kshandlers is valid just in case you don't care about
945 # handling any commands.
946 class KickstartParser
:
947 def __init__ (self
, ksdata
, kshandlers
, followIncludes
=True,
948 errorsAreFatal
=True, missingIncludeIsFatal
=True):
949 self
.handler
= kshandlers
951 self
.followIncludes
= followIncludes
952 self
.missingIncludeIsFatal
= missingIncludeIsFatal
953 self
.state
= STATE_COMMANDS
955 self
.includeDepth
= 0
956 self
.errorsAreFatal
= errorsAreFatal
958 # Functions to be called when we are at certain points in the
959 # kickstart file parsing. Override these if you need special
961 def addScript (self
):
962 if string
.join(self
.script
["body"]).strip() == "":
965 s
= Script (self
.script
["body"], self
.script
["interp"],
966 self
.script
["chroot"], self
.script
["log"],
967 self
.script
["errorOnFail"], self
.script
["type"])
969 self
.ksdata
.scripts
.append(s
)
971 def addPackages (self
, line
):
972 stripped
= line
.strip()
974 if stripped
[0] == '@':
975 self
.ksdata
.groupList
.append(stripped
[1:].lstrip())
976 elif stripped
[0] == '-':
977 self
.ksdata
.excludedList
.append(stripped
[1:].lstrip())
979 self
.ksdata
.packageList
.append(stripped
)
981 def handleCommand (self
, lineno
, args
):
988 if not self
.handler
.handlers
.has_key(cmd
):
989 raise KickstartParseError
, formatErrorMsg(lineno
, msg
=_("Unknown command: %s" % cmd
))
991 if self
.handler
.handlers
[cmd
] != None:
992 self
.handler
.currentCmd
= cmd
993 self
.handler
.lineno
= lineno
994 self
.handler
.handlers
[cmd
](cmdArgs
)
996 def handlePackageHdr (self
, lineno
, args
):
997 op
= KSOptionParser(lineno
=lineno
)
998 op
.add_option("--excludedocs", dest
="excludedocs", action
="store_true",
1000 op
.add_option("--ignoremissing", dest
="ignoremissing",
1001 action
="store_true", default
=False)
1002 op
.add_option("--nobase", dest
="nobase", action
="store_true",
1004 op
.add_option("--ignoredeps", dest
="resolveDeps", action
="store_false",
1006 op
.add_option("--resolvedeps", dest
="resolveDeps", action
="store_true",
1009 (opts
, extra
) = op
.parse_args(args
=args
[1:])
1011 self
.ksdata
.excludeDocs
= opts
.excludedocs
1012 self
.ksdata
.addBase
= not opts
.nobase
1013 if opts
.ignoremissing
:
1014 self
.ksdata
.handleMissing
= KS_MISSING_IGNORE
1016 self
.ksdata
.handleMissing
= KS_MISSING_PROMPT
1018 def handleScriptHdr (self
, lineno
, args
):
1019 op
= KSOptionParser(lineno
=lineno
)
1020 op
.add_option("--erroronfail", dest
="errorOnFail", action
="store_true",
1022 op
.add_option("--interpreter", dest
="interpreter", default
="/bin/sh")
1023 op
.add_option("--log", "--logfile", dest
="log")
1025 if args
[0] == "%pre" or args
[0] == "%traceback":
1026 self
.script
["chroot"] = False
1027 elif args
[0] == "%post":
1028 self
.script
["chroot"] = True
1029 op
.add_option("--nochroot", dest
="nochroot", action
="store_true",
1032 (opts
, extra
) = op
.parse_args(args
=args
[1:])
1034 self
.script
["interp"] = opts
.interpreter
1035 self
.script
["log"] = opts
.log
1036 self
.script
["errorOnFail"] = opts
.errorOnFail
1037 if hasattr(opts
, "nochroot"):
1038 self
.script
["chroot"] = not opts
.nochroot
1040 # Parser state machine. Don't override this in a subclass.
1041 def readKickstart (self
, file):
1042 # For error reporting.
1050 line
= fh
.readline()
1054 # At the end of an included file
1055 if line
== "" and self
.includeDepth
> 0:
1059 # Don't eliminate whitespace or comments from scripts.
1060 if line
.isspace() or (line
!= "" and line
.lstrip()[0] == '#'):
1061 # Save the platform for s-c-kickstart, though.
1062 if line
[:10] == "#platform=" and self
.state
== STATE_COMMANDS
:
1063 self
.ksdata
.platform
= line
[11:]
1065 if self
.state
in [STATE_PRE
, STATE_POST
, STATE_TRACEBACK
]:
1066 self
.script
["body"].append(line
)
1071 # We only want to split the line if we're outside of a script,
1072 # as inside the script might involve some pretty weird quoting
1073 # that shlex doesn't understand.
1074 if self
.state
in [STATE_PRE
, STATE_POST
, STATE_TRACEBACK
]:
1075 # Have we found a state transition? If so, we still want
1076 # to split. Otherwise, args won't be set but we'll fall through
1077 # all the way to the last case.
1078 if line
!= "" and string
.split(line
.lstrip())[0] in \
1079 ["%post", "%pre", "%traceback", "%include", "%packages"]:
1080 args
= shlex
.split(line
)
1084 args
= shlex
.split(line
)
1086 if args
and args
[0] == "%include":
1087 # This case comes up primarily in ksvalidator.
1088 if not self
.followIncludes
:
1093 raise KickstartParseError
, formatErrorMsg(lineno
)
1095 self
.includeDepth
+= 1
1098 self
.readKickstart (args
[1])
1100 # Handle the include file being provided over the
1101 # network in a %pre script. This case comes up in the
1102 # early parsing in anaconda.
1103 if self
.missingIncludeIsFatal
:
1106 self
.includeDepth
-= 1
1110 if self
.state
== STATE_COMMANDS
:
1111 if not args
and self
.includeDepth
== 0:
1112 self
.state
= STATE_END
1113 elif args
[0] in ["%pre", "%post", "%traceback"]:
1114 self
.state
= STATE_SCRIPT_HDR
1115 elif args
[0] == "%packages":
1116 self
.state
= STATE_PACKAGES
1117 elif args
[0][0] == '%':
1118 # This error is too difficult to continue from, without
1119 # lots of resync code. So just print this one and quit.
1120 raise KickstartParseError
, formatErrorMsg(lineno
)
1124 if self
.errorsAreFatal
:
1125 self
.handleCommand(lineno
, args
)
1128 self
.handleCommand(lineno
, args
)
1129 except Exception, msg
:
1132 elif self
.state
== STATE_PACKAGES
:
1133 if not args
and self
.includeDepth
== 0:
1134 self
.state
= STATE_END
1135 elif args
[0] in ["%pre", "%post", "%traceback"]:
1136 self
.state
= STATE_SCRIPT_HDR
1137 elif args
[0] == "%packages":
1140 if self
.errorsAreFatal
:
1141 self
.handlePackageHdr (lineno
, args
)
1144 self
.handlePackageHdr (lineno
, args
)
1145 except Exception, msg
:
1147 elif args
[0][0] == '%':
1148 # This error is too difficult to continue from, without
1149 # lots of resync code. So just print this one and quit.
1150 raise KickstartParseError
, formatErrorMsg(lineno
)
1153 self
.addPackages (string
.rstrip(line
))
1155 elif self
.state
== STATE_SCRIPT_HDR
:
1157 self
.script
= {"body": [], "interp": "/bin/sh", "log": None,
1158 "errorOnFail": False}
1160 if not args
and self
.includeDepth
== 0:
1161 self
.state
= STATE_END
1162 elif args
[0] == "%pre":
1163 self
.state
= STATE_PRE
1164 self
.script
["type"] = KS_SCRIPT_PRE
1165 elif args
[0] == "%post":
1166 self
.state
= STATE_POST
1167 self
.script
["type"] = KS_SCRIPT_POST
1168 elif args
[0] == "%traceback":
1169 self
.state
= STATE_TRACEBACK
1170 self
.script
["type"] = KS_SCRIPT_TRACEBACK
1171 elif args
[0][0] == '%':
1172 # This error is too difficult to continue from, without
1173 # lots of resync code. So just print this one and quit.
1174 raise KickstartParseError
, formatErrorMsg(lineno
)
1176 if self
.errorsAreFatal
:
1177 self
.handleScriptHdr (lineno
, args
)
1180 self
.handleScriptHdr (lineno
, args
)
1181 except Exception, msg
:
1184 elif self
.state
in [STATE_PRE
, STATE_POST
, STATE_TRACEBACK
]:
1185 if line
== "" and self
.includeDepth
== 0:
1186 # If we're at the end of the kickstart file, add the script.
1188 self
.state
= STATE_END
1189 elif args
and args
[0] in ["%pre", "%post", "%traceback", "%packages"]:
1190 # Otherwise we're now at the start of the next section.
1191 # Figure out what kind of a script we just finished
1192 # reading, add it to the list, and switch to the initial
1195 self
.state
= STATE_COMMANDS
1197 # Otherwise just add to the current script body.
1198 self
.script
["body"].append(line
)
1201 elif self
.state
== STATE_END
: