Statistics
| Branch: | Tag: | Revision:

root / tools / cfgshell @ 8b3fd458

History | View | Annotate | Download (8.9 kB)

1 a8083063 Iustin Pop
#!/usr/bin/python
2 a8083063 Iustin Pop
#
3 a8083063 Iustin Pop
4 a8083063 Iustin Pop
# Copyright (C) 2006, 2007 Google Inc.
5 a8083063 Iustin Pop
#
6 a8083063 Iustin Pop
# This program is free software; you can redistribute it and/or modify
7 a8083063 Iustin Pop
# it under the terms of the GNU General Public License as published by
8 a8083063 Iustin Pop
# the Free Software Foundation; either version 2 of the License, or
9 a8083063 Iustin Pop
# (at your option) any later version.
10 a8083063 Iustin Pop
#
11 a8083063 Iustin Pop
# This program is distributed in the hope that it will be useful, but
12 a8083063 Iustin Pop
# WITHOUT ANY WARRANTY; without even the implied warranty of
13 a8083063 Iustin Pop
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 a8083063 Iustin Pop
# General Public License for more details.
15 a8083063 Iustin Pop
#
16 a8083063 Iustin Pop
# You should have received a copy of the GNU General Public License
17 a8083063 Iustin Pop
# along with this program; if not, write to the Free Software
18 a8083063 Iustin Pop
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19 a8083063 Iustin Pop
# 02110-1301, USA.
20 a8083063 Iustin Pop
21 a8083063 Iustin Pop
22 a8083063 Iustin Pop
"""Tool to do manual changes to the config file.
23 a8083063 Iustin Pop
24 a8083063 Iustin Pop
"""
25 a8083063 Iustin Pop
26 3ecf6786 Iustin Pop
# functions in this module need to have a given name structure, so:
27 3ecf6786 Iustin Pop
# pylint: disable-msg=C0103
28 3ecf6786 Iustin Pop
29 a8083063 Iustin Pop
30 a8083063 Iustin Pop
import optparse
31 a8083063 Iustin Pop
import cmd
32 a8083063 Iustin Pop
33 a8083063 Iustin Pop
try:
34 a8083063 Iustin Pop
  import readline
35 a8083063 Iustin Pop
  _wd = readline.get_completer_delims()
36 a8083063 Iustin Pop
  _wd = _wd.replace("-", "")
37 a8083063 Iustin Pop
  readline.set_completer_delims(_wd)
38 a8083063 Iustin Pop
  del _wd
39 a8083063 Iustin Pop
except ImportError:
40 a8083063 Iustin Pop
  pass
41 a8083063 Iustin Pop
42 a8083063 Iustin Pop
from ganeti import errors
43 a8083063 Iustin Pop
from ganeti import config
44 a8083063 Iustin Pop
from ganeti import objects
45 a8083063 Iustin Pop
46 a8083063 Iustin Pop
47 a8083063 Iustin Pop
class ConfigShell(cmd.Cmd):
48 a8083063 Iustin Pop
  """Command tool for editing the config file.
49 a8083063 Iustin Pop
50 a8083063 Iustin Pop
  Note that although we don't do saves after remove, the current
51 a8083063 Iustin Pop
  ConfigWriter code does that; so we can't prevent someone from
52 a8083063 Iustin Pop
  actually breaking the config with this tool. It's the users'
53 a8083063 Iustin Pop
  responsibility to know what they're doing.
54 a8083063 Iustin Pop
55 a8083063 Iustin Pop
  """
56 a8083063 Iustin Pop
  prompt = "(/) "
57 a8083063 Iustin Pop
58 a8083063 Iustin Pop
  def __init__(self, cfg_file=None):
59 a8083063 Iustin Pop
    """Constructor for the ConfigShell object.
60 a8083063 Iustin Pop
61 a8083063 Iustin Pop
    The optional cfg_file argument will be used to load a config file
62 a8083063 Iustin Pop
    at startup.
63 a8083063 Iustin Pop
64 a8083063 Iustin Pop
    """
65 a8083063 Iustin Pop
    cmd.Cmd.__init__(self)
66 5fcdc80d Iustin Pop
    self.cfg = None
67 a8083063 Iustin Pop
    self.parents = []
68 a8083063 Iustin Pop
    self.path = []
69 a8083063 Iustin Pop
    if cfg_file:
70 a8083063 Iustin Pop
      self.do_load(cfg_file)
71 a8083063 Iustin Pop
      self.postcmd(False, "")
72 a8083063 Iustin Pop
73 a8083063 Iustin Pop
  def emptyline(self):
74 a8083063 Iustin Pop
    """Empty line handling.
75 a8083063 Iustin Pop
76 a8083063 Iustin Pop
    Note that the default will re-run the last command. We don't want
77 a8083063 Iustin Pop
    that, and just ignore the empty line.
78 a8083063 Iustin Pop
79 a8083063 Iustin Pop
    """
80 a8083063 Iustin Pop
    return False
81 a8083063 Iustin Pop
82 a8083063 Iustin Pop
  @staticmethod
83 a8083063 Iustin Pop
  def _get_entries(obj):
84 a8083063 Iustin Pop
    """Computes the list of subdirs and files in the given object.
85 a8083063 Iustin Pop
86 a8083063 Iustin Pop
    This, depending on the passed object entry, look at each logical
87 a8083063 Iustin Pop
    child of the object and decides if it's a container or a simple
88 a8083063 Iustin Pop
    object. Based on this, it computes the list of subdir and files.
89 a8083063 Iustin Pop
90 a8083063 Iustin Pop
    """
91 a8083063 Iustin Pop
    dirs = []
92 a8083063 Iustin Pop
    entries = []
93 a8083063 Iustin Pop
    if isinstance(obj, objects.ConfigObject):
94 a8083063 Iustin Pop
      for name in obj.__slots__:
95 a8083063 Iustin Pop
        child = getattr(obj, name, None)
96 a8083063 Iustin Pop
        if isinstance(child, (list, dict, tuple, objects.ConfigObject)):
97 a8083063 Iustin Pop
          dirs.append(name)
98 a8083063 Iustin Pop
        else:
99 a8083063 Iustin Pop
          entries.append(name)
100 a8083063 Iustin Pop
    elif isinstance(obj, (list, tuple)):
101 a8083063 Iustin Pop
      for idx, child in enumerate(obj):
102 a8083063 Iustin Pop
        if isinstance(child, (list, dict, tuple, objects.ConfigObject)):
103 a8083063 Iustin Pop
          dirs.append(str(idx))
104 a8083063 Iustin Pop
        else:
105 a8083063 Iustin Pop
          entries.append(str(idx))
106 a8083063 Iustin Pop
    elif isinstance(obj, dict):
107 a8083063 Iustin Pop
      dirs = obj.keys()
108 a8083063 Iustin Pop
109 a8083063 Iustin Pop
    return dirs, entries
110 a8083063 Iustin Pop
111 a8083063 Iustin Pop
  def precmd(self, line):
112 a8083063 Iustin Pop
    """Precmd hook to prevent commands in invalid states.
113 a8083063 Iustin Pop
114 a8083063 Iustin Pop
    This will prevent everything except load and quit when no
115 a8083063 Iustin Pop
    configuration is loaded.
116 a8083063 Iustin Pop
117 a8083063 Iustin Pop
    """
118 a8083063 Iustin Pop
    if line.startswith("load") or line == 'EOF' or line == "quit":
119 a8083063 Iustin Pop
      return line
120 a8083063 Iustin Pop
    if not self.parents or self.cfg is None:
121 a8083063 Iustin Pop
      print "No config data loaded"
122 a8083063 Iustin Pop
      return ""
123 a8083063 Iustin Pop
    return line
124 a8083063 Iustin Pop
125 a8083063 Iustin Pop
  def postcmd(self, stop, line):
126 a8083063 Iustin Pop
    """Postcmd hook to update the prompt.
127 a8083063 Iustin Pop
128 a8083063 Iustin Pop
    We show the current location in the prompt and this function is
129 a8083063 Iustin Pop
    used to update it; this is only needed after cd and load, but we
130 a8083063 Iustin Pop
    update it anyway.
131 a8083063 Iustin Pop
132 a8083063 Iustin Pop
    """
133 a8083063 Iustin Pop
    if self.cfg is None:
134 a8083063 Iustin Pop
      self.prompt = "(#no config) "
135 a8083063 Iustin Pop
    else:
136 5fcdc80d Iustin Pop
      self.prompt = "(/%s) " % ("/".join(self.path),)
137 a8083063 Iustin Pop
    return stop
138 a8083063 Iustin Pop
139 a8083063 Iustin Pop
  def do_load(self, line):
140 a8083063 Iustin Pop
    """Load function.
141 a8083063 Iustin Pop
142 a8083063 Iustin Pop
    Syntax: load [/path/to/config/file]
143 a8083063 Iustin Pop
144 a8083063 Iustin Pop
    This will load a new configuration, discarding any existing data
145 a8083063 Iustin Pop
    (if any). If no argument has been passed, it will use the default
146 a8083063 Iustin Pop
    config file location.
147 a8083063 Iustin Pop
148 a8083063 Iustin Pop
    """
149 a8083063 Iustin Pop
    if line:
150 a8083063 Iustin Pop
      arg = line
151 a8083063 Iustin Pop
    else:
152 a8083063 Iustin Pop
      arg = None
153 a8083063 Iustin Pop
    try:
154 a8083063 Iustin Pop
      self.cfg = config.ConfigWriter(cfg_file=arg, offline=True)
155 a8083063 Iustin Pop
      self.parents = [self.cfg._config_data]
156 a8083063 Iustin Pop
      self.path = []
157 a8083063 Iustin Pop
    except errors.ConfigurationError, err:
158 a8083063 Iustin Pop
      print "Error: %s" % str(err)
159 a8083063 Iustin Pop
    return False
160 a8083063 Iustin Pop
161 a8083063 Iustin Pop
  def do_ls(self, line):
162 a8083063 Iustin Pop
    """List the current entry.
163 a8083063 Iustin Pop
164 a8083063 Iustin Pop
    This will show directories with a slash appended and files
165 a8083063 Iustin Pop
    normally.
166 a8083063 Iustin Pop
167 a8083063 Iustin Pop
    """
168 a8083063 Iustin Pop
    dirs, entries = self._get_entries(self.parents[-1])
169 a8083063 Iustin Pop
    for i in dirs:
170 a8083063 Iustin Pop
      print i + "/"
171 a8083063 Iustin Pop
    for i in entries:
172 a8083063 Iustin Pop
      print i
173 a8083063 Iustin Pop
    return False
174 a8083063 Iustin Pop
175 a8083063 Iustin Pop
  def complete_cd(self, text, line, begidx, endidx):
176 a8083063 Iustin Pop
    """Completion function for the cd command.
177 a8083063 Iustin Pop
178 a8083063 Iustin Pop
    """
179 a8083063 Iustin Pop
    pointer = self.parents[-1]
180 a8083063 Iustin Pop
    dirs, entries = self._get_entries(pointer)
181 a8083063 Iustin Pop
    matches = [str(name) for name in dirs if name.startswith(text)]
182 a8083063 Iustin Pop
    return matches
183 a8083063 Iustin Pop
184 a8083063 Iustin Pop
  def do_cd(self, line):
185 a8083063 Iustin Pop
    """Changes the current path.
186 a8083063 Iustin Pop
187 033b7b64 Michael Hanselmann
    Valid arguments: either .., /, "" (no argument) or a child of the current
188 033b7b64 Michael Hanselmann
    object.
189 a8083063 Iustin Pop
190 a8083063 Iustin Pop
    """
191 a8083063 Iustin Pop
    if line == "..":
192 a8083063 Iustin Pop
      if self.path:
193 a8083063 Iustin Pop
        self.path.pop()
194 a8083063 Iustin Pop
        self.parents.pop()
195 a8083063 Iustin Pop
        return False
196 a8083063 Iustin Pop
      else:
197 a8083063 Iustin Pop
        print "Already at top level"
198 a8083063 Iustin Pop
        return False
199 033b7b64 Michael Hanselmann
    elif len(line) == 0 or line == "/":
200 033b7b64 Michael Hanselmann
      self.parents = self.parents[0:1]
201 033b7b64 Michael Hanselmann
      self.path = []
202 033b7b64 Michael Hanselmann
      return False
203 a8083063 Iustin Pop
204 a8083063 Iustin Pop
    pointer = self.parents[-1]
205 a8083063 Iustin Pop
    dirs, entries = self._get_entries(pointer)
206 a8083063 Iustin Pop
207 a8083063 Iustin Pop
    if line not in dirs:
208 a8083063 Iustin Pop
      print "No such child"
209 a8083063 Iustin Pop
      return False
210 a8083063 Iustin Pop
    if isinstance(pointer, (dict, list, tuple)):
211 a8083063 Iustin Pop
      if isinstance(pointer, (list, tuple)):
212 a8083063 Iustin Pop
        line = int(line)
213 a8083063 Iustin Pop
      new_obj = pointer[line]
214 a8083063 Iustin Pop
    else:
215 a8083063 Iustin Pop
      new_obj = getattr(pointer, line)
216 a8083063 Iustin Pop
    self.parents.append(new_obj)
217 a8083063 Iustin Pop
    self.path.append(str(line))
218 a8083063 Iustin Pop
    return False
219 a8083063 Iustin Pop
220 a8083063 Iustin Pop
  def do_pwd(self, line):
221 a8083063 Iustin Pop
    """Shows the current path.
222 a8083063 Iustin Pop
223 a8083063 Iustin Pop
    This duplicates the prompt functionality, but it's reasonable to
224 a8083063 Iustin Pop
    have.
225 a8083063 Iustin Pop
226 a8083063 Iustin Pop
    """
227 a8083063 Iustin Pop
    print "/" + "/".join(self.path)
228 a8083063 Iustin Pop
    return False
229 a8083063 Iustin Pop
230 a8083063 Iustin Pop
  def complete_cat(self, text, line, begidx, endidx):
231 a8083063 Iustin Pop
    """Completion for the cat command.
232 a8083063 Iustin Pop
233 a8083063 Iustin Pop
    """
234 a8083063 Iustin Pop
    pointer = self.parents[-1]
235 a8083063 Iustin Pop
    dirs, entries = self._get_entries(pointer)
236 a8083063 Iustin Pop
    matches = [name for name in entries if name.startswith(text)]
237 a8083063 Iustin Pop
    return matches
238 a8083063 Iustin Pop
239 a8083063 Iustin Pop
  def do_cat(self, line):
240 a8083063 Iustin Pop
    """Shows the contents of the given file.
241 a8083063 Iustin Pop
242 a8083063 Iustin Pop
    This will display the contents of the given file, which must be a
243 a8083063 Iustin Pop
    child of the current path (as shows by `ls`).
244 a8083063 Iustin Pop
245 a8083063 Iustin Pop
    """
246 a8083063 Iustin Pop
    pointer = self.parents[-1]
247 a8083063 Iustin Pop
    dirs, entries = self._get_entries(pointer)
248 a8083063 Iustin Pop
    if line not in entries:
249 a8083063 Iustin Pop
      print "No such entry"
250 a8083063 Iustin Pop
      return False
251 a8083063 Iustin Pop
252 a8083063 Iustin Pop
    if isinstance(pointer, (dict, list, tuple)):
253 a8083063 Iustin Pop
      if isinstance(pointer, (list, tuple)):
254 a8083063 Iustin Pop
        line = int(line)
255 a8083063 Iustin Pop
      val = pointer[line]
256 a8083063 Iustin Pop
    else:
257 a8083063 Iustin Pop
      val = getattr(pointer, line)
258 a8083063 Iustin Pop
    print val
259 a8083063 Iustin Pop
    return False
260 a8083063 Iustin Pop
261 a8083063 Iustin Pop
  def do_verify(self, line):
262 a8083063 Iustin Pop
    """Verify the configuration.
263 a8083063 Iustin Pop
264 a8083063 Iustin Pop
    This verifies the contents of the configuration file (and not the
265 a8083063 Iustin Pop
    in-memory data, as every modify operation automatically saves the
266 a8083063 Iustin Pop
    file).
267 a8083063 Iustin Pop
268 a8083063 Iustin Pop
    """
269 a8083063 Iustin Pop
    vdata = self.cfg.VerifyConfig()
270 a8083063 Iustin Pop
    if vdata:
271 a8083063 Iustin Pop
      print "Validation failed. Errors:"
272 a8083063 Iustin Pop
      for text in vdata:
273 a8083063 Iustin Pop
        print text
274 a8083063 Iustin Pop
    return False
275 a8083063 Iustin Pop
276 a8083063 Iustin Pop
  def do_save(self, line):
277 a8083063 Iustin Pop
    """Saves the configuration data.
278 a8083063 Iustin Pop
279 a8083063 Iustin Pop
    Note that is redundant (all modify operations automatically save
280 a8083063 Iustin Pop
    the data), but it is good to use it as in the future that could
281 a8083063 Iustin Pop
    change.
282 a8083063 Iustin Pop
283 a8083063 Iustin Pop
    """
284 a8083063 Iustin Pop
    if self.cfg.VerifyConfig():
285 a8083063 Iustin Pop
      print "Config data does not validate, refusing to save."
286 a8083063 Iustin Pop
      return False
287 a8083063 Iustin Pop
    self.cfg._WriteConfig()
288 a8083063 Iustin Pop
289 a8083063 Iustin Pop
  def do_rm(self, line):
290 a8083063 Iustin Pop
    """Removes an instance or a node.
291 a8083063 Iustin Pop
292 a8083063 Iustin Pop
    This function works only on instances or nodes. You must be in
293 a8083063 Iustin Pop
    either `/nodes` or `/instances` and give a valid argument.
294 a8083063 Iustin Pop
295 a8083063 Iustin Pop
    """
296 a8083063 Iustin Pop
    pointer = self.parents[-1]
297 a8083063 Iustin Pop
    data = self.cfg._config_data
298 a8083063 Iustin Pop
    if pointer not in (data.instances, data.nodes):
299 a8083063 Iustin Pop
      print "Can only delete instances and nodes"
300 a8083063 Iustin Pop
      return False
301 a8083063 Iustin Pop
    if pointer == data.instances:
302 a8083063 Iustin Pop
      if line in data.instances:
303 a8083063 Iustin Pop
        self.cfg.RemoveInstance(line)
304 a8083063 Iustin Pop
      else:
305 a8083063 Iustin Pop
        print "Invalid instance name"
306 a8083063 Iustin Pop
    else:
307 a8083063 Iustin Pop
      if line in data.nodes:
308 a8083063 Iustin Pop
        self.cfg.RemoveNode(line)
309 a8083063 Iustin Pop
      else:
310 a8083063 Iustin Pop
        print "Invalid node name"
311 a8083063 Iustin Pop
312 a8083063 Iustin Pop
  def do_EOF(self, line):
313 3ecf6786 Iustin Pop
    """Exit the application.
314 3ecf6786 Iustin Pop
315 3ecf6786 Iustin Pop
    """
316 a8083063 Iustin Pop
    print
317 a8083063 Iustin Pop
    return True
318 a8083063 Iustin Pop
319 a8083063 Iustin Pop
  def do_quit(self, line):
320 a8083063 Iustin Pop
    """Exit the application.
321 a8083063 Iustin Pop
322 a8083063 Iustin Pop
    """
323 a8083063 Iustin Pop
    print
324 a8083063 Iustin Pop
    return True
325 a8083063 Iustin Pop
326 033b7b64 Michael Hanselmann
327 a8083063 Iustin Pop
class Error(Exception):
328 a8083063 Iustin Pop
  """Generic exception"""
329 a8083063 Iustin Pop
  pass
330 a8083063 Iustin Pop
331 a8083063 Iustin Pop
332 a8083063 Iustin Pop
def ParseOptions():
333 a8083063 Iustin Pop
  """Parses the command line options.
334 a8083063 Iustin Pop
335 a8083063 Iustin Pop
  In case of command line errors, it will show the usage and exit the
336 a8083063 Iustin Pop
  program.
337 a8083063 Iustin Pop
338 a8083063 Iustin Pop
  Returns:
339 a8083063 Iustin Pop
    (options, args), as returned by OptionParser.parse_args
340 a8083063 Iustin Pop
  """
341 a8083063 Iustin Pop
342 a8083063 Iustin Pop
  parser = optparse.OptionParser()
343 a8083063 Iustin Pop
344 a8083063 Iustin Pop
  options, args = parser.parse_args()
345 a8083063 Iustin Pop
346 a8083063 Iustin Pop
  return options, args
347 a8083063 Iustin Pop
348 a8083063 Iustin Pop
349 a8083063 Iustin Pop
def main():
350 a8083063 Iustin Pop
  """Application entry point.
351 a8083063 Iustin Pop
352 a8083063 Iustin Pop
  This is just a wrapper over BootStrap, to handle our own exceptions.
353 a8083063 Iustin Pop
  """
354 a8083063 Iustin Pop
  options, args = ParseOptions()
355 a8083063 Iustin Pop
  if args:
356 a8083063 Iustin Pop
    cfg_file = args[0]
357 a8083063 Iustin Pop
  else:
358 a8083063 Iustin Pop
    cfg_file = None
359 a8083063 Iustin Pop
  shell = ConfigShell(cfg_file=cfg_file)
360 a8083063 Iustin Pop
  shell.cmdloop()
361 a8083063 Iustin Pop
362 a8083063 Iustin Pop
363 a8083063 Iustin Pop
if __name__ == "__main__":
364 a8083063 Iustin Pop
  main()