Revision f3e94e06

b/kamaki/cli/__init__.py
43 43

  
44 44
from inspect import getargspec
45 45
from argparse import ArgumentParser, ArgumentError
46
from base64 import b64encode
47
from os.path import abspath, basename, exists
46
from os.path import basename
48 47
from sys import exit, stdout, stderr, argv
49 48

  
50 49
try:
......
268 267
        if group is None:
269 268
            parser.print_help()
270 269
            shallow_load()
271
            print_commands(full_depth=_verbose)
270
            print_commands(full_depth=_debug)
272 271
            exit(0)
273 272

  
274 273
        cmd = load_command(group, unparsed)
......
294 293
            allow_subclass_signatures = True
295 294
            load_command(group, cmd.path.split('_')[1:], reload_package=True)
296 295

  
297
            print_commands(cmd.path, full_depth=_verbose)
296
            print_commands(cmd.path, full_depth=_debug)
298 297
            exit(0)
299 298

  
300 299
        setup_logging(silent=_arguments['silent'].value, debug=_debug, verbose=_verbose,
b/kamaki/cli/argument.py
200 200
                    lines.append(line)
201 201
        return lines
202 202

  
203
class KeyValueArgument(ValueArgument):
204
    def __init__(self, help='', parsed_name=None, default={}):
205
        super(KeyValueArgument,self).__init__(help, parsed_name, default)
206

  
207
    @property 
208
    def value(self):
209
        return super(KeyValueArgument, self).value
210
    @value.setter 
211
    def value(self, keyvalue_pairs):
212
        if keyvalue_pairs == self.default:
213
            return
214
        if isinstance(keyvalue_pairs, str):
215
            keyvalue_pairs = keyvalue_pairs.trim().split(' ')
216
        self._value = self.default
217
        for pair in keyvalue_pairs:
218
            key,sep,val = pair.partition('=')
219
            if not sep:
220
                raise CLISyntaxError(details='Missing "="" ( "key1=val1 key2=val2 ..."')
221
            self._value[key.trim()] = val.trim()
222

  
203 223
_arguments = dict(config = _config_arg, help = Argument(0, 'Show help message', ('-h', '--help')),
204 224
    debug = FlagArgument('Include debug output', ('-d', '--debug')),
205 225
    include = FlagArgument('Include protocol headers in the output', ('-i', '--include')),
206 226
    silent = FlagArgument('Do not output anything', ('-s', '--silent')),
207 227
    verbose = FlagArgument('More info at response', ('-v', '--verbose')),
208 228
    version = VersionArgument('Print current version', ('-V', '--version')),
209
    options = CmdLineConfigArgument(_config_arg, 'Override a config vale', ('-o', '--options')),
229
    options = CmdLineConfigArgument(_config_arg, 'Override a config value', ('-o', '--options')),
210 230
    history = HistoryArgument('Show user (prefixed) history', '--history')
211 231
)
212 232

  
b/kamaki/cli/commands/cyclades_cli.py
31 31
# interpreted as representing official policies, either expressed
32 32
# or implied, of GRNET S.A.
33 33

  
34
from kamaki.cli import command#, set_api_description
35
from kamaki.cli.utils import print_dict, print_items, print_list, format_size, bold
36
from kamaki.cli.errors import CLIError, raiseCLIError
37
#set_api_description('server', "Compute/Cyclades API server commands")
38
#set_api_description('flavor', "'Compute/Cyclades API flavor commands'")
39
#set_api_description('image', "Compute/Cyclades or Glance API image commands")
40
#set_api_description('network', "Compute/Cyclades API network commands")
41 34
API_DESCRIPTION = {'server':'Compute/Cyclades API server commands',
42 35
    'flavor':'Compute/Cyclades API flavor commands',
43 36
    'image':'Compute/Cyclades or Glance API image commands',
44 37
    'network': 'Compute/Cyclades API network commands'}
38

  
39
from kamaki.cli import command
40
from kamaki.cli.utils import print_dict, print_items, print_list, format_size, bold
41
from kamaki.cli.errors import CLIError, raiseCLIError
45 42
from kamaki.clients.cyclades import CycladesClient, ClientError
43
from kamaki.cli.argument import FlagArgument, ValueArgument
44
from base64 import b64encode
45
from os.path import abspath, exists
46 46

  
47 47
class _init_cyclades(object):
48
    def main(self):
49
        token = self.config.get('compute', 'token') or self.config.get('global', 'token')
50
        base_url = self.config.get('compute', 'url') or self.config.get('global', 'url')
48
    def __init__(self, arguments={}):
49
        self.arguments=arguments
50
        try:
51
            self.config = self.get_argument('config')
52
        except KeyError:
53
            pass
54

  
55
    def get_argument(self, argterm):
56
        return self.arguments[argterm].value
57

  
58
    def main(self, service='compute'):
59
        token = self.config.get(service, 'token') or self.config.get('global', 'token')
60
        base_url = self.config.get(service, 'url') or self.config.get('global', 'url')
51 61
        self.client = CycladesClient(base_url=base_url, token=token)
52 62

  
63
class _init_server(_init_cyclades):
64
    def main(self):
65
        super(_init_server, self).main('server')
66

  
53 67
@command()
54 68
class server_list(_init_cyclades):
55 69
    """List servers"""
56 70

  
71
    def __init__(self, arguments={}):
72
        super(server_list, self).__init__(arguments)
73
        self.arguments['detail'] = FlagArgument('show detailed output', '-l')
74

  
57 75
    def _print(self, servers):
58 76
        for server in servers:
59 77
            sname = server.pop('name')
60 78
            sid = server.pop('id')
61 79
            print('%s (%s)'%(bold(sname), bold(unicode(sid))))
62
            if getattr(self.args, 'detail'):
80
            if self.get_argument('detail'):
63 81
                server_info._print(server)
64 82
                print('- - -')
65 83

  
66
    def update_parser(self, parser):
67
        parser.add_argument('-l', dest='detail', action='store_true',
68
                default=False, help='show detailed output')
69

  
70 84
    def main(self):
71 85
        super(self.__class__, self).main()
72 86
        try:
73
            servers = self.client.list_servers(self.args.detail)
87
            servers = self.client.list_servers(self.get_argument('detail'))
74 88
            self._print(servers)
75 89
            #print_items(servers)
76 90
        except ClientError as err:
......
107 121
                importance=1)
108 122
        self._print(server)
109 123

  
124
class PersonalityArgument(ValueArgument):
125
    @property 
126
    def value(self):
127
        return [self._value] if hasattr(self, '_value') else []
128
    @value.setter 
129
    def value(self, newvalue):
130
        if newvalue == self.default:
131
            return self.value
132
        termlist = newvalue.split()
133
        if len(termlist) > 4:
134
                raise CLISyntaxError(details='Wrong number of personality terms ("PATH [OWNER [GROUP [MODE]]]"')
135
        path = termlist[0]
136
        self._value = dict(path=path)
137
        if not exists(path):
138
            raise CLIError(message="File %s does not exist" % path, importance=1)
139
        with open(path) as f:
140
            self._value['contents'] = b64encode(f.read())
141
        try:
142
            self._value['owner'] = termlist[1]
143
            self._value['group'] = termlist[2]
144
            self._value['mode'] = termlist[3]
145
        except IndexError:
146
            pass
147

  
110 148
@command()
111 149
class server_create(_init_cyclades):
112 150
    """Create a server"""
113 151

  
152
    def __init__(self, arguments={}):
153
        super(server_create, self).__init__(arguments)
154
        self.arguments['personality'] = PersonalityArgument(parsed_name='--personality',
155
            help='add a personality file ( "PATH [OWNER [GROUP [MODE]]]" )')
156

  
114 157
    def update_parser(self, parser):
115 158
        parser.add_argument('--personality', dest='personalities',
116 159
                          action='append', default=[],
......
119 162

  
120 163
    def main(self, name, flavor_id, image_id):
121 164
        super(self.__class__, self).main()
122
        personalities = []
123
        for personality in self.args.personalities:
124
            p = personality.split(',')
125
            p.extend([None] * (5 - len(p)))     # Fill missing fields with None
126

  
127
            path = p[0]
128

  
129
            if not path:
130
                raise CLIError(message='Invalid personality argument %s'%p, importance=1)
131
            if not exists(path):
132
                raise CLIError(message="File %s does not exist" % path, importance=1)
133

  
134
            with open(path) as f:
135
                contents = b64encode(f.read())
136

  
137
            d = {'path': p[1] or abspath(path), 'contents': contents}
138
            if p[2]:
139
                d['owner'] = p[2]
140
            if p[3]:
141
                d['group'] = p[3]
142
            if p[4]:
143
                d['mode'] = int(p[4])
144
            personalities.append(d)
145

  
146 165
        try:
147 166
            reply = self.client.create_server(name, int(flavor_id), image_id,
148
                personalities)
167
                self.get_argument('personality'))
149 168
        except ClientError as err:
150 169
            raiseCLIError(err)
151 170
        print_dict(reply)
......
180 199
class server_reboot(_init_cyclades):
181 200
    """Reboot a server"""
182 201

  
183
    def update_parser(self, parser):
184
        parser.add_argument('-f', dest='hard', action='store_true',
185
                default=False, help='perform a hard reboot')
202
    def __init__(self, arguments={}):
203
        super(server_reboot, self).__init__(arguments)
204
        self.arguments['hard'] = FlagArgument('perform a hard reboot', '-f')
186 205

  
187 206
    def main(self, server_id):
188 207
        super(self.__class__, self).main()
189 208
        try:
190
            self.client.reboot_server(int(server_id), self.args.hard)
209
            self.client.reboot_server(int(server_id), self.get_argument('hard'))
191 210
        except ClientError as err:
192 211
            raiseCLIError(err)
193 212
        except ValueError:
......
245 264
            raiseCLIError(err)
246 265
        except ValueError:
247 266
            raise CLIError(message='Server id must be positive integer', importance=1)
267

  
248 268
@command()
249 269
class server_addr(_init_cyclades):
250 270
    """List a server's nic address"""
......
333 353
class flavor_list(_init_cyclades):
334 354
    """List flavors"""
335 355

  
336
    def update_parser(self, parser):
337
        parser.add_argument('-l', dest='detail', action='store_true',
338
                default=False, help='show detailed output')
356
    def __init__(self, arguments={}):
357
        super(flavor_list, self).__init__(arguments)
358
        self.arguments['detail'] = FlagArgument('show detailed output', '-l')
339 359

  
340 360
    def main(self):
341 361
        super(self.__class__, self).main()
342 362
        try:
343
            flavors = self.client.list_flavors(self.args.detail)
363
            flavors = self.client.list_flavors(self.get_argument('detail'))
344 364
        except ClientError as err:
345 365
            raiseCLIError(err)
346 366
        print_items(flavors)
......
360 380
        print_dict(flavor)
361 381

  
362 382
@command()
363
class image_list(_init_cyclades):
364
    """List images"""
365

  
366
    def update_parser(self, parser):
367
        parser.add_argument('-l', dest='detail', action='store_true',
368
                default=False, help='show detailed output')
369

  
370
    def _print(self, images):
371
        for img in images:
372
            iname = img.pop('name')
373
            iid = img.pop('id')
374
            print('%s (%s)'%(bold(iname), bold(unicode(iid))))
375
            if getattr(self.args, 'detail'):
376
                image_info._print(img)
377
                print('- - -')
378

  
379
    def main(self):
380
        super(self.__class__, self).main()
381
        try:
382
            images = self.client.list_images(self.args.detail)
383
        except ClientError as err:
384
            raiseCLIError(err)
385
        #print_items(images)
386
        self._print(images)
387

  
388
@command()
389
class image_info(_init_cyclades):
390
    """Get image details"""
391

  
392
    @classmethod
393
    def _print(self, image):
394
        if image.has_key('metadata'):
395
            image['metadata'] = image['metadata']['values']
396
        print_dict(image, ident=14)
397

  
398
    def main(self, image_id):
399
        super(self.__class__, self).main()
400
        try:
401
            image = self.client.get_image_details(image_id)
402
        except ClientError as err:
403
            raiseCLIError(err)
404
        self._print(image)
405

  
406
@command()
407
class image_delete(_init_cyclades):
408
    """Delete image"""
409

  
410
    def main(self, image_id):
411
        super(self.__class__, self).main()
412
        try:
413
            self.client.delete_image(image_id)
414
        except ClientError as err:
415
            raiseCLIError(err)
416

  
417
@command()
418
class image_properties(_init_cyclades):
419
    """Get image properties"""
420

  
421
    def main(self, image_id, key=None):
422
        super(self.__class__, self).main()
423
        try:
424
            reply = self.client.get_image_metadata(image_id, key)
425
        except ClientError as err:
426
            raiseCLIError(err)
427
        print_dict(reply)
428

  
429
@command()
430
class image_addproperty(_init_cyclades):
431
    """Add an image property"""
432

  
433
    def main(self, image_id, key, val):
434
        super(self.__class__, self).main()
435
        try:
436
            reply = self.client.create_image_metadata(image_id, key, val)
437
        except ClientError as err:
438
            raiseCLIError(err)
439
        print_dict(reply)
440

  
441
@command()
442
class image_setproperty(_init_cyclades):
443
    """Update an image property"""
444

  
445
    def main(self, image_id, key, val):
446
        super(self.__class__, self).main()
447
        metadata = {key: val}
448
        try:
449
            reply = self.client.update_image_metadata(image_id, **metadata)
450
        except ClientError as err:
451
            raiseCLIError(err)
452
        print_dict(reply)
453

  
454
@command()
455
class image_delproperty(_init_cyclades):
456
    """Delete an image property"""
457

  
458
    def main(self, image_id, key):
459
        super(self.__class__, self).main()
460
        try:
461
            self.client.delete_image_metadata(image_id, key)
462
        except ClientError as err:
463
            raiseCLIError(err)
464

  
465
@command()
466 383
class network_list(_init_cyclades):
467 384
    """List networks"""
468 385

  
469
    def update_parser(self, parser):
470
        parser.add_argument('-l', dest='detail', action='store_true',
471
                default=False, help='show detailed output')
386
    def __init__(self, arguments={}):
387
        super(network_list, self).__init__(arguments)
388
        self.arguments['detail'] = FlagArgument('show detailed output', '-l')
472 389

  
473 390
    def print_networks(self, nets):
474 391
        for net in nets:
475 392
            netname = bold(net.pop('name'))
476 393
            netid = bold(unicode(net.pop('id')))
477 394
            print('%s (%s)'%(netname, netid))
478
            if getattr(self.args, 'detail'):
395
            if self.get_argument('detail'):
479 396
                network_info.print_network(net)
480
                print('- - -')
481 397

  
482 398
    def main(self):
483 399
        super(self.__class__, self).main()
484 400
        try:
485
            networks = self.client.list_networks(self.args.detail)
401
            networks = self.client.list_networks(self.get_argument('detail'))
486 402
        except ClientError as err:
487 403
            raiseCLIError(err)
488 404
        self.print_networks(networks)
......
491 407
class network_create(_init_cyclades):
492 408
    """Create a network"""
493 409

  
494
    def update_parser(self, parser):
495
        try:
496
            super(self.__class__, self).update_parser(parser)
497
        except AttributeError:
498
            pass
499
        parser.add_argument('--with-cidr', action='store', dest='cidr', default=False,
500
            help='specific cidr for new network')
501
        parser.add_argument('--with-gateway', action='store', dest='gateway', default=False,
502
            help='specific getaway for new network')
503
        parser.add_argument('--with-dhcp', action='store', dest='dhcp', default=False,
504
            help='specific dhcp for new network')
505
        parser.add_argument('--with-type', action='store', dest='type', default=False,
506
            help='specific type for new network')
410
    def __init__(self, arguments={}):
411
        super(network_create, self).__init__(arguments)
412
        self.arguments['cidr'] = ValueArgument('specific cidr for new network', '--with-cidr')
413
        self.arguments['gateway'] = ValueArgument('specific gateway for new network',
414
            '--with-gateway')
415
        self.arguments['dhcp'] = ValueArgument('specific dhcp for new network', '--with-dhcp')
416
        self.arguments['type'] = ValueArgument('specific type for new network', '--with-type')
507 417

  
508 418
    def main(self, name):
509 419
        super(self.__class__, self).main()
510 420
        try:
511
            reply = self.client.create_network(name, cidr=getattr(self.args, 'cidr'),
512
                gateway=getattr(self.args, 'gateway'), dhcp=getattr(self.args, 'gateway'),
513
                type=getattr(self.args, 'type'))
421
            reply = self.client.create_network(name, cidr= self.get_argument('cidr'),
422
                gateway=self.get_argument('gateway'), dhcp=self.get_argument('dhcp'),
423
                type=self.get_argument('type'))
514 424
        except ClientError as err:
515 425
            raiseCLIError(err)
516 426
        print_dict(reply)
b/kamaki/cli/commands/image_cli.py
31 31
# interpreted as representing official policies, either expressed
32 32
# or implied, of GRNET S.A.command
33 33

  
34
from kamaki.cli import command#, set_api_description
34
API_DESCRIPTION = {'image':'Compute/Cyclades or Glance API image commands'}
35

  
36
from kamaki.cli import command
35 37
from kamaki.cli.errors import raiseCLIError
36 38
from kamaki.cli.utils import print_dict, print_items
37
#set_api_description('image', "Compute/Cyclades or Glance API image commands")
38
API_DESCRIPTION = {'image':'Compute/Cyclades or Glance API image commands'}
39 39
from kamaki.clients.image import ImageClient, ClientError
40

  
40
from kamaki.cli.argument import FlagArgument, ValueArgument, KeyValueArgument, IntArgument
41
from .cyclades_cli import _init_cyclades
41 42

  
42 43
class _init_image(object):
44
    def __init__(self, arguments={}):
45
        self.arguments=arguments
46
        try:
47
            self.config = self.get_argument('config')
48
        except KeyError:
49
            pass
50

  
51
    def get_argument(self, arg_name):
52
        return self.arguments[arg_name].value
53

  
43 54
    def main(self):
44 55
        try:
45 56
            token = self.config.get('image', 'token') or self.config.get('global', 'token')
......
52 63
class image_public(_init_image):
53 64
    """List public images"""
54 65

  
55
    def update_parser(self, parser):
56
        parser.add_argument('-l', dest='detail', action='store_true',
57
                default=False, help='show detailed output')
58
        parser.add_argument('--container-format', dest='container_format',
59
                metavar='FORMAT', help='filter by container format')
60
        parser.add_argument('--disk-format', dest='disk_format',
61
                metavar='FORMAT', help='filter by disk format')
62
        parser.add_argument('--name', dest='name', metavar='NAME',
63
                help='filter by name')
64
        parser.add_argument('--size-min', dest='size_min', metavar='BYTES',
65
                help='filter by minimum size')
66
        parser.add_argument('--size-max', dest='size_max', metavar='BYTES',
67
                help='filter by maximum size')
68
        parser.add_argument('--status', dest='status', metavar='STATUS',
69
                help='filter by status')
70
        parser.add_argument('--order', dest='order', metavar='FIELD',
71
                help='order by FIELD (use a - prefix to reverse order)')
66
    def __init__(self, arguments={}):
67
        super(image_public, self).__init__(arguments)
68
        self.arguments['detail'] = FlagArgument('show detailed output', '-l')
69
        self.arguments['container_format'] = ValueArgument('filter by container format',
70
            '--container-format')
71
        self.arguments['disk_format'] = ValueArgument('filter by disk format', '--disk-format')
72
        self.arguments['name'] = ValueArgument('filter by name', '--name')
73
        self.arguments['size_min'] = IntArgument('filter by minimum size', '--size-min')
74
        self.arguments['size_max'] = IntArgument('filter by maximum size', '--size-max')
75
        self.arguments['status'] = ValueArgument('filter by status', '--status')
76
        self.arguments['order'] = ValueArgument('order by FIELD (use a - prefix to reverse order)',
77
            '--order', default='')
72 78

  
73 79
    def main(self):
74
    	super(self.__class__, self).main()
80
        super(self.__class__, self).main()
75 81
        filters = {}
76
        for filter in ('container_format', 'disk_format', 'name', 'size_min',
77
                       'size_max', 'status'):
78
            val = getattr(self.args, filter, None)
82
        for arg in ('container_format', 'disk_format', 'name', 'size_min', 'size_max', 'status'):
83
            val = self.get_argument(arg)
79 84
            if val is not None:
80
                filters[filter] = val
85
                filters[arg] = val
81 86

  
82
        order = self.args.order or ''
87
        order = self.get_argument('order')
83 88
        try:
84
            images = self.client.list_public(self.args.detail,
85
                filters=filters, order=order)
89
            images = self.client.list_public(self.get_argument('detail'), filters=filters,
90
                order=order)
86 91
        except ClientError as err:
87 92
            raiseCLIError(err)
88 93
        print_items(images, title=('name',))
......
92 97
    """Get image metadata"""
93 98

  
94 99
    def main(self, image_id):
95
    	super(self.__class__, self).main()
100
        super(self.__class__, self).main()
96 101
        try:
97 102
            image = self.client.get_meta(image_id)
98 103
        except ClientError as err:
......
103 108
class image_register(_init_image):
104 109
    """Register an image"""
105 110

  
106
    def update_parser(self, parser):
107
        parser.add_argument('--checksum', dest='checksum', metavar='CHECKSUM',
108
                help='set image checksum')
109
        parser.add_argument('--container-format', dest='container_format',
110
                metavar='FORMAT', help='set container format')
111
        parser.add_argument('--disk-format', dest='disk_format',
112
                metavar='FORMAT', help='set disk format')
113
        parser.add_argument('--id', dest='id',
114
                metavar='ID', help='set image ID')
115
        parser.add_argument('--owner', dest='owner',
116
                metavar='USER', help='set image owner (admin only)')
117
        parser.add_argument('--property', dest='properties', action='append',
118
                metavar='KEY=VAL',
119
                help='add a property (can be used multiple times)')
120
        parser.add_argument('--public', dest='is_public', action='store_true',
121
                help='mark image as public')
122
        parser.add_argument('--size', dest='size', metavar='SIZE',
123
                help='set image size')
111
    def __init__(self, arguments={}):
112
        super(image_register, self).__init__(arguments)
113
        self.arguments['checksum'] = ValueArgument('set image checksum', '--checksum')
114
        self.arguments['container_format'] = ValueArgument('set container format',
115
            '--container-format')
116
        self.arguments['disk_format'] = ValueArgument('set disk format', '--disk-format')
117
        self.arguments['id'] = ValueArgument('set image ID', '--id')
118
        self.arguments['owner'] = ValueArgument('set image owner (admin only)', '--owner')
119
        self.arguments['properties'] = KeyValueArgument(parsed_name='--properties',
120
            help = 'add properties in the form "key1=val1 key2=val2 ..."')
121
        self.arguments['is_public'] = FlagArgument('mark image as public', '--public')
122
        self.arguments['size'] = IntArgument('set image size', '--size')
124 123

  
125 124
    def main(self, name, location):
126
    	super(self.__class__, self).main()
125
        super(self.__class__, self).main()
127 126
        if not location.startswith('pithos://'):
128
            account = self.config.get('storage', 'account').split()[0]
127
            account = self.config.get('store', 'account') \
128
                or self.config.get('global', 'account')
129 129
            if account[-1] == '/':
130 130
                account = account[:-1]
131
            container = self.config.get('storage', 'container')
131
            container = self.config.get('store', 'container') \
132
                or self.config.get('global', 'container')
132 133
            location = 'pithos://%s/%s'%(account, location) \
133 134
                if container is None or len(container) == 0 \
134 135
                else 'pithos://%s/%s/%s' % (account, container, location)
135 136

  
136 137
        params = {}
137
        for key in ('checksum', 'container_format', 'disk_format', 'id',
138
                    'owner', 'size'):
139
            val = getattr(self.args, key)
138
        for key in ('checksum','container_format','disk_format','id','owner','size','is_public'):
139
            val = self.get_argument(key)
140 140
            if val is not None:
141 141
                params[key] = val
142 142

  
143
        if self.args.is_public:
144
            params['is_public'] = 'true'
145

  
146
        properties = {}
147
        for property in self.args.properties or []:
148
            key, sep, val = property.partition('=')
149
            if not sep:
150
                raise CLIError(message="Invalid property '%s'" % property, importance=1)
151
            properties[key.strip()] = val.strip()
152

  
153 143
        try:
154
            self.client.register(name, location, params, properties)
144
            self.client.register(name, location, params, self.get_argument('properties'))
155 145
        except ClientError as err:
156 146
            raiseCLIError(err)
157 147

  
......
160 150
    """Get image members"""
161 151

  
162 152
    def main(self, image_id):
163
    	super(self.__class__, self).main()
153
        super(self.__class__, self).main()
164 154
        try:
165 155
            members = self.client.list_members(image_id)
166 156
        except ClientError as err:
......
173 163
    """List shared images"""
174 164

  
175 165
    def main(self, member):
176
    	super(self.__class__, self).main()
166
        super(self.__class__, self).main()
177 167
        try:
178 168
            images = self.client.list_shared(member)
179 169
        except ClientError as err:
......
186 176
    """Add a member to an image"""
187 177

  
188 178
    def main(self, image_id, member):
189
    	super(self.__class__, self).main()
179
        super(self.__class__, self).main()
190 180
        try:
191 181
            self.client.add_member(image_id, member)
192 182
        except ClientError as err:
......
213 203
            self.client.set_members(image_id, member)
214 204
        except ClientError as err:
215 205
            raiseCLIError(err)
206

  
207

  
208
@command()
209
class image_list(_init_cyclades):
210
    """List images"""
211

  
212
    def __init__(self, arguments={}):
213
        super(image_list, self).__init__(arguments)
214
        self.arguments['detail'] = FlagArgument('show detailed output', '-l')
215

  
216
    def _print(self, images):
217
        for img in images:
218
            iname = img.pop('name')
219
            iid = img.pop('id')
220
            print('%s (%s)'%(bold(iname), bold(unicode(iid))))
221
            if self.get_argument('detail'):
222
                image_info._print(img)
223

  
224
    def main(self):
225
        super(self.__class__, self).main()
226
        try:
227
            images = self.client.list_images(self.get_argument('detail'))
228
        except ClientError as err:
229
            raiseCLIError(err)
230
        self._print(images)
231

  
232
@command()
233
class image_info(_init_cyclades):
234
    """Get image details"""
235

  
236
    @classmethod
237
    def _print(self, image):
238
        if image.has_key('metadata'):
239
            image['metadata'] = image['metadata']['values']
240
        print_dict(image, ident=14)
241

  
242
    def main(self, image_id):
243
        super(self.__class__, self).main()
244
        try:
245
            image = self.client.get_image_details(image_id)
246
        except ClientError as err:
247
            raiseCLIError(err)
248
        self._print(image)
249

  
250
@command()
251
class image_delete(_init_cyclades):
252
    """Delete image"""
253

  
254
    def main(self, image_id):
255
        super(self.__class__, self).main()
256
        try:
257
            self.client.delete_image(image_id)
258
        except ClientError as err:
259
            raiseCLIError(err)
260

  
261
@command()
262
class image_properties(_init_cyclades):
263
    """Get image properties"""
264

  
265
    def main(self, image_id, key=None):
266
        super(self.__class__, self).main()
267
        try:
268
            reply = self.client.get_image_metadata(image_id, key)
269
        except ClientError as err:
270
            raiseCLIError(err)
271
        print_dict(reply)
272

  
273
@command()
274
class image_addproperty(_init_cyclades):
275
    """Add an image property"""
276

  
277
    def main(self, image_id, key, val):
278
        super(self.__class__, self).main()
279
        try:
280
            reply = self.client.create_image_metadata(image_id, key, val)
281
        except ClientError as err:
282
            raiseCLIError(err)
283
        print_dict(reply)
284

  
285
@command()
286
class image_setproperty(_init_cyclades):
287
    """Update an image property"""
288

  
289
    def main(self, image_id, key, val):
290
        super(self.__class__, self).main()
291
        metadata = {key: val}
292
        try:
293
            reply = self.client.update_image_metadata(image_id, **metadata)
294
        except ClientError as err:
295
            raiseCLIError(err)
296
        print_dict(reply)
297

  
298
@command()
299
class image_delproperty(_init_cyclades):
300
    """Delete an image property"""
301

  
302
    def main(self, image_id, key):
303
        super(self.__class__, self).main()
304
        try:
305
            self.client.delete_image_metadata(image_id, key)
306
        except ClientError as err:
307
            raiseCLIError(err)

Also available in: Unified diff