Statistics
| Branch: | Tag: | Revision:

root / tools / cfgshell @ 18397489

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