Revision d971402f lib/cli.py
b/lib/cli.py | ||
---|---|---|
1480 | 1480 |
] |
1481 | 1481 |
|
1482 | 1482 |
|
1483 |
def _ParseArgs(argv, commands, aliases, env_override): |
|
1483 |
class _ShowUsage(Exception): |
|
1484 |
"""Exception class for L{_ParseArgs}. |
|
1485 |
|
|
1486 |
""" |
|
1487 |
def __init__(self, exit_error): |
|
1488 |
"""Initializes instances of this class. |
|
1489 |
|
|
1490 |
@type exit_error: bool |
|
1491 |
@param exit_error: Whether to report failure on exit |
|
1492 |
|
|
1493 |
""" |
|
1494 |
Exception.__init__(self) |
|
1495 |
self.exit_error = exit_error |
|
1496 |
|
|
1497 |
|
|
1498 |
class _ShowVersion(Exception): |
|
1499 |
"""Exception class for L{_ParseArgs}. |
|
1500 |
|
|
1501 |
""" |
|
1502 |
|
|
1503 |
|
|
1504 |
def _ParseArgs(binary, argv, commands, aliases, env_override): |
|
1484 | 1505 |
"""Parser for the command line arguments. |
1485 | 1506 |
|
1486 | 1507 |
This function parses the arguments and returns the function which |
1487 | 1508 |
must be executed together with its (modified) arguments. |
1488 | 1509 |
|
1489 |
@param argv: the command line
|
|
1490 |
@param commands: dictionary with special contents, see the design
|
|
1491 |
doc for cmdline handling
|
|
1492 |
@param aliases: dictionary with command aliases {'alias': 'target, ...}
|
|
1510 |
@param binary: Script name
|
|
1511 |
@param argv: Command line arguments
|
|
1512 |
@param commands: Dictionary containing command definitions
|
|
1513 |
@param aliases: dictionary with command aliases {"alias": "target", ...}
|
|
1493 | 1514 |
@param env_override: list of env variables allowed for default args |
1515 |
@raise _ShowUsage: If usage description should be shown |
|
1516 |
@raise _ShowVersion: If version should be shown |
|
1494 | 1517 |
|
1495 | 1518 |
""" |
1496 | 1519 |
assert not (env_override - set(commands)) |
1520 |
assert not (set(aliases.keys()) & set(commands.keys())) |
|
1497 | 1521 |
|
1498 |
if len(argv) == 0:
|
|
1499 |
binary = "<command>"
|
|
1522 |
if len(argv) > 1:
|
|
1523 |
cmd = argv[1]
|
|
1500 | 1524 |
else: |
1501 |
binary = argv[0].split("/")[-1] |
|
1525 |
# No option or command given |
|
1526 |
raise _ShowUsage(exit_error=True) |
|
1502 | 1527 |
|
1503 |
if len(argv) > 1 and argv[1] == "--version": |
|
1504 |
ToStdout("%s (ganeti %s) %s", binary, constants.VCS_VERSION, |
|
1505 |
constants.RELEASE_VERSION) |
|
1506 |
# Quit right away. That way we don't have to care about this special |
|
1507 |
# argument. optparse.py does it the same. |
|
1508 |
sys.exit(0) |
|
1509 |
|
|
1510 |
if len(argv) < 2 or not (argv[1] in commands or |
|
1511 |
argv[1] in aliases): |
|
1512 |
# let's do a nice thing |
|
1513 |
sortedcmds = commands.keys() |
|
1514 |
sortedcmds.sort() |
|
1515 |
|
|
1516 |
ToStdout("Usage: %s {command} [options...] [argument...]", binary) |
|
1517 |
ToStdout("%s <command> --help to see details, or man %s", binary, binary) |
|
1518 |
ToStdout("") |
|
1519 |
|
|
1520 |
# compute the max line length for cmd + usage |
|
1521 |
mlen = max([len(" %s" % cmd) for cmd in commands]) |
|
1522 |
mlen = min(60, mlen) # should not get here... |
|
1523 |
|
|
1524 |
# and format a nice command list |
|
1525 |
ToStdout("Commands:") |
|
1526 |
for cmd in sortedcmds: |
|
1527 |
cmdstr = " %s" % (cmd,) |
|
1528 |
help_text = commands[cmd][4] |
|
1529 |
help_lines = textwrap.wrap(help_text, 79 - 3 - mlen) |
|
1530 |
ToStdout("%-*s - %s", mlen, cmdstr, help_lines.pop(0)) |
|
1531 |
for line in help_lines: |
|
1532 |
ToStdout("%-*s %s", mlen, "", line) |
|
1533 |
|
|
1534 |
ToStdout("") |
|
1535 |
|
|
1536 |
return None, None, None |
|
1528 |
if cmd == "--version": |
|
1529 |
raise _ShowVersion() |
|
1530 |
elif cmd == "--help": |
|
1531 |
raise _ShowUsage(exit_error=False) |
|
1532 |
elif not (cmd in commands or cmd in aliases): |
|
1533 |
raise _ShowUsage(exit_error=True) |
|
1537 | 1534 |
|
1538 | 1535 |
# get command, unalias it, and look it up in commands |
1539 |
cmd = argv.pop(1) |
|
1540 | 1536 |
if cmd in aliases: |
1541 |
if cmd in commands: |
|
1542 |
raise errors.ProgrammerError("Alias '%s' overrides an existing" |
|
1543 |
" command" % cmd) |
|
1544 |
|
|
1545 | 1537 |
if aliases[cmd] not in commands: |
1546 | 1538 |
raise errors.ProgrammerError("Alias '%s' maps to non-existing" |
1547 | 1539 |
" command '%s'" % (cmd, aliases[cmd])) |
... | ... | |
1552 | 1544 |
args_env_name = ("%s_%s" % (binary.replace("-", "_"), cmd)).upper() |
1553 | 1545 |
env_args = os.environ.get(args_env_name) |
1554 | 1546 |
if env_args: |
1555 |
argv = utils.InsertAtPos(argv, 1, shlex.split(env_args))
|
|
1547 |
argv = utils.InsertAtPos(argv, 2, shlex.split(env_args))
|
|
1556 | 1548 |
|
1557 | 1549 |
func, args_def, parser_opts, usage, description = commands[cmd] |
1558 | 1550 |
parser = OptionParser(option_list=parser_opts + COMMON_OPTS, |
... | ... | |
1560 | 1552 |
formatter=TitledHelpFormatter(), |
1561 | 1553 |
usage="%%prog %s %s" % (cmd, usage)) |
1562 | 1554 |
parser.disable_interspersed_args() |
1563 |
options, args = parser.parse_args(args=argv[1:])
|
|
1555 |
options, args = parser.parse_args(args=argv[2:])
|
|
1564 | 1556 |
|
1565 | 1557 |
if not _CheckArguments(cmd, args_def, args): |
1566 | 1558 |
return None, None, None |
... | ... | |
1568 | 1560 |
return func, options, args |
1569 | 1561 |
|
1570 | 1562 |
|
1563 |
def _FormatUsage(binary, commands): |
|
1564 |
"""Generates a nice description of all commands. |
|
1565 |
|
|
1566 |
@param binary: Script name |
|
1567 |
@param commands: Dictionary containing command definitions |
|
1568 |
|
|
1569 |
""" |
|
1570 |
# compute the max line length for cmd + usage |
|
1571 |
mlen = min(60, max(map(len, commands))) |
|
1572 |
|
|
1573 |
yield "Usage: %s {command} [options...] [argument...]" % binary |
|
1574 |
yield "%s <command> --help to see details, or man %s" % (binary, binary) |
|
1575 |
yield "" |
|
1576 |
yield "Commands:" |
|
1577 |
|
|
1578 |
# and format a nice command list |
|
1579 |
for (cmd, (_, _, _, _, help_text)) in sorted(commands.items()): |
|
1580 |
help_lines = textwrap.wrap(help_text, 79 - 3 - mlen) |
|
1581 |
yield " %-*s - %s" % (mlen, cmd, help_lines.pop(0)) |
|
1582 |
for line in help_lines: |
|
1583 |
yield " %-*s %s" % (mlen, "", line) |
|
1584 |
|
|
1585 |
yield "" |
|
1586 |
|
|
1587 |
|
|
1571 | 1588 |
def _CheckArguments(cmd, args_def, args): |
1572 | 1589 |
"""Verifies the arguments using the argument definition. |
1573 | 1590 |
|
... | ... | |
2242 | 2259 |
aliases = {} |
2243 | 2260 |
|
2244 | 2261 |
try: |
2245 |
func, options, args = _ParseArgs(sys.argv, commands, aliases, env_override) |
|
2262 |
(func, options, args) = _ParseArgs(binary, sys.argv, commands, aliases, |
|
2263 |
env_override) |
|
2264 |
except _ShowVersion: |
|
2265 |
ToStdout("%s (ganeti %s) %s", binary, constants.VCS_VERSION, |
|
2266 |
constants.RELEASE_VERSION) |
|
2267 |
return constants.EXIT_SUCCESS |
|
2268 |
except _ShowUsage, err: |
|
2269 |
for line in _FormatUsage(binary, commands): |
|
2270 |
ToStdout(line) |
|
2271 |
|
|
2272 |
if err.exit_error: |
|
2273 |
return constants.EXIT_FAILURE |
|
2274 |
else: |
|
2275 |
return constants.EXIT_SUCCESS |
|
2246 | 2276 |
except errors.ParameterError, err: |
2247 | 2277 |
result, err_msg = FormatError(err) |
2248 | 2278 |
ToStderr(err_msg) |
Also available in: Unified diff