Statistics
| Branch: | Tag: | Revision:

root / tools / store @ 4211443b

History | View | Annotate | Download (29.1 kB)

1
#!/usr/bin/env python
2

    
3
# Copyright 2011 GRNET S.A. All rights reserved.
4
# 
5
# Redistribution and use in source and binary forms, with or
6
# without modification, are permitted provided that the following
7
# conditions are met:
8
# 
9
#   1. Redistributions of source code must retain the above
10
#      copyright notice, this list of conditions and the following
11
#      disclaimer.
12
# 
13
#   2. Redistributions in binary form must reproduce the above
14
#      copyright notice, this list of conditions and the following
15
#      disclaimer in the documentation and/or other materials
16
#      provided with the distribution.
17
# 
18
# THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
19
# OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
20
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
21
# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR
22
# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
25
# USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
26
# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
28
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29
# POSSIBILITY OF SUCH DAMAGE.
30
# 
31
# The views and conclusions contained in the software and
32
# documentation are those of the authors and should not be
33
# interpreted as representing official policies, either expressed
34
# or implied, of GRNET S.A.
35

    
36
from getpass import getuser
37
from optparse import OptionParser
38
from os import environ
39
from sys import argv, exit, stdin, stdout
40
from pithos.lib.client import Pithos_Client, Fault
41
from datetime import datetime
42

    
43
import json
44
import logging
45
import types
46
import re
47
import time as _time
48
import os
49

    
50
#DEFAULT_HOST = 'pithos.dev.grnet.gr'
51
DEFAULT_HOST = '127.0.0.1:8000'
52
DEFAULT_API = 'v1'
53

    
54
_cli_commands = {}
55

    
56
def cli_command(*args):
57
    def decorator(cls):
58
        cls.commands = args
59
        for name in args:
60
            _cli_commands[name] = cls
61
        return cls
62
    return decorator
63

    
64
def class_for_cli_command(name):
65
    return _cli_commands[name]
66

    
67
class Command(object):
68
    syntax = ''
69
    
70
    def __init__(self, name, argv):
71
        parser = OptionParser('%%prog %s [options] %s' % (name, self.syntax))
72
        parser.add_option('--host', dest='host', metavar='HOST',
73
                          default=_get_server(), help='use server HOST')
74
        parser.add_option('--user', dest='user', metavar='USERNAME',
75
                          default=_get_user(),
76
                          help='use account USERNAME')
77
        parser.add_option('--token', dest='token', metavar='AUTH',
78
                          default=_get_auth(),
79
                          help='use account AUTH')
80
        parser.add_option('--api', dest='api', metavar='API',
81
                          default=DEFAULT_API, help='use api API')
82
        parser.add_option('-v', action='store_true', dest='verbose',
83
                          default=False, help='use verbose output')
84
        parser.add_option('-d', action='store_true', dest='debug',
85
                          default=False, help='use debug output')
86
        self.add_options(parser)
87
        options, args = parser.parse_args(argv)
88
        
89
        # Add options to self
90
        for opt in parser.option_list:
91
            key = opt.dest
92
            if key:
93
                val = getattr(options, key)
94
                setattr(self, key, val)
95
        
96
        self.client = Pithos_Client(self.host, self.token, self.user, self.api, self.verbose,
97
                             self.debug)
98
        
99
        self.parser = parser
100
        self.args = args
101
    
102
    def _build_args(self, attrs):
103
        args = {}
104
        for a in [a for a in attrs if getattr(self, a)]:
105
            args[a] = getattr(self, a)
106
        return args
107

    
108
    def add_options(self, parser):
109
        pass
110
    
111
    def execute(self, *args):
112
        pass
113

    
114
@cli_command('list', 'ls')
115
class List(Command):
116
    syntax = '[<container>[/<object>]]'
117
    description = 'list containers or objects'
118
    
119
    def add_options(self, parser):
120
        parser.add_option('-l', action='store_true', dest='detail',
121
                          default=False, help='show detailed output')
122
        parser.add_option('-n', action='store', type='int', dest='limit',
123
                          default=10000, help='show limited output')
124
        parser.add_option('--marker', action='store', type='str',
125
                          dest='marker', default=None,
126
                          help='show output greater then marker')
127
        parser.add_option('--prefix', action='store', type='str',
128
                          dest='prefix', default=None,
129
                          help='show output starting with prefix')
130
        parser.add_option('--delimiter', action='store', type='str',
131
                          dest='delimiter', default=None,
132
                          help='show output up to the delimiter')
133
        parser.add_option('--path', action='store', type='str',
134
                          dest='path', default=None,
135
                          help='show output starting with prefix up to /')
136
        parser.add_option('--meta', action='store', type='str',
137
                          dest='meta', default=None,
138
                          help='show output having the specified meta keys')
139
        parser.add_option('--if-modified-since', action='store', type='str',
140
                          dest='if_modified_since', default=None,
141
                          help='show output if modified since then')
142
        parser.add_option('--if-unmodified-since', action='store', type='str',
143
                          dest='if_unmodified_since', default=None,
144
                          help='show output if not modified since then')
145
        parser.add_option('--until', action='store', dest='until',
146
                          default=None, help='show metadata until that date')
147
        parser.add_option('--format', action='store', dest='format',
148
                          default='%d/%m/%Y', help='format to parse until date')
149
    
150
    def execute(self, container=None):
151
        if container:
152
            self.list_objects(container)
153
        else:
154
            self.list_containers()
155
    
156
    def list_containers(self):
157
        attrs = ['limit', 'marker', 'if_modified_since',
158
                 'if_unmodified_since']
159
        args = self._build_args(attrs)
160
        args['format'] = 'json' if self.detail else 'text'
161
        
162
        if getattr(self, 'until'):
163
            t = _time.strptime(self.until, self.format)
164
            args['until'] = int(_time.mktime(t))
165
        
166
        l = self.client.list_containers(**args)
167
        print_list(l)
168
    
169
    def list_objects(self, container):
170
        #prepate params
171
        params = {}
172
        attrs = ['limit', 'marker', 'prefix', 'delimiter', 'path',
173
                 'meta', 'if_modified_since', 'if_unmodified_since']
174
        args = self._build_args(attrs)
175
        args['format'] = 'json' if self.detail else 'text'
176
        
177
        if self.until:
178
            t = _time.strptime(self.until, self.format)
179
            args['until'] = int(_time.mktime(t))
180
        
181
        container, sep, object = container.partition('/')
182
        if object:
183
            return
184
        
185
        detail = 'json'
186
        #if request with meta quering disable trash filtering
187
        show_trashed = True if self.meta else False
188
        l = self.client.list_objects(container, **args)
189
        print_list(l, detail=self.detail)
190

    
191
@cli_command('meta')
192
class Meta(Command):
193
    syntax = '[<container>[/<object>]]'
194
    description = 'get account/container/object metadata'
195
    
196
    def add_options(self, parser):
197
        parser.add_option('-r', action='store_true', dest='restricted',
198
                          default=False, help='show only user defined metadata')
199
        parser.add_option('--until', action='store', dest='until',
200
                          default=None, help='show metadata until that date')
201
        parser.add_option('--format', action='store', dest='format',
202
                          default='%d/%m/%Y', help='format to parse until date')
203
        parser.add_option('--version', action='store', dest='version',
204
                          default=None, help='show specific version \
205
                                  (applies only for objects)')
206
    
207
    def execute(self, path=''):
208
        container, sep, object = path.partition('/')
209
        args = {'restricted':self.restricted}
210
        if getattr(self, 'until'):
211
            t = _time.strptime(self.until, self.format)
212
            args['until'] = int(_time.mktime(t))
213
        
214
        if object:
215
            meta = self.client.retrieve_object_metadata(container, object,
216
                                                        self.restricted,
217
                                                        self.version)
218
        elif container:
219
            meta = self.client.retrieve_container_metadata(container, **args)
220
        else:
221
            meta = self.client.retrieve_account_metadata(**args)
222
        if meta == None:
223
            print 'Entity does not exist'
224
        else:
225
            print_dict(meta, header=None)
226

    
227
@cli_command('create')
228
class CreateContainer(Command):
229
    syntax = '<container> [key=val] [...]'
230
    description = 'create a container'
231
    
232
    def execute(self, container, *args):
233
        meta = {}
234
        for arg in args:
235
            key, sep, val = arg.partition('=')
236
            meta[key] = val
237
        ret = self.client.create_container(container, **meta)
238
        if not ret:
239
            print 'Container already exists'
240

    
241
@cli_command('delete', 'rm')
242
class Delete(Command):
243
    syntax = '<container>[/<object>]'
244
    description = 'delete a container or an object'
245
    
246
    def add_options(self, parser):
247
        parser.add_option('--until', action='store', dest='until',
248
                          default=None, help='remove history until that date')
249
        parser.add_option('--format', action='store', dest='format',
250
                          default='%d/%m/%Y', help='format to parse until date')
251
    
252
    def execute(self, path):
253
        container, sep, object = path.partition('/')
254
        until = None
255
        if getattr(self, 'until'):
256
            t = _time.strptime(self.until, self.format)
257
            until = int(_time.mktime(t))
258
        
259
        if object:
260
            self.client.delete_object(container, object, until)
261
        else:
262
            self.client.delete_container(container, until)
263

    
264
@cli_command('get')
265
class GetObject(Command):
266
    syntax = '<container>/<object>'
267
    description = 'get the data of an object'
268
    
269
    def add_options(self, parser):
270
        parser.add_option('-l', action='store_true', dest='detail',
271
                          default=False, help='show detailed output')
272
        parser.add_option('--range', action='store', dest='range',
273
                          default=None, help='show range of data')
274
        parser.add_option('--if-range', action='store', dest='if_range',
275
                          default=None, help='show range of data')
276
        parser.add_option('--if-match', action='store', dest='if_match',
277
                          default=None, help='show output if ETags match')
278
        parser.add_option('--if-none-match', action='store',
279
                          dest='if_none_match', default=None,
280
                          help='show output if ETags don\'t match')
281
        parser.add_option('--if-modified-since', action='store', type='str',
282
                          dest='if_modified_since', default=None,
283
                          help='show output if modified since then')
284
        parser.add_option('--if-unmodified-since', action='store', type='str',
285
                          dest='if_unmodified_since', default=None,
286
                          help='show output if not modified since then')
287
        parser.add_option('-o', action='store', type='str',
288
                          dest='file', default=None,
289
                          help='save output in file')
290
        parser.add_option('--version', action='store', type='str',
291
                          dest='version', default=None,
292
                          help='get the specific \
293
                               version')
294
        parser.add_option('--versionlist', action='store_true',
295
                          dest='versionlist', default=False,
296
                          help='get the full object version list')
297
    
298
    def execute(self, path):
299
        attrs = ['if_match', 'if_none_match', 'if_modified_since',
300
                 'if_unmodified_since']
301
        args = self._build_args(attrs)
302
        args['format'] = 'json' if self.detail else 'text'
303
        if self.range:
304
            args['range'] = 'bytes=%s' %self.range
305
        if getattr(self, 'if_range'):
306
            args['if-range'] = 'If-Range:%s' % getattr(self, 'if_range')
307
        
308
        container, sep, object = path.partition('/')
309
        data = None
310
        if self.versionlist:
311
            if 'detail' in args.keys():
312
                args.pop('detail')
313
            data = self.client.retrieve_object_versionlist(container, object, **args)
314
        elif self.version:
315
            data = self.client.retrieve_object_version(container, object,
316
                                                       self.version, **args)
317
        else:
318
            data = self.client.retrieve_object(container, object, **args)    
319
        
320
        f = self.file and open(self.file, 'w') or stdout
321
        if self.detail:
322
            data = json.loads(data)
323
            if self.versionlist:
324
                print_versions(data, f=f)
325
            else:
326
                print_dict(data, f=f)
327
        else:
328
            f.write(data)
329
        f.close()
330

    
331
@cli_command('mkdir')
332
class PutMarker(Command):
333
    syntax = '<container>/<directory marker>'
334
    description = 'create a directory marker'
335
    
336
    def execute(self, path):
337
        container, sep, object = path.partition('/')
338
        self.client.create_directory_marker(container, object)
339

    
340
@cli_command('put')
341
class PutObject(Command):
342
    syntax = '<container>/<object> [key=val] [...]'
343
    description = 'create/override object'
344
    
345
    def add_options(self, parser):
346
        parser.add_option('--use_hashes', action='store_true', dest='use_hashes',
347
                          default=False, help='provide hashmap instead of data')
348
        parser.add_option('--chunked', action='store_true', dest='chunked',
349
                          default=False, help='set chunked transfer mode')
350
        parser.add_option('--etag', action='store', dest='etag',
351
                          default=None, help='check written data')
352
        parser.add_option('--content-encoding', action='store',
353
                          dest='content_encoding', default=None,
354
                          help='provide the object MIME content type')
355
        parser.add_option('--content-disposition', action='store', type='str',
356
                          dest='content_disposition', default=None,
357
                          help='provide the presentation style of the object')
358
        #parser.add_option('-S', action='store',
359
        #                  dest='segment_size', default=False,
360
        #                  help='use for large file support')
361
        parser.add_option('--manifest', action='store_true',
362
                          dest='x_object_manifest', default=None,
363
                          help='upload a manifestation file')
364
        parser.add_option('--content-type', action='store',
365
                          dest='content_type', default=None,
366
                          help='create object with specific content type')
367
        parser.add_option('--sharing', action='store',
368
                          dest='x_object_sharing', default=None,
369
                          help='define sharing object policy')
370
        parser.add_option('-f', action='store',
371
                          dest='srcpath', default=None,
372
                          help='file descriptor to read from (pass - for standard input)')
373
        parser.add_option('--public', action='store_true',
374
                          dest='x_object_public', default=False,
375
                          help='make object publicly accessible')
376
    
377
    def execute(self, path, *args):
378
        if path.find('=') != -1:
379
            raise Fault('Missing path argument')
380
        
381
        #prepare user defined meta
382
        meta = {}
383
        for arg in args:
384
            key, sep, val = arg.partition('=')
385
            meta[key] = val
386
        
387
        attrs = ['etag', 'content_encoding', 'content_disposition',
388
                 'content_type', 'x_object_sharing', 'x_object_public']
389
        args = self._build_args(attrs)
390
        
391
        container, sep, object = path.partition('/')
392
        
393
        f = None
394
        if self.srcpath:
395
            f = open(self.srcpath) if self.srcpath != '-' else stdin
396
        
397
        if self.use_hashes and not f:
398
            raise Fault('Illegal option combination')
399
        
400
        if self.chunked:
401
            self.client.create_object_using_chunks(container, object, f,
402
                                                    meta=meta, **args)
403
        elif self.use_hashes:
404
            format = 'json' if detail else 'text'
405
            self.client.create_object_by_hashmap(container, object, f, format,
406
                                 meta=meta, **args)
407
        elif self.x_object_manifest:
408
            self.client.create_manifestation(container, object, self.x_object_manifest)
409
        elif not f:
410
            self.client.create_zero_length_object(container, object, meta=meta, **args)
411
        else:
412
            self.client.create_object(container, object, f, meta=meta, **args)
413
        if f:
414
            f.close()
415

    
416
@cli_command('copy', 'cp')
417
class CopyObject(Command):
418
    syntax = '<src container>/<src object> [<dst container>/]<dst object> [key=val] [...]'
419
    description = 'copy an object to a different location'
420
    
421
    def add_options(self, parser):
422
        parser.add_option('--version', action='store',
423
                          dest='version', default=False,
424
                          help='copy specific version')
425
        parser.add_option('--public', action='store_true',
426
                          dest='public', default=False,
427
                          help='make object publicly accessible')
428
    
429
    def execute(self, src, dst, *args):
430
        src_container, sep, src_object = src.partition('/')
431
        dst_container, sep, dst_object = dst.partition('/')
432
        
433
        #prepare user defined meta
434
        meta = {}
435
        for arg in args:
436
            key, sep, val = arg.partition('=')
437
            meta[key] = val
438
        
439
        if not sep:
440
            dst_container = src_container
441
            dst_object = dst
442
        
443
        self.client.copy_object(src_container, src_object, dst_container,
444
                                dst_object, meta, self.public, self.version, **meta)
445

    
446
@cli_command('set')
447
class SetMeta(Command):
448
    syntax = '[<container>[/<object>]] key=val [key=val] [...]'
449
    description = 'set account/container/object metadata'
450
    
451
    def execute(self, path, *args):
452
        #in case of account fix the args
453
        if path.find('=') != -1:
454
            args = list(args)
455
            args.append(path)
456
            args = tuple(args)
457
            path = ''
458
        meta = {}
459
        for arg in args:
460
            key, sep, val = arg.partition('=')
461
            meta[key.strip()] = val.strip()
462
        container, sep, object = path.partition('/')
463
        if object:
464
            self.client.update_object_metadata(container, object, **meta)
465
        elif container:
466
            self.client.update_container_metadata(container, **meta)
467
        else:
468
            self.client.update_account_metadata(**meta)
469

    
470
@cli_command('update')
471
class UpdateObject(Command):
472
    syntax = '<container>/<object> path [key=val] [...]'
473
    description = 'update object metadata/data (default mode: append)'
474
    
475
    def add_options(self, parser):
476
        parser.add_option('-a', action='store_true', dest='append',
477
                          default=True, help='append data')
478
        parser.add_option('--offset', action='store',
479
                          dest='offset',
480
                          default=None, help='starting offest to be updated')
481
        parser.add_option('--range', action='store', dest='content-range',
482
                          default=None, help='range of data to be updated')
483
        parser.add_option('--chunked', action='store_true', dest='chunked',
484
                          default=False, help='set chunked transfer mode')
485
        parser.add_option('--content-encoding', action='store',
486
                          dest='content_encoding', default=None,
487
                          help='provide the object MIME content type')
488
        parser.add_option('--content-disposition', action='store', type='str',
489
                          dest='content_disposition', default=None,
490
                          help='provide the presentation style of the object')
491
        parser.add_option('--manifest', action='store', type='str',
492
                          dest='x_object_manifest', default=None,
493
                          help='use for large file support')        
494
        parser.add_option('--sharing', action='store',
495
                          dest='x_object_sharing', default=None,
496
                          help='define sharing object policy')
497
        parser.add_option('--nosharing', action='store_true',
498
                          dest='no_sharing', default=None,
499
                          help='clear object sharing policy')
500
        parser.add_option('-f', action='store',
501
                          dest='srcpath', default=None,
502
                          help='file descriptor to read from: pass - for standard input')
503
        parser.add_option('--public', action='store_true',
504
                          dest='x_object_public', default=False,
505
                          help='make object publicly accessible')
506
    
507
    def execute(self, path, *args):
508
        if path.find('=') != -1:
509
            raise Fault('Missing path argument')
510
        
511
        #prepare user defined meta
512
        meta = {}
513
        for arg in args:
514
            key, sep, val = arg.partition('=')
515
            meta[key] = val
516
        
517
        if self.no_sharing:
518
            self.x_object_sharing = ''
519
        
520
        attrs = ['content_encoding', 'content_disposition', 'x_object_sharing',
521
                 'x_object_public']
522
        args = self._build_args(attrs)
523
        
524
        container, sep, object = path.partition('/')
525
        
526
        f = None
527
        if self.srcpath:
528
            f = open(self.srcpath) if self.srcpath != '-' else stdin
529
        
530
        if self.chunked:
531
            self.client.update_object_using_chunks(container, object, f,
532
                                                    meta=meta, **args)
533
        else:
534
            data = f.read() if f else None
535
            self.client.update_object(container, object, data, meta=meta, **args)
536
        if f:
537
            f.close()
538

    
539
@cli_command('move', 'mv')
540
class MoveObject(Command):
541
    syntax = '<src container>/<src object> [<dst container>/]<dst object>'
542
    description = 'move an object to a different location'
543
    
544
    def add_options(self, parser):
545
        parser.add_option('--version', action='store',
546
                          dest='version', default=None,
547
                          help='move a specific object version')
548
        parser.add_option('--public', action='store_true',
549
                          dest='public', default=False,
550
                          help='make object publicly accessible')
551
    
552
    def execute(self, src, dst, *args):
553
        src_container, sep, src_object = src.partition('/')
554
        dst_container, sep, dst_object = dst.partition('/')
555
        if not sep:
556
            dst_container = src_container
557
            dst_object = dst
558
        
559
        #prepare user defined meta
560
        meta = {}
561
        for arg in args:
562
            key, sep, val = arg.partition('=')
563
            meta[key] = val
564
        
565
        self.client.move_object(src_container, src_object, dst_container,
566
                                dst_object, meta, self.public, self.version)
567

    
568
@cli_command('remove')
569
class TrashObject(Command):
570
    syntax = '<container>/<object>'
571
    description = 'trash an object'
572
    
573
    def execute(self, src):
574
        src_container, sep, src_object = src.partition('/')
575
        
576
        self.client.trash_object(src_container, src_object)
577

    
578
@cli_command('restore')
579
class RestoreObject(Command):
580
    syntax = '<container>/<object>'
581
    description = 'restore a trashed object'
582
    
583
    def execute(self, src):
584
        src_container, sep, src_object = src.partition('/')
585
        
586
        self.client.restore_object(src_container, src_object)
587

    
588
@cli_command('unset')
589
class UnsetObject(Command):
590
    syntax = '<container>/[<object>] key [key] [...]'
591
    description = 'delete metadata info'
592
    
593
    def execute(self, path, *args):
594
        #in case of account fix the args
595
        if len(args) == 0:
596
            args = list(args)
597
            args.append(path)
598
            args = tuple(args)
599
            path = ''
600
        meta = []
601
        for key in args:
602
            meta.append(key)
603
        container, sep, object = path.partition('/')
604
        if object:
605
            self.client.delete_object_metadata(container, object, meta)
606
        elif container:
607
            self.client.delete_container_metadata(container, meta)
608
        else:
609
            self.client.delete_account_metadata(meta)
610

    
611
@cli_command('group')
612
class CreateGroup(Command):
613
    syntax = 'key=val [key=val] [...]'
614
    description = 'create account groups'
615
    
616
    def execute(self, *args):
617
        groups = {}
618
        for arg in args:
619
            key, sep, val = arg.partition('=')
620
            groups[key] = val
621
        self.client.set_account_groups(**groups)
622

    
623
@cli_command('ungroup')
624
class DeleteGroup(Command):
625
    syntax = 'key [key] [...]'
626
    description = 'delete account groups'
627
    
628
    def execute(self, *args):
629
        groups = []
630
        for arg in args:
631
            groups.append(arg)
632
        self.client.unset_account_groups(groups)
633

    
634
@cli_command('policy')
635
class SetPolicy(Command):
636
    syntax = 'container key=val [key=val] [...]'
637
    description = 'set container policies'
638
    
639
    def execute(self, path, *args):
640
        if path.find('=') != -1:
641
            raise Fault('Missing container argument')
642
        
643
        container, sep, object = path.partition('/')
644
        
645
        if object:
646
            raise Fault('Only containers have policies')
647
        
648
        policies = {}
649
        for arg in args:
650
            key, sep, val = arg.partition('=')
651
            policies[key] = val
652
        
653
        self.client.set_container_policies(container, **policies)
654

    
655
@cli_command('publish')
656
class PublishObject(Command):
657
    syntax = '<container>/<object>'
658
    description = 'publish an object'
659
    
660
    def execute(self, src):
661
        src_container, sep, src_object = src.partition('/')
662
        
663
        self.client.publish_object(src_container, src_object)
664

    
665
@cli_command('unpublish')
666
class UnpublishObject(Command):
667
    syntax = '<container>/<object>'
668
    description = 'unpublish an object'
669
    
670
    def execute(self, src):
671
        src_container, sep, src_object = src.partition('/')
672
        
673
        self.client.unpublish_object(src_container, src_object)
674

    
675
def print_usage():
676
    cmd = Command('', [])
677
    parser = cmd.parser
678
    parser.usage = '%prog <command> [options]'
679
    parser.print_help()
680
    
681
    commands = []
682
    for cls in set(_cli_commands.values()):
683
        name = ', '.join(cls.commands)
684
        description = getattr(cls, 'description', '')
685
        commands.append('  %s %s' % (name.ljust(12), description))
686
    print '\nCommands:\n' + '\n'.join(sorted(commands))
687

    
688
def print_dict(d, header='name', f=stdout, detail=True):
689
    header = header in d and header or 'subdir'
690
    if header and header in d:
691
        f.write('%s\n' %d.pop(header))
692
    if detail:
693
        patterns = ['^x_(account|container|object)_meta_(\w+)$']
694
        patterns.append(patterns[0].replace('_', '-'))
695
        for key, val in sorted(d.items()):
696
            f.write('%s: %s\n' % (key.rjust(30), val))
697

    
698
def print_list(l, verbose=False, f=stdout, detail=True):
699
    for elem in l:
700
        #if it's empty string continue
701
        if not elem:
702
            continue
703
        if type(elem) == types.DictionaryType:
704
            print_dict(elem, f=f, detail=detail)
705
        elif type(elem) == types.StringType:
706
            if not verbose:
707
                elem = elem.split('Traceback')[0]
708
            f.write('%s\n' % elem)
709
        else:
710
            f.write('%s\n' % elem)
711

    
712
def print_versions(data, f=stdout):
713
    if 'versions' not in data:
714
        f.write('%s\n' %data)
715
        return
716
    f.write('versions:\n')
717
    for id, t in data['versions']:
718
        f.write('%s @ %s\n' % (str(id).rjust(30), datetime.fromtimestamp(t)))
719

    
720
def _get_user():
721
        try:
722
            return os.environ['PITHOS_USER']
723
        except KeyError:
724
            return getuser()
725

    
726
def _get_auth():
727
        try:
728
            return os.environ['PITHOS_AUTH']
729
        except KeyError:
730
            return '0000'
731

    
732
def _get_server():
733
    try:
734
        return os.environ['PITHOS_SERVER']
735
    except KeyError:
736
        return DEFAULT_HOST
737

    
738
def main():
739
    try:
740
        name = argv[1]
741
        cls = class_for_cli_command(name)
742
    except (IndexError, KeyError):
743
        print_usage()
744
        exit(1)
745
    
746
    cmd = cls(name, argv[2:])
747
    
748
    cmd.execute(*cmd.args)
749
    #try:
750
    #    cmd.execute(*cmd.args)
751
    #except TypeError, e:
752
    #    cmd.parser.print_help()
753
    #    exit(1)
754
    #except Fault, f:
755
    #    status = f.status and '%s ' % f.status or ''
756
    #    print '%s%s' % (status, f.data)
757

    
758
if __name__ == '__main__':
759
    main()