2 # Chris Lumens <clumens@redhat.com>
4 # Copyright 2006, 2007 Red Hat, Inc.
6 # This software may be freely redistributed under the terms of the GNU
7 # general public license.
9 # You should have received a copy of the GNU General Public License
10 # along with this program; if not, write to the Free Software
11 # Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
14 Base classes for creating commands and syntax version object.
16 This module exports several important base classes:
18 BaseData - The base abstract class for all data objects. Data objects
19 are contained within a BaseHandler object.
21 BaseHandler - The base abstract class from which versioned kickstart
22 handler are derived. Subclasses of BaseHandler hold
23 BaseData and KickstartCommand objects.
25 DeprecatedCommand - A concrete subclass of KickstartCommand that should
26 be further subclassed by users of this module. When
27 a subclass is used, a warning message will be
30 KickstartCommand - The base abstract class for all kickstart commands.
31 Command objects are contained within a BaseHandler
34 from rhpl
.translate
import _
35 import rhpl
.translate
as translate
37 translate
.textdomain("pykickstart")
40 from pykickstart
.errors
import *
41 from pykickstart
.parser
import Packages
46 class KickstartCommand
:
47 """The base class for all kickstart commands. This is an abstract class."""
48 def __init__(self
, writePriority
=0):
49 """Create a new KickstartCommand instance. This method must be
50 provided by all subclasses, but subclasses must call
51 KickstartCommand.__init__ first. Instance attributes:
53 currentCmd -- The name of the command in the input file that
54 caused this handler to be run.
55 handler -- A reference to the BaseHandler subclass this
56 command is contained withing. This is needed to
57 allow referencing of Data objects.
58 lineno -- The current line number in the input file.
59 writePriority -- An integer specifying when this command should be
60 printed when iterating over all commands' __str__
61 methods. The higher the number, the later this
62 command will be written. All commands with the
63 same priority will be written alphabetically.
66 # We don't want people using this class by itself.
67 if self
.__class
__ is KickstartCommand
:
68 raise TypeError, "KickstartCommand is an abstract class."
70 self
.writePriority
= writePriority
72 # These will be set by the dispatcher.
77 def __call__(self
, *args
, **kwargs
):
78 """Set multiple attributes on a subclass of KickstartCommand at once
79 via keyword arguments. Valid attributes are anything specified in
80 a subclass, but unknown attributes will be ignored.
82 for (key
, val
) in kwargs
.items():
83 if hasattr(self
, key
):
84 setattr(self
, key
, val
)
87 """Return a string formatted for output to a kickstart file. This
88 method must be provided by all subclasses.
90 raise TypeError, "__str__() not implemented for KickstartCommand"
92 def parse(self
, args
):
93 """Parse the list of args and set data on the KickstartCommand object.
94 This method must be provided by all subclasses.
96 raise TypeError, "parse() not implemented for KickstartCommand"
98 # Set the contents of the opts object (an instance of optparse.Values
99 # returned by parse_args) as attributes on the KickstartCommand object.
100 # It's useful to call this from KickstartCommand subclasses after parsing
102 def _setToSelf(self
, optParser
, opts
):
103 for key
in filter (lambda k
: getattr(opts
, k
) != None, optParser
.keys()):
104 setattr(self
, key
, getattr(opts
, key
))
106 # Sets the contents of the opts object (an instance of optparse.Values
107 # returned by parse_args) as attributes on the provided object obj. It's
108 # useful to call this from KickstartCommand subclasses that handle lists
109 # of objects (like partitions, network devices, etc.) and need to populate
111 def _setToObj(self
, optParser
, opts
, obj
):
112 for key
in filter (lambda k
: getattr(opts
, k
) != None, optParser
.keys()):
113 setattr(obj
, key
, getattr(opts
, key
))
115 class DeprecatedCommand(KickstartCommand
):
116 """Specify that a command is deprecated and no longer has any function.
117 Any command that is deprecated should be subclassed from this class,
118 only specifying an __init__ method that calls the superclass's __init__.
120 def __init__(self
, writePriority
=None):
121 """Create a new DeprecatedCommand instance."""
122 KickstartCommand
.__init
__(self
, writePriority
)
125 """Placeholder since DeprecatedCommands don't work anymore."""
128 def parse(self
, args
):
129 """Print a warning message if the command is seen in the input file."""
130 mapping
= {"lineno": self
.lineno
, "cmd": self
.currentCmd
}
131 warnings
.warn(_("Ignoring deprecated command on line %(lineno)s: The %(cmd)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.") % mapping
, DeprecationWarning)
137 """Each version of kickstart syntax is provided by a subclass of this
138 class. These subclasses are what users will interact with for parsing,
139 extracting data, and writing out kickstart files. This is an abstract
143 """version -- The version this syntax handler supports. This is set by
144 a class attribute of a BaseHandler subclass and is used to
145 set up the command dict. It is for read-only use.
149 def __init__(self
, mapping
={}):
150 """Create a new BaseHandler instance. This method must be provided by
151 all subclasses, but subclasses must call BaseHandler.__init__ first.
152 mapping is a custom map from command strings to classes, useful when
153 creating your own handler with special command objects. It is
154 otherwise unused and rarely needed.
158 commands -- A mapping from a string command to a KickstartCommand
159 subclass object that handles it. Multiple strings can
160 map to the same object, but only one instance of the
161 command object should ever exist. Most users should
162 never have to deal with this directly, as it is
163 manipulated internally and called through dispatcher.
164 packages -- An instance of pykickstart.parser.Packages which
165 describes the packages section of the input file.
166 platform -- A string describing the hardware platform, which is
167 needed only by system-config-kickstart.
168 scripts -- A list of pykickstart.parser.Script instances, which is
169 populated by KickstartParser.addScript and describes the
170 %pre/%post/%traceback script section of the input file.
173 # We don't want people using this class by itself.
174 if self
.__class
__ is BaseHandler
:
175 raise TypeError, "BaseHandler is an abstract class."
177 # This isn't really a good place for these, but it's better than
178 # everything else I can think of.
180 self
.packages
= Packages()
185 # A dict keyed by an integer priority number, with each value being a
186 # list of KickstartCommand subclasses. This dict is maintained by
187 # registerCommand and used in __str__. No one else should be touching
189 self
._writeOrder
= {}
191 self
._registerCommands
(mapping
=mapping
)
194 """Return a string formatted for output to a kickstart file."""
197 if self
.platform
!= "":
198 retval
+= "#platform=%s\n" % self
.platform
200 lst
= self
._writeOrder
.keys()
204 for obj
in self
._writeOrder
[prio
]:
205 retval
+= obj
.__str
__()
207 for script
in self
.scripts
:
208 retval
+= script
.__str
__()
210 retval
+= self
.packages
.__str
__()
214 def _insertSorted(self
, list, obj
):
219 # If the two classes have the same name, it's because we are
220 # overriding an existing class with one from a later kickstart
221 # version, so remove the old one in favor of the new one.
222 if obj
.__class
__.__name
__ > list[i
].__class
__.__name
__:
224 elif obj
.__class
__.__name
__ == list[i
].__class
__.__name
__:
227 elif obj
.__class
__.__name
__ < list[i
].__class
__.__name
__:
235 def _setCommand(self
, cmdObj
):
236 # Add an attribute on this version object. We need this to provide a
237 # way for clients to access the command objects. We also need to strip
238 # off the version part from the front of the name.
239 if cmdObj
.__class
__.__name
__.find("_") != -1:
240 name
= cmdObj
.__class
__.__name
__.split("_", 1)[1]
242 name
= cmdObj
.__class
__.__name
__.lower()
244 setattr(self
, name
.lower(), cmdObj
)
246 # Also, add the object into the _writeOrder dict in the right place.
247 if cmdObj
.writePriority
is not None:
248 if self
._writeOrder
.has_key(cmdObj
.writePriority
):
249 self
._insertSorted
(self
._writeOrder
[cmdObj
.writePriority
], cmdObj
)
251 self
._writeOrder
[cmdObj
.writePriority
] = [cmdObj
]
253 def _registerCommands(self
, mapping
={}):
255 from pykickstart
.handlers
.control
import commandMap
256 mapping
= commandMap
[self
.version
]
258 for (cmdName
, cmdClass
) in mapping
.iteritems():
259 # First make sure we haven't instantiated this command handler
260 # already. If we have, we just need to make another mapping to
261 # it in self.commands.
264 for (key
, val
) in self
.commands
.iteritems():
265 if val
.__class
__.__name
__ == cmdClass
.__name
__:
269 # If we didn't find an instance in self.commands, create one now.
272 self
._setCommand
(cmdObj
)
274 # Finally, add the mapping to the commands dict.
275 self
.commands
[cmdName
] = cmdObj
277 def dispatcher(self
, cmd
, cmdArgs
, lineno
):
278 """Given the command string cmd and the list of arguments cmdArgs, call
279 the appropriate KickstartCommand handler that has been previously
280 registered. lineno is needed for error reporting. If cmd does not
281 exist in the commands dict, KickstartParseError will be raised. A
282 handler of None for the given command is not an error.
284 if not self
.commands
.has_key(cmd
):
285 raise KickstartParseError
, formatErrorMsg(lineno
, msg
=_("Unknown command: %s" % cmd
))
287 if self
.commands
[cmd
] != None:
288 self
.commands
[cmd
].currentCmd
= cmd
289 self
.commands
[cmd
].handler
= self
290 self
.commands
[cmd
].lineno
= lineno
291 self
.commands
[cmd
].parse(cmdArgs
)
293 def maskAllExcept(self
, lst
):
294 """Set all entries in the commands dict to None, except the ones in
295 the lst. All other commands will not be processed.
297 self
._writeOrder
= {}
299 for (key
, val
) in self
.commands
.iteritems():
301 self
.commands
[key
] = None
303 def hasCommand(self
, cmd
):
304 """Return true if there is a handler for the string cmd."""
305 return hasattr(self
, cmd
)
311 """The base class for all data objects. This is an abstract class."""
313 """Create a new BaseData instance. There are no attributes."""
315 # We don't want people using this class by itself.
316 if self
.__class
__ is BaseData
:
317 raise TypeError, "BaseData is an abstract class."
320 """Return a string formatted for output to a kickstart file."""
323 def __call__(self
, *args
, **kwargs
):
324 """Set multiple attributes on a subclass of BaseData at once via
325 keyword arguments. Valid attributes are anything specified in a
326 subclass, but unknown attributes will be ignored.
328 for (key
, val
) in kwargs
.items():
329 if hasattr(self
, key
):
330 setattr(self
, key
, val
)