Statistics
| Branch: | Tag: | Revision:

root / lib / cli.py @ dcb93971

History | View | Annotate | Download (8.3 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
"""Module dealing with command line parsing"""
23 a8083063 Iustin Pop
24 a8083063 Iustin Pop
25 a8083063 Iustin Pop
import sys
26 a8083063 Iustin Pop
import textwrap
27 a8083063 Iustin Pop
import os.path
28 a8083063 Iustin Pop
import copy
29 a8083063 Iustin Pop
30 a8083063 Iustin Pop
from ganeti import utils
31 a8083063 Iustin Pop
from ganeti import logger
32 a8083063 Iustin Pop
from ganeti import errors
33 a8083063 Iustin Pop
from ganeti import mcpu
34 a8083063 Iustin Pop
from ganeti import constants
35 a8083063 Iustin Pop
36 a8083063 Iustin Pop
from optparse import (OptionParser, make_option, TitledHelpFormatter,
37 a8083063 Iustin Pop
                      Option, OptionValueError, SUPPRESS_HELP)
38 a8083063 Iustin Pop
39 a8083063 Iustin Pop
__all__ = ["DEBUG_OPT", "NOHDR_OPT", "SEP_OPT", "GenericMain", "SubmitOpCode",
40 a8083063 Iustin Pop
           "cli_option",
41 a8083063 Iustin Pop
           "ARGS_NONE", "ARGS_FIXED", "ARGS_ATLEAST", "ARGS_ANY", "ARGS_ONE",
42 dcb93971 Michael Hanselmann
           "USEUNITS_OPT", "FIELDS_OPT"]
43 a8083063 Iustin Pop
44 a8083063 Iustin Pop
DEBUG_OPT = make_option("-d", "--debug", default=False,
45 a8083063 Iustin Pop
                        action="store_true",
46 a8083063 Iustin Pop
                        help="Turn debugging on")
47 a8083063 Iustin Pop
48 a8083063 Iustin Pop
NOHDR_OPT = make_option("--no-headers", default=False,
49 a8083063 Iustin Pop
                        action="store_true", dest="no_headers",
50 a8083063 Iustin Pop
                        help="Don't display column headers")
51 a8083063 Iustin Pop
52 a8083063 Iustin Pop
SEP_OPT = make_option("--separator", default=" ",
53 a8083063 Iustin Pop
                      action="store", dest="separator",
54 a8083063 Iustin Pop
                      help="Separator between output fields"
55 a8083063 Iustin Pop
                      " (defaults to one space)")
56 a8083063 Iustin Pop
57 a8083063 Iustin Pop
USEUNITS_OPT = make_option("--human-readable", default=False,
58 a8083063 Iustin Pop
                           action="store_true", dest="human_readable",
59 a8083063 Iustin Pop
                           help="Print sizes in human readable format")
60 a8083063 Iustin Pop
61 dcb93971 Michael Hanselmann
FIELDS_OPT = make_option("-o", "--output", dest="output", action="store",
62 dcb93971 Michael Hanselmann
                         type="string", help="Select output fields",
63 dcb93971 Michael Hanselmann
                         metavar="FIELDS")
64 dcb93971 Michael Hanselmann
65 a8083063 Iustin Pop
_LOCK_OPT = make_option("--lock-retries", default=None,
66 a8083063 Iustin Pop
                        type="int", help=SUPPRESS_HELP)
67 a8083063 Iustin Pop
68 a8083063 Iustin Pop
69 a8083063 Iustin Pop
def ARGS_FIXED(val):
70 a8083063 Iustin Pop
  """Macro-like function denoting a fixed number of arguments"""
71 a8083063 Iustin Pop
  return -val
72 a8083063 Iustin Pop
73 a8083063 Iustin Pop
74 a8083063 Iustin Pop
def ARGS_ATLEAST(val):
75 a8083063 Iustin Pop
  """Macro-like function denoting a minimum number of arguments"""
76 a8083063 Iustin Pop
  return val
77 a8083063 Iustin Pop
78 a8083063 Iustin Pop
79 a8083063 Iustin Pop
ARGS_NONE = None
80 a8083063 Iustin Pop
ARGS_ONE = ARGS_FIXED(1)
81 a8083063 Iustin Pop
ARGS_ANY = ARGS_ATLEAST(0)
82 a8083063 Iustin Pop
83 a8083063 Iustin Pop
84 a8083063 Iustin Pop
def check_unit(option, opt, value):
85 a8083063 Iustin Pop
  try:
86 a8083063 Iustin Pop
    return utils.ParseUnit(value)
87 a8083063 Iustin Pop
  except errors.UnitParseError, err:
88 a8083063 Iustin Pop
    raise OptionValueError, ("option %s: %s" % (opt, err))
89 a8083063 Iustin Pop
90 a8083063 Iustin Pop
91 a8083063 Iustin Pop
class CliOption(Option):
92 a8083063 Iustin Pop
  TYPES = Option.TYPES + ("unit",)
93 a8083063 Iustin Pop
  TYPE_CHECKER = copy.copy(Option.TYPE_CHECKER)
94 a8083063 Iustin Pop
  TYPE_CHECKER["unit"] = check_unit
95 a8083063 Iustin Pop
96 a8083063 Iustin Pop
97 a8083063 Iustin Pop
# optparse.py sets make_option, so we do it for our own option class, too
98 a8083063 Iustin Pop
cli_option = CliOption
99 a8083063 Iustin Pop
100 a8083063 Iustin Pop
101 a8083063 Iustin Pop
def _ParseArgs(argv, commands):
102 a8083063 Iustin Pop
  """Parses the command line and return the function which must be
103 a8083063 Iustin Pop
  executed together with its arguments
104 a8083063 Iustin Pop

105 a8083063 Iustin Pop
  Arguments:
106 a8083063 Iustin Pop
    argv: the command line
107 a8083063 Iustin Pop

108 a8083063 Iustin Pop
    commands: dictionary with special contents, see the design doc for
109 a8083063 Iustin Pop
    cmdline handling
110 a8083063 Iustin Pop
  """
111 a8083063 Iustin Pop
  if len(argv) == 0:
112 a8083063 Iustin Pop
    binary = "<command>"
113 a8083063 Iustin Pop
  else:
114 a8083063 Iustin Pop
    binary = argv[0].split("/")[-1]
115 a8083063 Iustin Pop
116 a8083063 Iustin Pop
  if len(argv) > 1 and argv[1] == "--version":
117 a8083063 Iustin Pop
    print "%s (ganeti) %s" % (binary, constants.RELEASE_VERSION)
118 a8083063 Iustin Pop
    # Quit right away. That way we don't have to care about this special
119 a8083063 Iustin Pop
    # argument. optparse.py does it the same.
120 a8083063 Iustin Pop
    sys.exit(0)
121 a8083063 Iustin Pop
122 a8083063 Iustin Pop
  if len(argv) < 2 or argv[1] not in commands.keys():
123 a8083063 Iustin Pop
    # let's do a nice thing
124 a8083063 Iustin Pop
    sortedcmds = commands.keys()
125 a8083063 Iustin Pop
    sortedcmds.sort()
126 a8083063 Iustin Pop
    print ("Usage: %(bin)s {command} [options...] [argument...]"
127 a8083063 Iustin Pop
           "\n%(bin)s <command> --help to see details, or"
128 a8083063 Iustin Pop
           " man %(bin)s\n" % {"bin": binary})
129 a8083063 Iustin Pop
    # compute the max line length for cmd + usage
130 a8083063 Iustin Pop
    mlen = max([len(" %s %s" % (cmd, commands[cmd][3])) for cmd in commands])
131 a8083063 Iustin Pop
    mlen = min(60, mlen) # should not get here...
132 a8083063 Iustin Pop
    # and format a nice command list
133 a8083063 Iustin Pop
    print "Commands:"
134 a8083063 Iustin Pop
    for cmd in sortedcmds:
135 a8083063 Iustin Pop
      cmdstr = " %s %s" % (cmd, commands[cmd][3])
136 a8083063 Iustin Pop
      help_text = commands[cmd][4]
137 a8083063 Iustin Pop
      help_lines = textwrap.wrap(help_text, 79-3-mlen)
138 a8083063 Iustin Pop
      print "%-*s - %s" % (mlen, cmdstr,
139 a8083063 Iustin Pop
                                          help_lines.pop(0))
140 a8083063 Iustin Pop
      for line in help_lines:
141 a8083063 Iustin Pop
        print "%-*s   %s" % (mlen, "", line)
142 a8083063 Iustin Pop
    print
143 a8083063 Iustin Pop
    return None, None, None
144 a8083063 Iustin Pop
  cmd = argv.pop(1)
145 a8083063 Iustin Pop
  func, nargs, parser_opts, usage, description = commands[cmd]
146 a8083063 Iustin Pop
  parser_opts.append(_LOCK_OPT)
147 a8083063 Iustin Pop
  parser = OptionParser(option_list=parser_opts,
148 a8083063 Iustin Pop
                        description=description,
149 a8083063 Iustin Pop
                        formatter=TitledHelpFormatter(),
150 a8083063 Iustin Pop
                        usage="%%prog %s %s" % (cmd, usage))
151 a8083063 Iustin Pop
  parser.disable_interspersed_args()
152 a8083063 Iustin Pop
  options, args = parser.parse_args()
153 a8083063 Iustin Pop
  if nargs is None:
154 a8083063 Iustin Pop
    if len(args) != 0:
155 a8083063 Iustin Pop
      print >> sys.stderr, ("Error: Command %s expects no arguments" % cmd)
156 a8083063 Iustin Pop
      return None, None, None
157 a8083063 Iustin Pop
  elif nargs < 0 and len(args) != -nargs:
158 a8083063 Iustin Pop
    print >> sys.stderr, ("Error: Command %s expects %d argument(s)" %
159 a8083063 Iustin Pop
                         (cmd, -nargs))
160 a8083063 Iustin Pop
    return None, None, None
161 a8083063 Iustin Pop
  elif nargs >= 0 and len(args) < nargs:
162 a8083063 Iustin Pop
    print >> sys.stderr, ("Error: Command %s expects at least %d argument(s)" %
163 a8083063 Iustin Pop
                         (cmd, nargs))
164 a8083063 Iustin Pop
    return None, None, None
165 a8083063 Iustin Pop
166 a8083063 Iustin Pop
  return func, options, args
167 a8083063 Iustin Pop
168 a8083063 Iustin Pop
169 a8083063 Iustin Pop
def _AskUser(text):
170 a8083063 Iustin Pop
  """Ask the user a yes/no question.
171 a8083063 Iustin Pop

172 a8083063 Iustin Pop
  Args:
173 a8083063 Iustin Pop
    questionstring - the question to ask.
174 a8083063 Iustin Pop

175 a8083063 Iustin Pop
  Returns:
176 a8083063 Iustin Pop
    True or False depending on answer (No for False is default).
177 a8083063 Iustin Pop

178 a8083063 Iustin Pop
  """
179 a8083063 Iustin Pop
  try:
180 a8083063 Iustin Pop
    f = file("/dev/tty", "r+")
181 a8083063 Iustin Pop
  except IOError:
182 a8083063 Iustin Pop
    return False
183 a8083063 Iustin Pop
  answer = False
184 a8083063 Iustin Pop
  try:
185 a8083063 Iustin Pop
    f.write(textwrap.fill(text))
186 a8083063 Iustin Pop
    f.write('\n')
187 a8083063 Iustin Pop
    f.write("y/[n]: ")
188 a8083063 Iustin Pop
    line = f.readline(16).strip().lower()
189 a8083063 Iustin Pop
    answer = line in ('y', 'yes')
190 a8083063 Iustin Pop
  finally:
191 a8083063 Iustin Pop
    f.close()
192 a8083063 Iustin Pop
  return answer
193 a8083063 Iustin Pop
194 a8083063 Iustin Pop
195 a8083063 Iustin Pop
def SubmitOpCode(op):
196 a8083063 Iustin Pop
  """Function to submit an opcode.
197 a8083063 Iustin Pop

198 a8083063 Iustin Pop
  This is just a simple wrapper over the construction of the processor
199 a8083063 Iustin Pop
  instance. It should be extended to better handle feedback and
200 a8083063 Iustin Pop
  interaction functions.
201 a8083063 Iustin Pop

202 a8083063 Iustin Pop
  """
203 a8083063 Iustin Pop
  proc = mcpu.Processor()
204 a8083063 Iustin Pop
  return proc.ExecOpCode(op, logger.ToStdout)
205 a8083063 Iustin Pop
206 a8083063 Iustin Pop
207 a8083063 Iustin Pop
def GenericMain(commands):
208 a8083063 Iustin Pop
  """Generic main function for all the gnt-* commands.
209 a8083063 Iustin Pop

210 a8083063 Iustin Pop
  Argument: a dictionary with a special structure, see the design doc
211 a8083063 Iustin Pop
  for command line handling.
212 a8083063 Iustin Pop

213 a8083063 Iustin Pop
  """
214 a8083063 Iustin Pop
  # save the program name and the entire command line for later logging
215 a8083063 Iustin Pop
  if sys.argv:
216 a8083063 Iustin Pop
    binary = os.path.basename(sys.argv[0]) or sys.argv[0]
217 a8083063 Iustin Pop
    if len(sys.argv) >= 2:
218 a8083063 Iustin Pop
      binary += " " + sys.argv[1]
219 a8083063 Iustin Pop
      old_cmdline = " ".join(sys.argv[2:])
220 a8083063 Iustin Pop
    else:
221 a8083063 Iustin Pop
      old_cmdline = ""
222 a8083063 Iustin Pop
  else:
223 a8083063 Iustin Pop
    binary = "<unknown program>"
224 a8083063 Iustin Pop
    old_cmdline = ""
225 a8083063 Iustin Pop
226 a8083063 Iustin Pop
  func, options, args = _ParseArgs(sys.argv, commands)
227 a8083063 Iustin Pop
  if func is None: # parse error
228 a8083063 Iustin Pop
    return 1
229 a8083063 Iustin Pop
230 a8083063 Iustin Pop
  options._ask_user = _AskUser
231 a8083063 Iustin Pop
232 a8083063 Iustin Pop
  logger.SetupLogging(debug=options.debug, program=binary)
233 a8083063 Iustin Pop
234 a8083063 Iustin Pop
  try:
235 a8083063 Iustin Pop
    utils.Lock('cmd', max_retries=options.lock_retries, debug=options.debug)
236 a8083063 Iustin Pop
  except errors.LockError, err:
237 a8083063 Iustin Pop
    logger.ToStderr(str(err))
238 a8083063 Iustin Pop
    return 1
239 a8083063 Iustin Pop
240 a8083063 Iustin Pop
  if old_cmdline:
241 a8083063 Iustin Pop
    logger.Info("run with arguments '%s'" % old_cmdline)
242 a8083063 Iustin Pop
  else:
243 a8083063 Iustin Pop
    logger.Info("run with no arguments")
244 a8083063 Iustin Pop
245 a8083063 Iustin Pop
  try:
246 a8083063 Iustin Pop
    try:
247 a8083063 Iustin Pop
      result = func(options, args)
248 a8083063 Iustin Pop
    except errors.ConfigurationError, err:
249 a8083063 Iustin Pop
      logger.Error("Corrupt configuration file: %s" % err)
250 a8083063 Iustin Pop
      logger.ToStderr("Aborting.")
251 a8083063 Iustin Pop
      result = 2
252 a8083063 Iustin Pop
    except errors.HooksAbort, err:
253 a8083063 Iustin Pop
      logger.ToStderr("Failure: hooks execution failed:")
254 a8083063 Iustin Pop
      for node, script, out in err.args[0]:
255 a8083063 Iustin Pop
        if out:
256 a8083063 Iustin Pop
          logger.ToStderr("  node: %s, script: %s, output: %s" %
257 a8083063 Iustin Pop
                          (node, script, out))
258 a8083063 Iustin Pop
        else:
259 a8083063 Iustin Pop
          logger.ToStderr("  node: %s, script: %s (no output)" %
260 a8083063 Iustin Pop
                          (node, script))
261 a8083063 Iustin Pop
      result = 1
262 a8083063 Iustin Pop
    except errors.HooksFailure, err:
263 a8083063 Iustin Pop
      logger.ToStderr("Failure: hooks general failure: %s" % str(err))
264 a8083063 Iustin Pop
      result = 1
265 a8083063 Iustin Pop
    except errors.OpPrereqError, err:
266 a8083063 Iustin Pop
      logger.ToStderr("Failure: prerequisites not met for this"
267 a8083063 Iustin Pop
                      " operation:\n%s" % str(err))
268 a8083063 Iustin Pop
      result = 1
269 a8083063 Iustin Pop
    except errors.OpExecError, err:
270 a8083063 Iustin Pop
      logger.ToStderr("Failure: command execution error:\n%s" % str(err))
271 a8083063 Iustin Pop
      result = 1
272 a8083063 Iustin Pop
  finally:
273 a8083063 Iustin Pop
    utils.Unlock('cmd')
274 a8083063 Iustin Pop
    utils.LockCleanup()
275 a8083063 Iustin Pop
276 a8083063 Iustin Pop
  return result