Statistics
| Branch: | Tag: | Revision:

root / tools / cfgshell @ 577d45d4

History | View | Annotate | Download (9.1 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 2d54e29c Iustin Pop
  # all do_/complete_* functions follow the same API
57 2d54e29c Iustin Pop
  # pylint: disable-msg=W0613
58 a8083063 Iustin Pop
  prompt = "(/) "
59 a8083063 Iustin Pop
60 a8083063 Iustin Pop
  def __init__(self, cfg_file=None):
61 a8083063 Iustin Pop
    """Constructor for the ConfigShell object.
62 a8083063 Iustin Pop
63 a8083063 Iustin Pop
    The optional cfg_file argument will be used to load a config file
64 a8083063 Iustin Pop
    at startup.
65 a8083063 Iustin Pop
66 a8083063 Iustin Pop
    """
67 a8083063 Iustin Pop
    cmd.Cmd.__init__(self)
68 5fcdc80d Iustin Pop
    self.cfg = None
69 a8083063 Iustin Pop
    self.parents = []
70 a8083063 Iustin Pop
    self.path = []
71 a8083063 Iustin Pop
    if cfg_file:
72 a8083063 Iustin Pop
      self.do_load(cfg_file)
73 a8083063 Iustin Pop
      self.postcmd(False, "")
74 a8083063 Iustin Pop
75 a8083063 Iustin Pop
  def emptyline(self):
76 a8083063 Iustin Pop
    """Empty line handling.
77 a8083063 Iustin Pop
78 a8083063 Iustin Pop
    Note that the default will re-run the last command. We don't want
79 a8083063 Iustin Pop
    that, and just ignore the empty line.
80 a8083063 Iustin Pop
81 a8083063 Iustin Pop
    """
82 a8083063 Iustin Pop
    return False
83 a8083063 Iustin Pop
84 a8083063 Iustin Pop
  @staticmethod
85 a8083063 Iustin Pop
  def _get_entries(obj):
86 a8083063 Iustin Pop
    """Computes the list of subdirs and files in the given object.
87 a8083063 Iustin Pop
88 a8083063 Iustin Pop
    This, depending on the passed object entry, look at each logical
89 a8083063 Iustin Pop
    child of the object and decides if it's a container or a simple
90 a8083063 Iustin Pop
    object. Based on this, it computes the list of subdir and files.
91 a8083063 Iustin Pop
92 a8083063 Iustin Pop
    """
93 a8083063 Iustin Pop
    dirs = []
94 a8083063 Iustin Pop
    entries = []
95 a8083063 Iustin Pop
    if isinstance(obj, objects.ConfigObject):
96 adf385c7 Iustin Pop
      # pylint: disable-msg=W0212
97 adf385c7 Iustin Pop
      # yes, we're using a protected member
98 adf385c7 Iustin Pop
      for name in obj._all_slots():
99 a8083063 Iustin Pop
        child = getattr(obj, name, None)
100 a8083063 Iustin Pop
        if isinstance(child, (list, dict, tuple, objects.ConfigObject)):
101 a8083063 Iustin Pop
          dirs.append(name)
102 a8083063 Iustin Pop
        else:
103 a8083063 Iustin Pop
          entries.append(name)
104 a8083063 Iustin Pop
    elif isinstance(obj, (list, tuple)):
105 a8083063 Iustin Pop
      for idx, child in enumerate(obj):
106 a8083063 Iustin Pop
        if isinstance(child, (list, dict, tuple, objects.ConfigObject)):
107 a8083063 Iustin Pop
          dirs.append(str(idx))
108 a8083063 Iustin Pop
        else:
109 a8083063 Iustin Pop
          entries.append(str(idx))
110 a8083063 Iustin Pop
    elif isinstance(obj, dict):
111 a8083063 Iustin Pop
      dirs = obj.keys()
112 a8083063 Iustin Pop
113 a8083063 Iustin Pop
    return dirs, entries
114 a8083063 Iustin Pop
115 a8083063 Iustin Pop
  def precmd(self, line):
116 a8083063 Iustin Pop
    """Precmd hook to prevent commands in invalid states.
117 a8083063 Iustin Pop
118 a8083063 Iustin Pop
    This will prevent everything except load and quit when no
119 a8083063 Iustin Pop
    configuration is loaded.
120 a8083063 Iustin Pop
121 a8083063 Iustin Pop
    """
122 a8083063 Iustin Pop
    if line.startswith("load") or line == 'EOF' or line == "quit":
123 a8083063 Iustin Pop
      return line
124 a8083063 Iustin Pop
    if not self.parents or self.cfg is None:
125 a8083063 Iustin Pop
      print "No config data loaded"
126 a8083063 Iustin Pop
      return ""
127 a8083063 Iustin Pop
    return line
128 a8083063 Iustin Pop
129 a8083063 Iustin Pop
  def postcmd(self, stop, line):
130 a8083063 Iustin Pop
    """Postcmd hook to update the prompt.
131 a8083063 Iustin Pop
132 a8083063 Iustin Pop
    We show the current location in the prompt and this function is
133 a8083063 Iustin Pop
    used to update it; this is only needed after cd and load, but we
134 a8083063 Iustin Pop
    update it anyway.
135 a8083063 Iustin Pop
136 a8083063 Iustin Pop
    """
137 a8083063 Iustin Pop
    if self.cfg is None:
138 a8083063 Iustin Pop
      self.prompt = "(#no config) "
139 a8083063 Iustin Pop
    else:
140 5fcdc80d Iustin Pop
      self.prompt = "(/%s) " % ("/".join(self.path),)
141 a8083063 Iustin Pop
    return stop
142 a8083063 Iustin Pop
143 a8083063 Iustin Pop
  def do_load(self, line):
144 a8083063 Iustin Pop
    """Load function.
145 a8083063 Iustin Pop
146 a8083063 Iustin Pop
    Syntax: load [/path/to/config/file]
147 a8083063 Iustin Pop
148 a8083063 Iustin Pop
    This will load a new configuration, discarding any existing data
149 a8083063 Iustin Pop
    (if any). If no argument has been passed, it will use the default
150 a8083063 Iustin Pop
    config file location.
151 a8083063 Iustin Pop
152 a8083063 Iustin Pop
    """
153 a8083063 Iustin Pop
    if line:
154 a8083063 Iustin Pop
      arg = line
155 a8083063 Iustin Pop
    else:
156 a8083063 Iustin Pop
      arg = None
157 a8083063 Iustin Pop
    try:
158 a8083063 Iustin Pop
      self.cfg = config.ConfigWriter(cfg_file=arg, offline=True)
159 7260cfbe Iustin Pop
      self.parents = [self.cfg._config_data] # pylint: disable-msg=W0212
160 a8083063 Iustin Pop
      self.path = []
161 a8083063 Iustin Pop
    except errors.ConfigurationError, err:
162 a8083063 Iustin Pop
      print "Error: %s" % str(err)
163 a8083063 Iustin Pop
    return False
164 a8083063 Iustin Pop
165 a8083063 Iustin Pop
  def do_ls(self, line):
166 a8083063 Iustin Pop
    """List the current entry.
167 a8083063 Iustin Pop
168 a8083063 Iustin Pop
    This will show directories with a slash appended and files
169 a8083063 Iustin Pop
    normally.
170 a8083063 Iustin Pop
171 a8083063 Iustin Pop
    """
172 a8083063 Iustin Pop
    dirs, entries = self._get_entries(self.parents[-1])
173 a8083063 Iustin Pop
    for i in dirs:
174 a8083063 Iustin Pop
      print i + "/"
175 a8083063 Iustin Pop
    for i in entries:
176 a8083063 Iustin Pop
      print i
177 a8083063 Iustin Pop
    return False
178 a8083063 Iustin Pop
179 a8083063 Iustin Pop
  def complete_cd(self, text, line, begidx, endidx):
180 a8083063 Iustin Pop
    """Completion function for the cd command.
181 a8083063 Iustin Pop
182 a8083063 Iustin Pop
    """
183 a8083063 Iustin Pop
    pointer = self.parents[-1]
184 f4ad2ef0 Iustin Pop
    dirs, _ = self._get_entries(pointer)
185 a8083063 Iustin Pop
    matches = [str(name) for name in dirs if name.startswith(text)]
186 a8083063 Iustin Pop
    return matches
187 a8083063 Iustin Pop
188 a8083063 Iustin Pop
  def do_cd(self, line):
189 a8083063 Iustin Pop
    """Changes the current path.
190 a8083063 Iustin Pop
191 033b7b64 Michael Hanselmann
    Valid arguments: either .., /, "" (no argument) or a child of the current
192 033b7b64 Michael Hanselmann
    object.
193 a8083063 Iustin Pop
194 a8083063 Iustin Pop
    """
195 a8083063 Iustin Pop
    if line == "..":
196 a8083063 Iustin Pop
      if self.path:
197 a8083063 Iustin Pop
        self.path.pop()
198 a8083063 Iustin Pop
        self.parents.pop()
199 a8083063 Iustin Pop
        return False
200 a8083063 Iustin Pop
      else:
201 a8083063 Iustin Pop
        print "Already at top level"
202 a8083063 Iustin Pop
        return False
203 033b7b64 Michael Hanselmann
    elif len(line) == 0 or line == "/":
204 033b7b64 Michael Hanselmann
      self.parents = self.parents[0:1]
205 033b7b64 Michael Hanselmann
      self.path = []
206 033b7b64 Michael Hanselmann
      return False
207 a8083063 Iustin Pop
208 a8083063 Iustin Pop
    pointer = self.parents[-1]
209 f4ad2ef0 Iustin Pop
    dirs, _ = self._get_entries(pointer)
210 a8083063 Iustin Pop
211 a8083063 Iustin Pop
    if line not in dirs:
212 a8083063 Iustin Pop
      print "No such child"
213 a8083063 Iustin Pop
      return False
214 a8083063 Iustin Pop
    if isinstance(pointer, (dict, list, tuple)):
215 a8083063 Iustin Pop
      if isinstance(pointer, (list, tuple)):
216 a8083063 Iustin Pop
        line = int(line)
217 a8083063 Iustin Pop
      new_obj = pointer[line]
218 a8083063 Iustin Pop
    else:
219 a8083063 Iustin Pop
      new_obj = getattr(pointer, line)
220 a8083063 Iustin Pop
    self.parents.append(new_obj)
221 a8083063 Iustin Pop
    self.path.append(str(line))
222 a8083063 Iustin Pop
    return False
223 a8083063 Iustin Pop
224 a8083063 Iustin Pop
  def do_pwd(self, line):
225 a8083063 Iustin Pop
    """Shows the current path.
226 a8083063 Iustin Pop
227 a8083063 Iustin Pop
    This duplicates the prompt functionality, but it's reasonable to
228 a8083063 Iustin Pop
    have.
229 a8083063 Iustin Pop
230 a8083063 Iustin Pop
    """
231 a8083063 Iustin Pop
    print "/" + "/".join(self.path)
232 a8083063 Iustin Pop
    return False
233 a8083063 Iustin Pop
234 a8083063 Iustin Pop
  def complete_cat(self, text, line, begidx, endidx):
235 a8083063 Iustin Pop
    """Completion for the cat command.
236 a8083063 Iustin Pop
237 a8083063 Iustin Pop
    """
238 a8083063 Iustin Pop
    pointer = self.parents[-1]
239 f4ad2ef0 Iustin Pop
    _, entries = self._get_entries(pointer)
240 a8083063 Iustin Pop
    matches = [name for name in entries if name.startswith(text)]
241 a8083063 Iustin Pop
    return matches
242 a8083063 Iustin Pop
243 a8083063 Iustin Pop
  def do_cat(self, line):
244 a8083063 Iustin Pop
    """Shows the contents of the given file.
245 a8083063 Iustin Pop
246 a8083063 Iustin Pop
    This will display the contents of the given file, which must be a
247 a8083063 Iustin Pop
    child of the current path (as shows by `ls`).
248 a8083063 Iustin Pop
249 a8083063 Iustin Pop
    """
250 a8083063 Iustin Pop
    pointer = self.parents[-1]
251 f4ad2ef0 Iustin Pop
    _, entries = self._get_entries(pointer)
252 a8083063 Iustin Pop
    if line not in entries:
253 a8083063 Iustin Pop
      print "No such entry"
254 a8083063 Iustin Pop
      return False
255 a8083063 Iustin Pop
256 a8083063 Iustin Pop
    if isinstance(pointer, (dict, list, tuple)):
257 a8083063 Iustin Pop
      if isinstance(pointer, (list, tuple)):
258 a8083063 Iustin Pop
        line = int(line)
259 a8083063 Iustin Pop
      val = pointer[line]
260 a8083063 Iustin Pop
    else:
261 a8083063 Iustin Pop
      val = getattr(pointer, line)
262 a8083063 Iustin Pop
    print val
263 a8083063 Iustin Pop
    return False
264 a8083063 Iustin Pop
265 a8083063 Iustin Pop
  def do_verify(self, line):
266 a8083063 Iustin Pop
    """Verify the configuration.
267 a8083063 Iustin Pop
268 a8083063 Iustin Pop
    This verifies the contents of the configuration file (and not the
269 a8083063 Iustin Pop
    in-memory data, as every modify operation automatically saves the
270 a8083063 Iustin Pop
    file).
271 a8083063 Iustin Pop
272 a8083063 Iustin Pop
    """
273 a8083063 Iustin Pop
    vdata = self.cfg.VerifyConfig()
274 a8083063 Iustin Pop
    if vdata:
275 a8083063 Iustin Pop
      print "Validation failed. Errors:"
276 a8083063 Iustin Pop
      for text in vdata:
277 a8083063 Iustin Pop
        print text
278 a8083063 Iustin Pop
    return False
279 a8083063 Iustin Pop
280 a8083063 Iustin Pop
  def do_save(self, line):
281 a8083063 Iustin Pop
    """Saves the configuration data.
282 a8083063 Iustin Pop
283 a8083063 Iustin Pop
    Note that is redundant (all modify operations automatically save
284 a8083063 Iustin Pop
    the data), but it is good to use it as in the future that could
285 a8083063 Iustin Pop
    change.
286 a8083063 Iustin Pop
287 a8083063 Iustin Pop
    """
288 a8083063 Iustin Pop
    if self.cfg.VerifyConfig():
289 a8083063 Iustin Pop
      print "Config data does not validate, refusing to save."
290 a8083063 Iustin Pop
      return False
291 7260cfbe Iustin Pop
    self.cfg._WriteConfig() # pylint: disable-msg=W0212
292 a8083063 Iustin Pop
293 a8083063 Iustin Pop
  def do_rm(self, line):
294 a8083063 Iustin Pop
    """Removes an instance or a node.
295 a8083063 Iustin Pop
296 a8083063 Iustin Pop
    This function works only on instances or nodes. You must be in
297 a8083063 Iustin Pop
    either `/nodes` or `/instances` and give a valid argument.
298 a8083063 Iustin Pop
299 a8083063 Iustin Pop
    """
300 a8083063 Iustin Pop
    pointer = self.parents[-1]
301 7260cfbe Iustin Pop
    data = self.cfg._config_data  # pylint: disable-msg=W0212
302 a8083063 Iustin Pop
    if pointer not in (data.instances, data.nodes):
303 a8083063 Iustin Pop
      print "Can only delete instances and nodes"
304 a8083063 Iustin Pop
      return False
305 a8083063 Iustin Pop
    if pointer == data.instances:
306 a8083063 Iustin Pop
      if line in data.instances:
307 a8083063 Iustin Pop
        self.cfg.RemoveInstance(line)
308 a8083063 Iustin Pop
      else:
309 a8083063 Iustin Pop
        print "Invalid instance name"
310 a8083063 Iustin Pop
    else:
311 a8083063 Iustin Pop
      if line in data.nodes:
312 a8083063 Iustin Pop
        self.cfg.RemoveNode(line)
313 a8083063 Iustin Pop
      else:
314 a8083063 Iustin Pop
        print "Invalid node name"
315 a8083063 Iustin Pop
316 7e950d31 Iustin Pop
  @staticmethod
317 7e950d31 Iustin Pop
  def do_EOF(line):
318 3ecf6786 Iustin Pop
    """Exit the application.
319 3ecf6786 Iustin Pop
320 3ecf6786 Iustin Pop
    """
321 a8083063 Iustin Pop
    print
322 a8083063 Iustin Pop
    return True
323 a8083063 Iustin Pop
324 7e950d31 Iustin Pop
  @staticmethod
325 7e950d31 Iustin Pop
  def do_quit(line):
326 a8083063 Iustin Pop
    """Exit the application.
327 a8083063 Iustin Pop
328 a8083063 Iustin Pop
    """
329 a8083063 Iustin Pop
    print
330 a8083063 Iustin Pop
    return True
331 a8083063 Iustin Pop
332 033b7b64 Michael Hanselmann
333 a8083063 Iustin Pop
class Error(Exception):
334 a8083063 Iustin Pop
  """Generic exception"""
335 a8083063 Iustin Pop
  pass
336 a8083063 Iustin Pop
337 a8083063 Iustin Pop
338 a8083063 Iustin Pop
def ParseOptions():
339 a8083063 Iustin Pop
  """Parses the command line options.
340 a8083063 Iustin Pop
341 a8083063 Iustin Pop
  In case of command line errors, it will show the usage and exit the
342 a8083063 Iustin Pop
  program.
343 a8083063 Iustin Pop
344 454723b5 Iustin Pop
  @return: a tuple (options, args), as returned by OptionParser.parse_args
345 a8083063 Iustin Pop
346 454723b5 Iustin Pop
  """
347 a8083063 Iustin Pop
  parser = optparse.OptionParser()
348 a8083063 Iustin Pop
349 a8083063 Iustin Pop
  options, args = parser.parse_args()
350 a8083063 Iustin Pop
351 a8083063 Iustin Pop
  return options, args
352 a8083063 Iustin Pop
353 a8083063 Iustin Pop
354 a8083063 Iustin Pop
def main():
355 a8083063 Iustin Pop
  """Application entry point.
356 a8083063 Iustin Pop
357 a8083063 Iustin Pop
  """
358 f4ad2ef0 Iustin Pop
  _, args = ParseOptions()
359 a8083063 Iustin Pop
  if args:
360 a8083063 Iustin Pop
    cfg_file = args[0]
361 a8083063 Iustin Pop
  else:
362 a8083063 Iustin Pop
    cfg_file = None
363 a8083063 Iustin Pop
  shell = ConfigShell(cfg_file=cfg_file)
364 a8083063 Iustin Pop
  shell.cmdloop()
365 a8083063 Iustin Pop
366 a8083063 Iustin Pop
367 a8083063 Iustin Pop
if __name__ == "__main__":
368 a8083063 Iustin Pop
  main()