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:
|