Add new RHEL7 logvol objects to master
[pykickstart.git] / tools / ksshell
blob3de34c07fa1394c429c589e2bc630a2cf80cbead
1 #!/usr/bin/python
3 # Chris Lumens <clumens@redhat.com>
5 # Copyright 2013-2014 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.
27 # TODO:
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?)
33 # - .save
35 import readline
36 import argparse
37 import os, sys
39 from pykickstart.errors import KickstartError, KickstartVersionError
40 from pykickstart.parser import KickstartParser, preprocessKickstart
41 from pykickstart.version import DEVEL, makeVersion
43 import gettext
44 gettext.textdomain("pykickstart")
45 _ = lambda x: gettext.ldgettext("pykickstart", x)
48 ## INTERNAL COMMANDS
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):
54 def __init__(self):
55 self.op = argparse.ArgumentParser()
57 def execute(self, parser):
58 pass
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):
67 raise EOFError
69 class ShowCommand(InternalCommand):
70 def execute(self, parser):
71 print(parser.handler)
74 ## COMMAND COMPLETION
77 class KickstartCompleter(object):
78 def __init__(self, handler, internal):
79 # Build up a dict of kickstart commands and their valid options:
80 # { command_name: [options] }
81 self.commands = {}
83 for (cStr, cObj) in handler.commands.iteritems():
84 self._add_command(cStr, cObj)
86 for (cStr, cObj) in internal.iteritems():
87 self._add_command(cStr, cObj)
89 self.currentCandidates = []
91 def _add_command(self, cStr, cObj):
92 self.commands[cStr] = []
94 # Internal and simple commands don't have any optparse crud.
95 if cStr.startswith(".") or not hasattr(cObj, "op"):
96 return
98 for opt in cObj.op.option_list:
99 self.commands[cStr] += opt._short_opts + opt._long_opts
101 def complete(self, _text, state):
102 response = None
104 # This is the first time Tab has been pressed, so build up a list of matches.
105 if state == 0:
106 origline = readline.get_line_buffer()
107 begin = readline.get_begidx()
108 end = readline.get_endidx()
110 beingCompleted = origline[begin:end]
111 parts = origline.split()
113 if not parts:
114 # Everything's a match for an empty string.
115 self.currentCandidates = sorted(self.commands.keys())
116 else:
117 try:
118 # Ignore leading whitespace when trying to figure out
119 # completions for a kickstart command.
120 if begin == 0 or origline[:begin].strip() == "":
121 # first word
122 candidates = self.commands.keys()
123 else:
124 # later word
125 candidates = self.commands[parts[0]]
127 if beingCompleted:
128 self.currentCandidates = [w for w in candidates if w.startswith(beingCompleted)]
129 else:
130 self.currentCandidates = candidates
131 except (KeyError, IndexError):
132 self.currentCandidates = []
134 try:
135 response = self.currentCandidates[state]
136 except IndexError:
137 response = None
139 return response
142 ## OPTION PROCESSING
145 op = argparse.ArgumentParser()
146 op.add_argument("-i", "--input", dest="input",
147 help=_("a basis file to use for seeding the kickstart data (optional)"))
148 op.add_argument("-o", "--output", dest="output",
149 help=_("the location to write the finished kickstart file, or stdout if not given"))
150 op.add_argument("-v", "--version", dest="version", default=DEVEL,
151 help=_("version of kickstart syntax to validate against"))
153 opts = op.parse_args(sys.argv[1:])
156 ## SETTING UP PYKICKSTART
159 try:
160 kshandler = makeVersion(opts.version)
161 except KickstartVersionError:
162 print(_("The version %s is not supported by pykickstart") % opts.version)
163 sys.exit(1)
165 ksparser = KickstartParser(kshandler, followIncludes=True, errorsAreFatal=False)
167 if opts.input:
168 try:
169 processedFile = preprocessKickstart(opts.input)
170 ksparser.readKickstart(processedFile)
171 os.remove(processedFile)
172 except KickstartError as e:
173 # Errors should just dump you to the prompt anyway.
174 print(_("Warning: The following error occurred when processing the input file:\n%s\n") % e)
176 internalCommands = {".clear": ClearCommand(),
177 ".show": ShowCommand(),
178 ".quit": QuitCommand()}
181 ## SETTING UP READLINE
184 readline.parse_and_bind("tab: complete")
185 readline.set_completer(KickstartCompleter(kshandler, internalCommands).complete)
187 # Since everything in kickstart looks like a command line arg, we need to
188 # remove '-' from the delimiter string.
189 delims = readline.get_completer_delims()
190 readline.set_completer_delims(delims.replace('-', ''))
193 ## REPL
196 print("Press ^D to exit.")
198 while True:
199 try:
200 line = raw_input("ks> ")
201 except EOFError:
202 # ^D was hit, time to quit.
203 break
204 except KeyboardInterrupt:
205 # ^C was hit, time to quit. Don't be like other programs.
206 break
208 # All internal commands start with a ., so if that's the beginning of the
209 # line, we need to dispatch ourselves.
210 if line.startswith("."):
211 words = line.split()
212 if words[0] in internalCommands:
213 try:
214 internalCommands[words[0]].execute(ksparser)
215 except EOFError:
216 # ".quit" was typed, time to quit.
217 break
218 else:
219 print(_("Internal command %s not recognized.") % words[0])
221 continue
223 # Now process the line of input as if it were a kickstart file - just an
224 # extremely short one.
225 try:
226 ksparser.readKickstartFromString(line)
227 except KickstartError as e:
228 print(e)
230 # And finally, print the output kickstart file.
231 if opts.output:
232 with open(opts.output, "w") as fd:
233 fd.write(str(ksparser.handler))
234 else:
235 print("\n" + str(ksparser.handler))