Statistics
| Branch: | Tag: | Revision:

root / tools / cfgshell @ a4af651e

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.cfg._OpenConfig()
156 a8083063 Iustin Pop
      self.parents = [self.cfg._config_data]
157 a8083063 Iustin Pop
      self.path = []
158 a8083063 Iustin Pop
    except errors.ConfigurationError, err:
159 a8083063 Iustin Pop
      print "Error: %s" % str(err)
160 a8083063 Iustin Pop
    return False
161 a8083063 Iustin Pop
162 a8083063 Iustin Pop
  def do_ls(self, line):
163 a8083063 Iustin Pop
    """List the current entry.
164 a8083063 Iustin Pop
165 a8083063 Iustin Pop
    This will show directories with a slash appended and files
166 a8083063 Iustin Pop
    normally.
167 a8083063 Iustin Pop
168 a8083063 Iustin Pop
    """
169 a8083063 Iustin Pop
    dirs, entries = self._get_entries(self.parents[-1])
170 a8083063 Iustin Pop
    for i in dirs:
171 a8083063 Iustin Pop
      print i + "/"
172 a8083063 Iustin Pop
    for i in entries:
173 a8083063 Iustin Pop
      print i
174 a8083063 Iustin Pop
    return False
175 a8083063 Iustin Pop
176 a8083063 Iustin Pop
  def complete_cd(self, text, line, begidx, endidx):
177 a8083063 Iustin Pop
    """Completion function for the cd command.
178 a8083063 Iustin Pop
179 a8083063 Iustin Pop
    """
180 a8083063 Iustin Pop
    pointer = self.parents[-1]
181 a8083063 Iustin Pop
    dirs, entries = self._get_entries(pointer)
182 a8083063 Iustin Pop
    matches = [str(name) for name in dirs if name.startswith(text)]
183 a8083063 Iustin Pop
    return matches
184 a8083063 Iustin Pop
185 a8083063 Iustin Pop
  def do_cd(self, line):
186 a8083063 Iustin Pop
    """Changes the current path.
187 a8083063 Iustin Pop
188 033b7b64 Michael Hanselmann
    Valid arguments: either .., /, "" (no argument) or a child of the current
189 033b7b64 Michael Hanselmann
    object.
190 a8083063 Iustin Pop
191 a8083063 Iustin Pop
    """
192 a8083063 Iustin Pop
    if line == "..":
193 a8083063 Iustin Pop
      if self.path:
194 a8083063 Iustin Pop
        self.path.pop()
195 a8083063 Iustin Pop
        self.parents.pop()
196 a8083063 Iustin Pop
        return False
197 a8083063 Iustin Pop
      else:
198 a8083063 Iustin Pop
        print "Already at top level"
199 a8083063 Iustin Pop
        return False
200 033b7b64 Michael Hanselmann
    elif len(line) == 0 or line == "/":
201 033b7b64 Michael Hanselmann
      self.parents = self.parents[0:1]
202 033b7b64 Michael Hanselmann
      self.path = []
203 033b7b64 Michael Hanselmann
      return False
204 a8083063 Iustin Pop
205 a8083063 Iustin Pop
    pointer = self.parents[-1]
206 a8083063 Iustin Pop
    dirs, entries = self._get_entries(pointer)
207 a8083063 Iustin Pop
208 a8083063 Iustin Pop
    if line not in dirs:
209 a8083063 Iustin Pop
      print "No such child"
210 a8083063 Iustin Pop
      return False
211 a8083063 Iustin Pop
    if isinstance(pointer, (dict, list, tuple)):
212 a8083063 Iustin Pop
      if isinstance(pointer, (list, tuple)):
213 a8083063 Iustin Pop
        line = int(line)
214 a8083063 Iustin Pop
      new_obj = pointer[line]
215 a8083063 Iustin Pop
    else:
216 a8083063 Iustin Pop
      new_obj = getattr(pointer, line)
217 a8083063 Iustin Pop
    self.parents.append(new_obj)
218 a8083063 Iustin Pop
    self.path.append(str(line))
219 a8083063 Iustin Pop
    return False
220 a8083063 Iustin Pop
221 a8083063 Iustin Pop
  def do_pwd(self, line):
222 a8083063 Iustin Pop
    """Shows the current path.
223 a8083063 Iustin Pop
224 a8083063 Iustin Pop
    This duplicates the prompt functionality, but it's reasonable to
225 a8083063 Iustin Pop
    have.
226 a8083063 Iustin Pop
227 a8083063 Iustin Pop
    """
228 a8083063 Iustin Pop
    print "/" + "/".join(self.path)
229 a8083063 Iustin Pop
    return False
230 a8083063 Iustin Pop
231 a8083063 Iustin Pop
  def complete_cat(self, text, line, begidx, endidx):
232 a8083063 Iustin Pop
    """Completion for the cat command.
233 a8083063 Iustin Pop
234 a8083063 Iustin Pop
    """
235 a8083063 Iustin Pop
    pointer = self.parents[-1]
236 a8083063 Iustin Pop
    dirs, entries = self._get_entries(pointer)
237 a8083063 Iustin Pop
    matches = [name for name in entries if name.startswith(text)]
238 a8083063 Iustin Pop
    return matches
239 a8083063 Iustin Pop
240 a8083063 Iustin Pop
  def do_cat(self, line):
241 a8083063 Iustin Pop
    """Shows the contents of the given file.
242 a8083063 Iustin Pop
243 a8083063 Iustin Pop
    This will display the contents of the given file, which must be a
244 a8083063 Iustin Pop
    child of the current path (as shows by `ls`).
245 a8083063 Iustin Pop
246 a8083063 Iustin Pop
    """
247 a8083063 Iustin Pop
    pointer = self.parents[-1]
248 a8083063 Iustin Pop
    dirs, entries = self._get_entries(pointer)
249 a8083063 Iustin Pop
    if line not in entries:
250 a8083063 Iustin Pop
      print "No such entry"
251 a8083063 Iustin Pop
      return False
252 a8083063 Iustin Pop
253 a8083063 Iustin Pop
    if isinstance(pointer, (dict, list, tuple)):
254 a8083063 Iustin Pop
      if isinstance(pointer, (list, tuple)):
255 a8083063 Iustin Pop
        line = int(line)
256 a8083063 Iustin Pop
      val = pointer[line]
257 a8083063 Iustin Pop
    else:
258 a8083063 Iustin Pop
      val = getattr(pointer, line)
259 a8083063 Iustin Pop
    print val
260 a8083063 Iustin Pop
    return False
261 a8083063 Iustin Pop
262 a8083063 Iustin Pop
  def do_verify(self, line):
263 a8083063 Iustin Pop
    """Verify the configuration.
264 a8083063 Iustin Pop
265 a8083063 Iustin Pop
    This verifies the contents of the configuration file (and not the
266 a8083063 Iustin Pop
    in-memory data, as every modify operation automatically saves the
267 a8083063 Iustin Pop
    file).
268 a8083063 Iustin Pop
269 a8083063 Iustin Pop
    """
270 a8083063 Iustin Pop
    vdata = self.cfg.VerifyConfig()
271 a8083063 Iustin Pop
    if vdata:
272 a8083063 Iustin Pop
      print "Validation failed. Errors:"
273 a8083063 Iustin Pop
      for text in vdata:
274 a8083063 Iustin Pop
        print text
275 a8083063 Iustin Pop
    return False
276 a8083063 Iustin Pop
277 a8083063 Iustin Pop
  def do_save(self, line):
278 a8083063 Iustin Pop
    """Saves the configuration data.
279 a8083063 Iustin Pop
280 a8083063 Iustin Pop
    Note that is redundant (all modify operations automatically save
281 a8083063 Iustin Pop
    the data), but it is good to use it as in the future that could
282 a8083063 Iustin Pop
    change.
283 a8083063 Iustin Pop
284 a8083063 Iustin Pop
    """
285 a8083063 Iustin Pop
    if self.cfg.VerifyConfig():
286 a8083063 Iustin Pop
      print "Config data does not validate, refusing to save."
287 a8083063 Iustin Pop
      return False
288 a8083063 Iustin Pop
    self.cfg._WriteConfig()
289 a8083063 Iustin Pop
290 a8083063 Iustin Pop
  def do_rm(self, line):
291 a8083063 Iustin Pop
    """Removes an instance or a node.
292 a8083063 Iustin Pop
293 a8083063 Iustin Pop
    This function works only on instances or nodes. You must be in
294 a8083063 Iustin Pop
    either `/nodes` or `/instances` and give a valid argument.
295 a8083063 Iustin Pop
296 a8083063 Iustin Pop
    """
297 a8083063 Iustin Pop
    pointer = self.parents[-1]
298 a8083063 Iustin Pop
    data = self.cfg._config_data
299 a8083063 Iustin Pop
    if pointer not in (data.instances, data.nodes):
300 a8083063 Iustin Pop
      print "Can only delete instances and nodes"
301 a8083063 Iustin Pop
      return False
302 a8083063 Iustin Pop
    if pointer == data.instances:
303 a8083063 Iustin Pop
      if line in data.instances:
304 a8083063 Iustin Pop
        self.cfg.RemoveInstance(line)
305 a8083063 Iustin Pop
      else:
306 a8083063 Iustin Pop
        print "Invalid instance name"
307 a8083063 Iustin Pop
    else:
308 a8083063 Iustin Pop
      if line in data.nodes:
309 a8083063 Iustin Pop
        self.cfg.RemoveNode(line)
310 a8083063 Iustin Pop
      else:
311 a8083063 Iustin Pop
        print "Invalid node name"
312 a8083063 Iustin Pop
313 a8083063 Iustin Pop
  def do_EOF(self, line):
314 3ecf6786 Iustin Pop
    """Exit the application.
315 3ecf6786 Iustin Pop
316 3ecf6786 Iustin Pop
    """
317 a8083063 Iustin Pop
    print
318 a8083063 Iustin Pop
    return True
319 a8083063 Iustin Pop
320 a8083063 Iustin Pop
  def do_quit(self, line):
321 a8083063 Iustin Pop
    """Exit the application.
322 a8083063 Iustin Pop
323 a8083063 Iustin Pop
    """
324 a8083063 Iustin Pop
    print
325 a8083063 Iustin Pop
    return True
326 a8083063 Iustin Pop
327 033b7b64 Michael Hanselmann
328 a8083063 Iustin Pop
class Error(Exception):
329 a8083063 Iustin Pop
  """Generic exception"""
330 a8083063 Iustin Pop
  pass
331 a8083063 Iustin Pop
332 a8083063 Iustin Pop
333 a8083063 Iustin Pop
def ParseOptions():
334 a8083063 Iustin Pop
  """Parses the command line options.
335 a8083063 Iustin Pop
336 a8083063 Iustin Pop
  In case of command line errors, it will show the usage and exit the
337 a8083063 Iustin Pop
  program.
338 a8083063 Iustin Pop
339 a8083063 Iustin Pop
  Returns:
340 a8083063 Iustin Pop
    (options, args), as returned by OptionParser.parse_args
341 a8083063 Iustin Pop
  """
342 a8083063 Iustin Pop
343 a8083063 Iustin Pop
  parser = optparse.OptionParser()
344 a8083063 Iustin Pop
345 a8083063 Iustin Pop
  options, args = parser.parse_args()
346 a8083063 Iustin Pop
347 a8083063 Iustin Pop
  return options, args
348 a8083063 Iustin Pop
349 a8083063 Iustin Pop
350 a8083063 Iustin Pop
def main():
351 a8083063 Iustin Pop
  """Application entry point.
352 a8083063 Iustin Pop
353 a8083063 Iustin Pop
  This is just a wrapper over BootStrap, to handle our own exceptions.
354 a8083063 Iustin Pop
  """
355 a8083063 Iustin Pop
  options, args = ParseOptions()
356 a8083063 Iustin Pop
  if args:
357 a8083063 Iustin Pop
    cfg_file = args[0]
358 a8083063 Iustin Pop
  else:
359 a8083063 Iustin Pop
    cfg_file = None
360 a8083063 Iustin Pop
  shell = ConfigShell(cfg_file=cfg_file)
361 a8083063 Iustin Pop
  shell.cmdloop()
362 a8083063 Iustin Pop
363 a8083063 Iustin Pop
364 a8083063 Iustin Pop
if __name__ == "__main__":
365 a8083063 Iustin Pop
  main()