3 # Chris Lumens <clumens@redhat.com>
5 # Copyright 2013 Red Hat, Inc.
7 # This copyrighted material is made available to anyone wishing to use, modify,
8 # copy, or redistribute it subject to the terms and conditions of the GNU
9 # General Public License v.2. This program is distributed in the hope that it
10 # will be useful, but WITHOUT ANY WARRANTY expressed or implied, including the
11 # implied warranties of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
12 # See the GNU General Public License for more details.
14 # You should have received a copy of the GNU General Public License along with
15 # this program; if not, write to the Free Software Foundation, Inc., 51
16 # Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. Any Red Hat
17 # trademarks that are incorporated in the source code or documentation are not
18 # subject to the GNU General Public License and may only be used or replicated
19 # with the express permission of Red Hat, Inc.
21 # This script takes as input an optional input kickstart file and an optional
22 # kickstart syntax version (the latest is assumed, if none specified). It
23 # then provides an interactive shell for the user to manipulate the state of
24 # the input kickstart file and either prints the final results on stdout or to
25 # a designated output file when the program quits.
28 # - error reporting always says you are on line 1
29 # - handle sections like %packages
30 # - some meta-commands might be nice:
31 # - .delete (a previous line)
32 # - .help (requires moving help text into each optionparser object?)
36 from optparse
import OptionParser
39 from pykickstart
.errors
import KickstartError
, KickstartVersionError
40 from pykickstart
.parser
import KickstartParser
, preprocessKickstart
41 from pykickstart
.version
import DEVEL
, makeVersion
44 gettext
.textdomain("pykickstart")
45 _
= lambda x
: gettext
.ldgettext("pykickstart", x
)
49 ## These are commands that control operation of the kickstart shell and are
50 ## handled by this program. They are not recognized kickstart commands.
53 class InternalCommand(object):
55 self
.op
= OptionParser()
57 def execute(self
, parser
):
60 class ClearCommand(InternalCommand
):
61 def execute(self
, parser
):
62 version
= parser
.version
63 parser
.handler
= makeVersion(version
)
65 class QuitCommand(InternalCommand
):
66 def execute(self
, parser
):
69 class ShowCommand(InternalCommand
):
70 def execute(self
, parser
):
77 class KickstartCompleter(object):
78 def __init__(self
, handler
, internalCommands
):
79 # Build up a dict of kickstart commands and their valid options:
80 # { command_name: [options] }
83 for (cStr
, cObj
) in handler
.commands
.iteritems():
84 self
._add
_command
(cStr
, cObj
)
86 for (cStr
, cObj
) in internalCommands
.iteritems():
87 self
._add
_command
(cStr
, cObj
)
89 self
.currentCandidates
= []
91 def _add_command(self
, cStr
, cObj
):
92 self
.commands
[cStr
] = []
94 # Simple commands do not have any optparse crud.
95 if not hasattr(cObj
, "op"):
98 for opt
in cObj
.op
.option_list
:
99 self
.commands
[cStr
] += opt
._short
_opts
+ opt
._long
_opts
101 def complete(self
, text
, state
):
104 # This is the first time Tab has been pressed, so build up a list of matches.
106 origline
= readline
.get_line_buffer()
107 begin
= readline
.get_begidx()
108 end
= readline
.get_endidx()
110 beingCompleted
= origline
[begin
:end
]
111 words
= origline
.split()
114 # Everything's a match for an empty string.
115 self
.currentCandidates
= sorted(self
.commands
.keys())
118 # Ignore leading whitespace when trying to figure out
119 # completions for a kickstart command.
120 if begin
== 0 or origline
[:begin
].strip() == "":
122 candidates
= self
.commands
.keys()
125 candidates
= self
.commands
[words
[0]]
128 self
.currentCandidates
= [w
for w
in candidates
if w
.startswith(beingCompleted
)]
130 self
.currentCandidates
= candidates
131 except (KeyError, IndexError) as e
:
132 self
.currentCandidates
= []
135 response
= self
.currentCandidates
[state
]
145 op
= OptionParser(usage
="usage: %prog [options]")
146 op
.add_option("-i", "--input", dest
="input",
147 help=_("a basis file to use for seeding the kickstart data (optional)"))
148 op
.add_option("-o", "--output", dest
="output",
149 help=_("the location to write the finished kickstart file, or stdout if not given"))
150 op
.add_option("-v", "--version", dest
="version", default
=DEVEL
,
151 help=_("version of kickstart syntax to validate against"))
153 (opts
, extra
) = op
.parse_args(sys
.argv
[1:])
159 ## SETTING UP PYKICKSTART
163 handler
= makeVersion(opts
.version
)
164 except KickstartVersionError
:
165 print(_("The version %s is not supported by pykickstart") % opts
.version
)
168 ksparser
= KickstartParser(handler
, followIncludes
=True, errorsAreFatal
=False)
172 processedFile
= preprocessKickstart(opts
.input)
173 ksparser
.readKickstart(processedFile
)
174 os
.remove(processedFile
)
175 except KickstartError
as e
:
176 # Errors should just dump you to the prompt anyway.
177 print(_("Warning: The following error occurred when processing the input file:\n%s\n") % e
)
179 internalCommands
= {".clear": ClearCommand(),
180 ".show": ShowCommand(),
181 ".quit": QuitCommand()}
184 ## SETTING UP READLINE
187 readline
.parse_and_bind("tab: complete")
188 readline
.set_completer(KickstartCompleter(handler
, internalCommands
).complete
)
190 # Since everything in kickstart looks like a command line arg, we need to
191 # remove '-' from the delimiter string.
192 delims
= readline
.get_completer_delims()
193 readline
.set_completer_delims(delims
.replace('-', ''))
199 print("Press ^D to exit.")
203 line
= raw_input("ks> ")
205 # ^D was hit, time to quit.
207 except KeyboardInterrupt:
208 # ^C was hit, time to quit. Don't be like other programs.
211 # All internal commands start with a ., so if that's the beginning of the
212 # line, we need to dispatch ourselves.
213 if line
.startswith("."):
215 if words
[0] in internalCommands
:
217 internalCommands
[words
[0]].execute(ksparser
)
219 # ".quit" was typed, time to quit.
222 print(_("Internal command %s not recognized.") % words
[0])
226 # Now process the line of input as if it were a kickstart file - just an
227 # extremely short one.
229 ksparser
.readKickstartFromString(line
)
230 except KickstartError
as e
:
233 # And finally, print the output kickstart file.
235 with
open(opts
.output
, "w") as fd
:
236 fd
.write(str(ksparser
.handler
))
238 print("\n" + str(ksparser
.handler
))