Revision 8378faf2 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