Revision 56ccdbee kamaki/cli.py
b/kamaki/cli.py | ||
---|---|---|
71 | 71 |
import inspect |
72 | 72 |
import logging |
73 | 73 |
import os |
74 |
import sys |
|
74 | 75 |
|
76 |
from argparse import ArgumentParser |
|
75 | 77 |
from base64 import b64encode |
76 | 78 |
from grp import getgrgid |
77 |
from optparse import OptionParser |
|
78 | 79 |
from os.path import abspath, basename, exists |
79 | 80 |
from pwd import getpwuid |
80 |
from sys import argv, exit, stdout, stderr
|
|
81 |
from sys import exit, stdout, stderr |
|
81 | 82 |
|
82 |
from clint import args |
|
83 | 83 |
from colors import magenta, red, yellow |
84 | 84 |
from progress.bar import IncrementalBar |
85 | 85 |
from requests.exceptions import ConnectionError |
... | ... | |
146 | 146 |
"""List configuration options""" |
147 | 147 |
|
148 | 148 |
def update_parser(self, parser): |
149 |
parser.add_option('-a', dest='all', action='store_true',
|
|
149 |
parser.add_argument('-a', dest='all', action='store_true',
|
|
150 | 150 |
default=False, help='include default values') |
151 |
|
|
151 |
|
|
152 | 152 |
def main(self): |
153 |
include_defaults = self.options.all
|
|
153 |
include_defaults = self.args.all
|
|
154 | 154 |
for section in sorted(self.config.sections()): |
155 | 155 |
items = self.config.items(section, include_defaults) |
156 | 156 |
for key, val in sorted(items): |
... | ... | |
196 | 196 |
"""List servers""" |
197 | 197 |
|
198 | 198 |
def update_parser(self, parser): |
199 |
parser.add_option('-l', dest='detail', action='store_true',
|
|
199 |
parser.add_argument('-l', dest='detail', action='store_true',
|
|
200 | 200 |
default=False, help='show detailed output') |
201 |
|
|
201 |
|
|
202 | 202 |
def main(self): |
203 |
servers = self.client.list_servers(self.options.detail)
|
|
203 |
servers = self.client.list_servers(self.args.detail)
|
|
204 | 204 |
print_items(servers) |
205 | 205 |
|
206 | 206 |
|
... | ... | |
218 | 218 |
"""Create a server""" |
219 | 219 |
|
220 | 220 |
def update_parser(self, parser): |
221 |
parser.add_option('--personality', dest='personalities',
|
|
221 |
parser.add_argument('--personality', dest='personalities',
|
|
222 | 222 |
action='append', default=[], |
223 | 223 |
metavar='PATH[,SERVER PATH[,OWNER[,GROUP,[MODE]]]]', |
224 | 224 |
help='add a personality file') |
225 |
parser.epilog = "If missing, optional personality values will be " \
|
|
226 |
"filled based on the file at PATH." |
|
227 |
|
|
225 |
parser.epilog = ("If missing, optional personality values will be "
|
|
226 |
"filled based on the file at PATH.")
|
|
227 |
|
|
228 | 228 |
def main(self, name, flavor_id, image_id): |
229 | 229 |
personalities = [] |
230 |
for personality in self.options.personalities:
|
|
230 |
for personality in self.args.personalities:
|
|
231 | 231 |
p = personality.split(',') |
232 | 232 |
p.extend([None] * (5 - len(p))) # Fill missing fields with None |
233 | 233 |
|
... | ... | |
277 | 277 |
"""Reboot a server""" |
278 | 278 |
|
279 | 279 |
def update_parser(self, parser): |
280 |
parser.add_option('-f', dest='hard', action='store_true',
|
|
280 |
parser.add_argument('-f', dest='hard', action='store_true',
|
|
281 | 281 |
default=False, help='perform a hard reboot') |
282 |
|
|
282 |
|
|
283 | 283 |
def main(self, server_id): |
284 |
self.client.reboot_server(int(server_id), self.options.hard)
|
|
284 |
self.client.reboot_server(int(server_id), self.args.hard)
|
|
285 | 285 |
|
286 | 286 |
|
287 | 287 |
@command(api='cyclades') |
... | ... | |
377 | 377 |
"""List flavors""" |
378 | 378 |
|
379 | 379 |
def update_parser(self, parser): |
380 |
parser.add_option('-l', dest='detail', action='store_true',
|
|
380 |
parser.add_argument('-l', dest='detail', action='store_true',
|
|
381 | 381 |
default=False, help='show detailed output') |
382 |
|
|
382 |
|
|
383 | 383 |
def main(self): |
384 |
flavors = self.client.list_flavors(self.options.detail)
|
|
384 |
flavors = self.client.list_flavors(self.args.detail)
|
|
385 | 385 |
print_items(flavors) |
386 | 386 |
|
387 | 387 |
|
... | ... | |
399 | 399 |
"""List images""" |
400 | 400 |
|
401 | 401 |
def update_parser(self, parser): |
402 |
parser.add_option('-l', dest='detail', action='store_true',
|
|
402 |
parser.add_argument('-l', dest='detail', action='store_true',
|
|
403 | 403 |
default=False, help='show detailed output') |
404 |
|
|
404 |
|
|
405 | 405 |
def main(self): |
406 |
images = self.client.list_images(self.options.detail)
|
|
406 |
images = self.client.list_images(self.args.detail)
|
|
407 | 407 |
print_items(images) |
408 | 408 |
|
409 | 409 |
|
... | ... | |
465 | 465 |
"""List networks""" |
466 | 466 |
|
467 | 467 |
def update_parser(self, parser): |
468 |
parser.add_option('-l', dest='detail', action='store_true',
|
|
468 |
parser.add_argument('-l', dest='detail', action='store_true',
|
|
469 | 469 |
default=False, help='show detailed output') |
470 |
|
|
470 |
|
|
471 | 471 |
def main(self): |
472 |
networks = self.client.list_networks(self.options.detail)
|
|
472 |
networks = self.client.list_networks(self.args.detail)
|
|
473 | 473 |
print_items(networks) |
474 | 474 |
|
475 | 475 |
|
... | ... | |
528 | 528 |
"""List public images""" |
529 | 529 |
|
530 | 530 |
def update_parser(self, parser): |
531 |
parser.add_option('-l', dest='detail', action='store_true',
|
|
531 |
parser.add_argument('-l', dest='detail', action='store_true',
|
|
532 | 532 |
default=False, help='show detailed output') |
533 |
parser.add_option('--container-format', dest='container_format',
|
|
533 |
parser.add_argument('--container-format', dest='container_format',
|
|
534 | 534 |
metavar='FORMAT', help='filter by container format') |
535 |
parser.add_option('--disk-format', dest='disk_format',
|
|
535 |
parser.add_argument('--disk-format', dest='disk_format',
|
|
536 | 536 |
metavar='FORMAT', help='filter by disk format') |
537 |
parser.add_option('--name', dest='name', metavar='NAME',
|
|
537 |
parser.add_argument('--name', dest='name', metavar='NAME',
|
|
538 | 538 |
help='filter by name') |
539 |
parser.add_option('--size-min', dest='size_min', metavar='BYTES',
|
|
539 |
parser.add_argument('--size-min', dest='size_min', metavar='BYTES',
|
|
540 | 540 |
help='filter by minimum size') |
541 |
parser.add_option('--size-max', dest='size_max', metavar='BYTES',
|
|
541 |
parser.add_argument('--size-max', dest='size_max', metavar='BYTES',
|
|
542 | 542 |
help='filter by maximum size') |
543 |
parser.add_option('--status', dest='status', metavar='STATUS',
|
|
543 |
parser.add_argument('--status', dest='status', metavar='STATUS',
|
|
544 | 544 |
help='filter by status') |
545 |
parser.add_option('--order', dest='order', metavar='FIELD',
|
|
545 |
parser.add_argument('--order', dest='order', metavar='FIELD',
|
|
546 | 546 |
help='order by FIELD (use a - prefix to reverse order)') |
547 |
|
|
547 |
|
|
548 | 548 |
def main(self): |
549 | 549 |
filters = {} |
550 | 550 |
for filter in ('container_format', 'disk_format', 'name', 'size_min', |
551 | 551 |
'size_max', 'status'): |
552 |
val = getattr(self.options, filter, None)
|
|
552 |
val = getattr(self.args, filter, None)
|
|
553 | 553 |
if val is not None: |
554 | 554 |
filters[filter] = val |
555 | 555 |
|
556 |
order = self.options.order or ''
|
|
557 |
images = self.client.list_public(self.options.detail, filters=filters,
|
|
556 |
order = self.args.order or ''
|
|
557 |
images = self.client.list_public(self.args.detail, filters=filters,
|
|
558 | 558 |
order=order) |
559 | 559 |
print_items(images, title=('name',)) |
560 | 560 |
|
... | ... | |
573 | 573 |
"""Register an image""" |
574 | 574 |
|
575 | 575 |
def update_parser(self, parser): |
576 |
parser.add_option('--checksum', dest='checksum', metavar='CHECKSUM',
|
|
576 |
parser.add_argument('--checksum', dest='checksum', metavar='CHECKSUM',
|
|
577 | 577 |
help='set image checksum') |
578 |
parser.add_option('--container-format', dest='container_format',
|
|
578 |
parser.add_argument('--container-format', dest='container_format',
|
|
579 | 579 |
metavar='FORMAT', help='set container format') |
580 |
parser.add_option('--disk-format', dest='disk_format',
|
|
580 |
parser.add_argument('--disk-format', dest='disk_format',
|
|
581 | 581 |
metavar='FORMAT', help='set disk format') |
582 |
parser.add_option('--id', dest='id',
|
|
582 |
parser.add_argument('--id', dest='id',
|
|
583 | 583 |
metavar='ID', help='set image ID') |
584 |
parser.add_option('--owner', dest='owner',
|
|
584 |
parser.add_argument('--owner', dest='owner',
|
|
585 | 585 |
metavar='USER', help='set image owner (admin only)') |
586 |
parser.add_option('--property', dest='properties', action='append',
|
|
586 |
parser.add_argument('--property', dest='properties', action='append',
|
|
587 | 587 |
metavar='KEY=VAL', |
588 | 588 |
help='add a property (can be used multiple times)') |
589 |
parser.add_option('--public', dest='is_public', action='store_true',
|
|
589 |
parser.add_argument('--public', dest='is_public', action='store_true',
|
|
590 | 590 |
help='mark image as public') |
591 |
parser.add_option('--size', dest='size', metavar='SIZE',
|
|
591 |
parser.add_argument('--size', dest='size', metavar='SIZE',
|
|
592 | 592 |
help='set image size') |
593 |
|
|
593 |
|
|
594 | 594 |
def main(self, name, location): |
595 | 595 |
if not location.startswith('pithos://'): |
596 | 596 |
account = self.config.get('storage', 'account') |
... | ... | |
600 | 600 |
params = {} |
601 | 601 |
for key in ('checksum', 'container_format', 'disk_format', 'id', |
602 | 602 |
'owner', 'size'): |
603 |
val = getattr(self.options, key)
|
|
603 |
val = getattr(self.args, key)
|
|
604 | 604 |
if val is not None: |
605 | 605 |
params[key] = val |
606 | 606 |
|
607 |
if self.options.is_public:
|
|
607 |
if self.args.is_public:
|
|
608 | 608 |
params['is_public'] = 'true' |
609 | 609 |
|
610 | 610 |
properties = {} |
611 |
for property in self.options.properties or []:
|
|
611 |
for property in self.args.properties or []:
|
|
612 | 612 |
key, sep, val = property.partition('=') |
613 | 613 |
if not sep: |
614 | 614 |
print("Invalid property '%s'" % property) |
... | ... | |
666 | 666 |
"""Base class for account level storage commands""" |
667 | 667 |
|
668 | 668 |
def update_parser(self, parser): |
669 |
parser.add_option('--account', dest='account', metavar='NAME',
|
|
669 |
parser.add_argument('--account', dest='account', metavar='NAME',
|
|
670 | 670 |
help="Specify an account to use") |
671 |
|
|
671 |
|
|
672 | 672 |
def progress(self, message): |
673 | 673 |
"""Return a generator function to be used for progress tracking""" |
674 | 674 |
|
... | ... | |
683 | 683 |
return progress_gen |
684 | 684 |
|
685 | 685 |
def main(self): |
686 |
if self.options.account is not None:
|
|
687 |
self.client.account = self.options.account
|
|
686 |
if self.args.account is not None:
|
|
687 |
self.client.account = self.args.account
|
|
688 | 688 |
|
689 | 689 |
|
690 | 690 |
class _store_container_command(_store_account_command): |
... | ... | |
692 | 692 |
|
693 | 693 |
def update_parser(self, parser): |
694 | 694 |
super(_store_container_command, self).update_parser(parser) |
695 |
parser.add_option('--container', dest='container', metavar='NAME',
|
|
695 |
parser.add_argument('--container', dest='container', metavar='NAME',
|
|
696 | 696 |
help="Specify a container to use") |
697 |
|
|
697 |
|
|
698 | 698 |
def main(self): |
699 | 699 |
super(_store_container_command, self).main() |
700 |
if self.options.container is not None:
|
|
701 |
self.client.container = self.options.container
|
|
700 |
if self.args.container is not None:
|
|
701 |
self.client.container = self.args.container
|
|
702 | 702 |
|
703 | 703 |
|
704 | 704 |
@command(api='storage') |
... | ... | |
838 | 838 |
|
839 | 839 |
|
840 | 840 |
def main(): |
841 |
parser = OptionParser(add_help_option=False) |
|
842 |
parser.usage = '%prog <group> <command> [options]' |
|
843 |
parser.add_option('-h', '--help', dest='help', action='store_true', |
|
841 |
exe = basename(sys.argv[0]) |
|
842 |
parser = ArgumentParser(add_help=False) |
|
843 |
parser.prog = '%s <group> <command>' % exe |
|
844 |
parser.add_argument('-h', '--help', dest='help', action='store_true', |
|
844 | 845 |
default=False, |
845 | 846 |
help="Show this help message and exit") |
846 |
parser.add_option('--config', dest='config', metavar='PATH',
|
|
847 |
parser.add_argument('--config', dest='config', metavar='PATH',
|
|
847 | 848 |
help="Specify the path to the configuration file") |
848 |
parser.add_option('-d', '--debug', dest='debug', action='store_true',
|
|
849 |
parser.add_argument('-d', '--debug', dest='debug', action='store_true',
|
|
849 | 850 |
default=False, |
850 | 851 |
help="Include debug output") |
851 |
parser.add_option('-i', '--include', dest='include', action='store_true',
|
|
852 |
parser.add_argument('-i', '--include', dest='include', action='store_true',
|
|
852 | 853 |
default=False, |
853 | 854 |
help="Include protocol headers in the output") |
854 |
parser.add_option('-s', '--silent', dest='silent', action='store_true',
|
|
855 |
parser.add_argument('-s', '--silent', dest='silent', action='store_true',
|
|
855 | 856 |
default=False, |
856 | 857 |
help="Silent mode, don't output anything") |
857 |
parser.add_option('-v', '--verbose', dest='verbose', action='store_true',
|
|
858 |
parser.add_argument('-v', '--verbose', dest='verbose', action='store_true',
|
|
858 | 859 |
default=False, |
859 | 860 |
help="Make the operation more talkative") |
860 |
parser.add_option('-V', '--version', dest='version', action='store_true',
|
|
861 |
parser.add_argument('-V', '--version', dest='version', action='store_true',
|
|
861 | 862 |
default=False, |
862 | 863 |
help="Show version number and quit") |
863 |
parser.add_option('-o', dest='options', action='append',
|
|
864 |
parser.add_argument('-o', dest='options', action='append',
|
|
864 | 865 |
default=[], metavar="KEY=VAL", |
865 | 866 |
help="Override a config values") |
866 |
|
|
867 |
if args.contains(['-V', '--version']): |
|
867 |
|
|
868 |
args, argv = parser.parse_known_args() |
|
869 |
|
|
870 |
if args.version: |
|
868 | 871 |
import kamaki |
869 | 872 |
print("kamaki %s" % kamaki.__version__) |
870 | 873 |
exit(0) |
871 |
|
|
872 |
if '--config' in args: |
|
873 |
config = Config(args.grouped['--config'].get(0)) |
|
874 |
else: |
|
875 |
config = Config() |
|
876 | 874 |
|
877 |
for option in args.grouped.get('-o', []): |
|
875 |
config = Config(args.config) if args.config else Config() |
|
876 |
|
|
877 |
for option in args.options: |
|
878 | 878 |
keypath, sep, val = option.partition('=') |
879 | 879 |
if not sep: |
880 | 880 |
print("Invalid option '%s'" % option) |
... | ... | |
901 | 901 |
del group_commands[name] |
902 | 902 |
if not group_commands: |
903 | 903 |
del _commands[group] |
904 |
|
|
905 |
if not args.grouped['_']: |
|
904 |
|
|
905 |
group = argv.pop(0) if argv else None |
|
906 |
|
|
907 |
if not group: |
|
906 | 908 |
parser.print_help() |
907 | 909 |
print_groups() |
908 | 910 |
exit(0) |
909 |
|
|
910 |
group = args.grouped['_'][0] |
|
911 |
|
|
911 |
|
|
912 | 912 |
if group not in _commands: |
913 | 913 |
parser.print_help() |
914 | 914 |
print_groups() |
915 | 915 |
exit(1) |
916 |
|
|
917 |
parser.usage = '%%prog %s <command> [options]' % group |
|
918 |
|
|
919 |
if len(args.grouped['_']) == 1: |
|
916 |
|
|
917 |
parser.prog = '%s %s <command>' % (exe, group) |
|
918 |
command = argv.pop(0) if argv else None |
|
919 |
|
|
920 |
if not command: |
|
920 | 921 |
parser.print_help() |
921 | 922 |
print_commands(group) |
922 | 923 |
exit(0) |
923 |
|
|
924 |
name = args.grouped['_'][1] |
|
925 |
|
|
926 |
if name not in _commands[group]: |
|
924 |
|
|
925 |
if command not in _commands[group]: |
|
927 | 926 |
parser.print_help() |
928 | 927 |
print_commands(group) |
929 | 928 |
exit(1) |
930 | 929 |
|
931 |
cmd = _commands[group][name]() |
|
932 |
|
|
933 |
syntax = '%s [options]' % cmd.syntax if cmd.syntax else '[options]' |
|
934 |
parser.usage = '%%prog %s %s %s' % (group, name, syntax) |
|
930 |
cmd = _commands[group][command]() |
|
931 |
|
|
932 |
parser.prog = '%s %s %s' % (exe, group, command) |
|
933 |
if cmd.syntax: |
|
934 |
parser.prog += ' %s' % cmd.syntax |
|
935 | 935 |
parser.description = cmd.description |
936 | 936 |
parser.epilog = '' |
937 | 937 |
if hasattr(cmd, 'update_parser'): |
938 | 938 |
cmd.update_parser(parser) |
939 | 939 |
|
940 |
options, arguments = parser.parse_args(argv)
|
|
940 |
args, argv = parser.parse_known_args()
|
|
941 | 941 |
|
942 |
if options.help:
|
|
942 |
if args.help:
|
|
943 | 943 |
parser.print_help() |
944 | 944 |
exit(0) |
945 | 945 |
|
946 |
if options.silent:
|
|
946 |
if args.silent:
|
|
947 | 947 |
add_handler('', logging.CRITICAL) |
948 |
elif options.debug:
|
|
948 |
elif args.debug:
|
|
949 | 949 |
add_handler('requests', logging.INFO, prefix='* ') |
950 | 950 |
add_handler('clients.send', logging.DEBUG, prefix='> ') |
951 | 951 |
add_handler('clients.recv', logging.DEBUG, prefix='< ') |
952 |
elif options.verbose:
|
|
952 |
elif args.verbose:
|
|
953 | 953 |
add_handler('requests', logging.INFO, prefix='* ') |
954 | 954 |
add_handler('clients.send', logging.INFO, prefix='> ') |
955 | 955 |
add_handler('clients.recv', logging.INFO, prefix='< ') |
956 |
elif options.include:
|
|
956 |
elif args.include:
|
|
957 | 957 |
add_handler('clients.recv', logging.INFO) |
958 | 958 |
else: |
959 | 959 |
add_handler('', logging.WARNING) |
... | ... | |
984 | 984 |
token = config.get('astakos', 'token') or config.get('global', 'token') |
985 | 985 |
cmd.client = clients.astakos(url, token) |
986 | 986 |
|
987 |
cmd.options = options
|
|
987 |
cmd.args = args
|
|
988 | 988 |
cmd.config = config |
989 | 989 |
|
990 | 990 |
try: |
991 |
ret = cmd.main(*arguments[3:])
|
|
991 |
ret = cmd.main(*argv[2:])
|
|
992 | 992 |
exit(ret) |
993 | 993 |
except TypeError as e: |
994 | 994 |
if e.args and e.args[0].startswith('main()'): |
... | ... | |
998 | 998 |
raise |
999 | 999 |
except clients.ClientError as err: |
1000 | 1000 |
if err.status == 404: |
1001 |
color = yellow
|
|
1001 |
message = yellow(err.message)
|
|
1002 | 1002 |
elif 500 <= err.status < 600: |
1003 |
color = magenta
|
|
1003 |
message = magenta(err.message)
|
|
1004 | 1004 |
else: |
1005 |
color = red
|
|
1005 |
message = red(err.message)
|
|
1006 | 1006 |
|
1007 |
print(color(err.message), file=stderr)
|
|
1008 |
if err.details and (options.verbose or options.debug):
|
|
1007 |
print(message, file=stderr)
|
|
1008 |
if err.details and (args.verbose or args.debug):
|
|
1009 | 1009 |
print(err.details, file=stderr) |
1010 | 1010 |
exit(2) |
1011 | 1011 |
except ConnectionError as err: |
Also available in: Unified diff