Revision 2715ade4 snf-pithos-tools/pithos/tools/sh.py
b/snf-pithos-tools/pithos/tools/sh.py | ||
---|---|---|
1 | 1 |
#!/usr/bin/env python |
2 | 2 |
|
3 | 3 |
# Copyright 2011-2012 GRNET S.A. All rights reserved. |
4 |
#
|
|
4 |
# |
|
5 | 5 |
# Redistribution and use in source and binary forms, with or |
6 | 6 |
# without modification, are permitted provided that the following |
7 | 7 |
# conditions are met: |
8 |
#
|
|
8 |
# |
|
9 | 9 |
# 1. Redistributions of source code must retain the above |
10 | 10 |
# copyright notice, this list of conditions and the following |
11 | 11 |
# disclaimer. |
12 |
#
|
|
12 |
# |
|
13 | 13 |
# 2. Redistributions in binary form must reproduce the above |
14 | 14 |
# copyright notice, this list of conditions and the following |
15 | 15 |
# disclaimer in the documentation and/or other materials |
16 | 16 |
# provided with the distribution. |
17 |
#
|
|
17 |
# |
|
18 | 18 |
# THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS |
19 | 19 |
# OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED |
20 | 20 |
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
... | ... | |
27 | 27 |
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN |
28 | 28 |
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
29 | 29 |
# POSSIBILITY OF SUCH DAMAGE. |
30 |
#
|
|
30 |
# |
|
31 | 31 |
# The views and conclusions contained in the software and |
32 | 32 |
# documentation are those of the authors and should not be |
33 | 33 |
# interpreted as representing official policies, either expressed |
... | ... | |
52 | 52 |
|
53 | 53 |
_cli_commands = {} |
54 | 54 |
|
55 |
|
|
55 | 56 |
def cli_command(*args): |
56 | 57 |
def decorator(cls): |
57 | 58 |
cls.commands = args |
... | ... | |
60 | 61 |
return cls |
61 | 62 |
return decorator |
62 | 63 |
|
64 |
|
|
63 | 65 |
def class_for_cli_command(name): |
64 | 66 |
return _cli_commands[name] |
65 | 67 |
|
68 |
|
|
66 | 69 |
class Command(object): |
67 | 70 |
syntax = '' |
68 |
|
|
71 |
|
|
69 | 72 |
def __init__(self, name, argv): |
70 | 73 |
parser = OptionParser('%%prog %s [options] %s' % (name, self.syntax)) |
71 | 74 |
parser.add_option('--url', dest='url', metavar='URL', |
... | ... | |
82 | 85 |
default=False, help='debug output') |
83 | 86 |
self.add_options(parser) |
84 | 87 |
options, args = parser.parse_args(argv) |
85 |
|
|
88 |
|
|
86 | 89 |
# Add options to self |
87 | 90 |
for opt in parser.option_list: |
88 | 91 |
key = opt.dest |
89 | 92 |
if key: |
90 | 93 |
val = getattr(options, key) |
91 | 94 |
setattr(self, key, val) |
92 |
|
|
93 |
self.client = Pithos_Client(self.url, self.token, self.user, self.verbose, |
|
94 |
self.debug) |
|
95 |
|
|
95 |
|
|
96 |
self.client = Pithos_Client( |
|
97 |
self.url, self.token, self.user, self.verbose, |
|
98 |
self.debug) |
|
99 |
|
|
96 | 100 |
self.parser = parser |
97 | 101 |
self.args = args |
98 |
|
|
102 |
|
|
99 | 103 |
def _build_args(self, attrs): |
100 | 104 |
args = {} |
101 | 105 |
for a in [a for a in attrs if getattr(self, a)]: |
... | ... | |
104 | 108 |
|
105 | 109 |
def add_options(self, parser): |
106 | 110 |
pass |
107 |
|
|
111 |
|
|
108 | 112 |
def execute(self, *args): |
109 | 113 |
pass |
110 | 114 |
|
115 |
|
|
111 | 116 |
@cli_command('list', 'ls') |
112 | 117 |
class List(Command): |
113 | 118 |
syntax = '[<container>[/<object>]]' |
114 | 119 |
description = 'list containers or objects' |
115 |
|
|
120 |
|
|
116 | 121 |
def add_options(self, parser): |
117 | 122 |
parser.add_option('-l', action='store_true', dest='detail', |
118 | 123 |
default=False, help='show detailed output') |
... | ... | |
147 | 152 |
default=False, help='show only shared') |
148 | 153 |
parser.add_option('--public', action='store_true', dest='public', |
149 | 154 |
default=False, help='show only public') |
150 |
|
|
151 |
|
|
155 |
|
|
152 | 156 |
def execute(self, container=None): |
153 | 157 |
if container: |
154 | 158 |
self.list_objects(container) |
155 | 159 |
else: |
156 | 160 |
self.list_containers() |
157 |
|
|
161 |
|
|
158 | 162 |
def list_containers(self): |
159 | 163 |
attrs = ['limit', 'marker', 'if_modified_since', |
160 | 164 |
'if_unmodified_since', 'shared', 'public'] |
161 | 165 |
args = self._build_args(attrs) |
162 | 166 |
args['format'] = 'json' if self.detail else 'text' |
163 |
|
|
167 |
|
|
164 | 168 |
if getattr(self, 'until'): |
165 | 169 |
t = _time.strptime(self.until, self.format) |
166 | 170 |
args['until'] = int(_time.mktime(t)) |
167 |
|
|
171 |
|
|
168 | 172 |
l = self.client.list_containers(**args) |
169 | 173 |
print_list(l) |
170 |
|
|
174 |
|
|
171 | 175 |
def list_objects(self, container): |
172 | 176 |
#prepate params |
173 | 177 |
params = {} |
... | ... | |
176 | 180 |
'shared', 'public'] |
177 | 181 |
args = self._build_args(attrs) |
178 | 182 |
args['format'] = 'json' if self.detail else 'text' |
179 |
|
|
183 |
|
|
180 | 184 |
if self.until: |
181 | 185 |
t = _time.strptime(self.until, self.format) |
182 | 186 |
args['until'] = int(_time.mktime(t)) |
183 |
|
|
187 |
|
|
184 | 188 |
container, sep, object = container.partition('/') |
185 | 189 |
if object: |
186 | 190 |
return |
187 |
|
|
191 |
|
|
188 | 192 |
detail = 'json' |
189 | 193 |
#if request with meta quering disable trash filtering |
190 | 194 |
show_trashed = True if self.meta else False |
191 | 195 |
l = self.client.list_objects(container, **args) |
192 | 196 |
print_list(l, detail=self.detail) |
193 | 197 |
|
198 |
|
|
194 | 199 |
@cli_command('meta') |
195 | 200 |
class Meta(Command): |
196 | 201 |
syntax = '[<container>[/<object>]]' |
197 | 202 |
description = 'get account/container/object metadata' |
198 |
|
|
203 |
|
|
199 | 204 |
def add_options(self, parser): |
200 | 205 |
parser.add_option('-r', action='store_true', dest='restricted', |
201 | 206 |
default=False, help='show only user defined metadata') |
... | ... | |
206 | 211 |
parser.add_option('--version', action='store', dest='version', |
207 | 212 |
default=None, help='show specific version \ |
208 | 213 |
(applies only for objects)') |
209 |
|
|
214 |
|
|
210 | 215 |
def execute(self, path=''): |
211 | 216 |
container, sep, object = path.partition('/') |
212 | 217 |
args = {'restricted': self.restricted} |
213 | 218 |
if getattr(self, 'until'): |
214 | 219 |
t = _time.strptime(self.until, self.format) |
215 | 220 |
args['until'] = int(_time.mktime(t)) |
216 |
|
|
221 |
|
|
217 | 222 |
if object: |
218 | 223 |
meta = self.client.retrieve_object_metadata(container, object, |
219 | 224 |
self.restricted, |
... | ... | |
222 | 227 |
meta = self.client.retrieve_container_metadata(container, **args) |
223 | 228 |
else: |
224 | 229 |
meta = self.client.retrieve_account_metadata(**args) |
225 |
if meta == None:
|
|
230 |
if meta is None:
|
|
226 | 231 |
print 'Entity does not exist' |
227 | 232 |
else: |
228 | 233 |
print_dict(meta, header=None) |
229 | 234 |
|
235 |
|
|
230 | 236 |
@cli_command('create') |
231 | 237 |
class CreateContainer(Command): |
232 | 238 |
syntax = '<container> [key=val] [...]' |
233 | 239 |
description = 'create a container' |
234 |
|
|
240 |
|
|
235 | 241 |
def add_options(self, parser): |
236 | 242 |
parser.add_option('--versioning', action='store', dest='versioning', |
237 | 243 |
default=None, help='set container versioning (auto/none)') |
238 | 244 |
parser.add_option('--quota', action='store', dest='quota', |
239 | 245 |
default=None, help='set default container quota') |
240 |
|
|
246 |
|
|
241 | 247 |
def execute(self, container, *args): |
242 | 248 |
meta = {} |
243 | 249 |
for arg in args: |
... | ... | |
248 | 254 |
policy['versioning'] = self.versioning |
249 | 255 |
if getattr(self, 'quota'): |
250 | 256 |
policy['quota'] = self.quota |
251 |
ret = self.client.create_container(container, meta=meta, policies=policy) |
|
257 |
ret = self.client.create_container( |
|
258 |
container, meta=meta, policies=policy) |
|
252 | 259 |
if not ret: |
253 | 260 |
print 'Container already exists' |
254 | 261 |
|
262 |
|
|
255 | 263 |
@cli_command('delete', 'rm') |
256 | 264 |
class Delete(Command): |
257 | 265 |
syntax = '<container>[/<object>]' |
258 | 266 |
description = 'delete a container or an object' |
259 |
|
|
267 |
|
|
260 | 268 |
def add_options(self, parser): |
261 | 269 |
parser.add_option('--until', action='store', dest='until', |
262 | 270 |
default=None, help='remove history until that date') |
... | ... | |
268 | 276 |
parser.add_option('-r', action='store_true', |
269 | 277 |
dest='recursive', default=False, |
270 | 278 |
help='mass delimiter objects with delimiter /') |
271 |
|
|
279 |
|
|
272 | 280 |
def execute(self, path): |
273 | 281 |
container, sep, object = path.partition('/') |
274 | 282 |
until = None |
275 | 283 |
if getattr(self, 'until'): |
276 | 284 |
t = _time.strptime(self.until, self.format) |
277 | 285 |
until = int(_time.mktime(t)) |
278 |
|
|
286 |
|
|
279 | 287 |
kwargs = {} |
280 | 288 |
if self.delimiter: |
281 | 289 |
kwargs['delimiter'] = self.delimiter |
282 | 290 |
elif self.recursive: |
283 | 291 |
kwargs['delimiter'] = '/' |
284 |
|
|
292 |
|
|
285 | 293 |
if object: |
286 | 294 |
self.client.delete_object(container, object, until, **kwargs) |
287 | 295 |
else: |
288 | 296 |
self.client.delete_container(container, until, **kwargs) |
289 | 297 |
|
298 |
|
|
290 | 299 |
@cli_command('get') |
291 | 300 |
class GetObject(Command): |
292 | 301 |
syntax = '<container>/<object>' |
293 | 302 |
description = 'get the data of an object' |
294 |
|
|
303 |
|
|
295 | 304 |
def add_options(self, parser): |
296 | 305 |
parser.add_option('-l', action='store_true', dest='detail', |
297 | 306 |
default=False, help='show detailed output') |
... | ... | |
323 | 332 |
parser.add_option('--hashmap', action='store_true', |
324 | 333 |
dest='hashmap', default=False, |
325 | 334 |
help='get the object hashmap instead') |
326 |
|
|
335 |
|
|
327 | 336 |
def execute(self, path): |
328 | 337 |
attrs = ['if_match', 'if_none_match', 'if_modified_since', |
329 | 338 |
'if_unmodified_since', 'hashmap'] |
... | ... | |
333 | 342 |
args['range'] = 'bytes=%s' % self.range |
334 | 343 |
if getattr(self, 'if_range'): |
335 | 344 |
args['if-range'] = 'If-Range:%s' % getattr(self, 'if_range') |
336 |
|
|
345 |
|
|
337 | 346 |
container, sep, object = path.partition('/') |
338 | 347 |
data = None |
339 | 348 |
if self.versionlist: |
... | ... | |
341 | 350 |
args.pop('detail') |
342 | 351 |
args.pop('format') |
343 | 352 |
self.detail = True |
344 |
data = self.client.retrieve_object_versionlist(container, object, **args) |
|
353 |
data = self.client.retrieve_object_versionlist( |
|
354 |
container, object, **args) |
|
345 | 355 |
elif self.version: |
346 | 356 |
data = self.client.retrieve_object_version(container, object, |
347 | 357 |
self.version, **args) |
... | ... | |
350 | 360 |
args.pop('detail') |
351 | 361 |
args.pop('format') |
352 | 362 |
self.detail = True |
353 |
data = self.client.retrieve_object_hashmap(container, object, **args) |
|
363 |
data = self.client.retrieve_object_hashmap( |
|
364 |
container, object, **args) |
|
354 | 365 |
else: |
355 |
data = self.client.retrieve_object(container, object, **args)
|
|
356 |
|
|
366 |
data = self.client.retrieve_object(container, object, **args) |
|
367 |
|
|
357 | 368 |
f = open(self.file, 'w') if self.file else stdout |
358 |
if self.detail or type(data) == types.DictionaryType:
|
|
369 |
if self.detail or isinstance(data, types.DictionaryType):
|
|
359 | 370 |
if self.versionlist: |
360 | 371 |
print_versions(data, f=f) |
361 | 372 |
else: |
... | ... | |
364 | 375 |
f.write(data) |
365 | 376 |
f.close() |
366 | 377 |
|
378 |
|
|
367 | 379 |
@cli_command('mkdir') |
368 | 380 |
class PutMarker(Command): |
369 | 381 |
syntax = '<container>/<directory marker>' |
370 | 382 |
description = 'create a directory marker' |
371 |
|
|
383 |
|
|
372 | 384 |
def execute(self, path): |
373 | 385 |
container, sep, object = path.partition('/') |
374 | 386 |
self.client.create_directory_marker(container, object) |
375 | 387 |
|
388 |
|
|
376 | 389 |
@cli_command('put') |
377 | 390 |
class PutObject(Command): |
378 | 391 |
syntax = '<container>/<object> [key=val] [...]' |
379 | 392 |
description = 'create/override object' |
380 |
|
|
393 |
|
|
381 | 394 |
def add_options(self, parser): |
382 |
parser.add_option('--use_hashes', action='store_true', dest='use_hashes', |
|
383 |
default=False, help='provide hashmap instead of data') |
|
395 |
parser.add_option( |
|
396 |
'--use_hashes', action='store_true', dest='use_hashes', |
|
397 |
default=False, help='provide hashmap instead of data') |
|
384 | 398 |
parser.add_option('--chunked', action='store_true', dest='chunked', |
385 | 399 |
default=False, help='set chunked transfer mode') |
386 | 400 |
parser.add_option('--etag', action='store', dest='etag', |
... | ... | |
409 | 423 |
parser.add_option('--public', action='store_true', |
410 | 424 |
dest='x_object_public', default=False, |
411 | 425 |
help='make object publicly accessible') |
412 |
|
|
426 |
|
|
413 | 427 |
def execute(self, path, *args): |
414 | 428 |
if path.find('=') != -1: |
415 | 429 |
raise Fault('Missing path argument') |
416 |
|
|
430 |
|
|
417 | 431 |
#prepare user defined meta |
418 | 432 |
meta = {} |
419 | 433 |
for arg in args: |
420 | 434 |
key, sep, val = arg.partition('=') |
421 | 435 |
meta[key] = val |
422 |
|
|
436 |
|
|
423 | 437 |
attrs = ['etag', 'content_encoding', 'content_disposition', |
424 | 438 |
'content_type', 'x_object_sharing', 'x_object_public'] |
425 | 439 |
args = self._build_args(attrs) |
426 |
|
|
440 |
|
|
427 | 441 |
container, sep, object = path.partition('/') |
428 |
|
|
442 |
|
|
429 | 443 |
f = None |
430 | 444 |
if self.srcpath: |
431 | 445 |
f = open(self.srcpath) if self.srcpath != '-' else stdin |
432 |
|
|
446 |
|
|
433 | 447 |
if self.use_hashes and not f: |
434 | 448 |
raise Fault('Illegal option combination') |
435 |
|
|
449 |
|
|
436 | 450 |
if self.chunked: |
437 | 451 |
self.client.create_object_using_chunks(container, object, f, |
438 |
meta=meta, **args)
|
|
452 |
meta=meta, **args) |
|
439 | 453 |
elif self.use_hashes: |
440 | 454 |
data = f.read() |
441 | 455 |
hashmap = json.loads(data) |
442 | 456 |
self.client.create_object_by_hashmap(container, object, hashmap, |
443 |
meta=meta, **args) |
|
457 |
meta=meta, **args)
|
|
444 | 458 |
elif self.x_object_manifest: |
445 |
self.client.create_manifestation(container, object, self.x_object_manifest) |
|
459 |
self.client.create_manifestation( |
|
460 |
container, object, self.x_object_manifest) |
|
446 | 461 |
elif not f: |
447 |
self.client.create_zero_length_object(container, object, meta=meta, **args) |
|
462 |
self.client.create_zero_length_object( |
|
463 |
container, object, meta=meta, **args) |
|
448 | 464 |
else: |
449 | 465 |
self.client.create_object(container, object, f, meta=meta, **args) |
450 | 466 |
if f: |
451 | 467 |
f.close() |
452 | 468 |
|
469 |
|
|
453 | 470 |
@cli_command('copy', 'cp') |
454 | 471 |
class CopyObject(Command): |
455 | 472 |
syntax = '<src container>/<src object> [<dst container>/]<dst object> [key=val] [...]' |
456 | 473 |
description = 'copy an object to a different location' |
457 |
|
|
474 |
|
|
458 | 475 |
def add_options(self, parser): |
459 | 476 |
parser.add_option('--version', action='store', |
460 | 477 |
dest='version', default=False, |
... | ... | |
471 | 488 |
parser.add_option('-r', action='store_true', |
472 | 489 |
dest='recursive', default=False, |
473 | 490 |
help='mass copy with delimiter /') |
474 |
|
|
491 |
|
|
475 | 492 |
def execute(self, src, dst, *args): |
476 | 493 |
src_container, sep, src_object = src.partition('/') |
477 | 494 |
dst_container, sep, dst_object = dst.partition('/') |
478 |
|
|
495 |
|
|
479 | 496 |
#prepare user defined meta |
480 | 497 |
meta = {} |
481 | 498 |
for arg in args: |
482 | 499 |
key, sep, val = arg.partition('=') |
483 | 500 |
meta[key] = val |
484 |
|
|
501 |
|
|
485 | 502 |
if not sep: |
486 | 503 |
dst_container = src_container |
487 | 504 |
dst_object = dst |
488 |
|
|
489 |
args = {'content_type':self.content_type} if self.content_type else {} |
|
505 |
|
|
506 |
args = {'content_type': self.content_type} if self.content_type else {}
|
|
490 | 507 |
if self.delimiter: |
491 |
args['delimiter'] = self.delimiter
|
|
508 |
args['delimiter'] = self.delimiter
|
|
492 | 509 |
elif self.recursive: |
493 |
args['delimiter'] = '/'
|
|
510 |
args['delimiter'] = '/'
|
|
494 | 511 |
self.client.copy_object(src_container, src_object, dst_container, |
495 | 512 |
dst_object, meta, self.public, self.version, |
496 | 513 |
**args) |
497 | 514 |
|
515 |
|
|
498 | 516 |
@cli_command('set') |
499 | 517 |
class SetMeta(Command): |
500 | 518 |
syntax = '[<container>[/<object>]] key=val [key=val] [...]' |
501 | 519 |
description = 'set account/container/object metadata' |
502 |
|
|
520 |
|
|
503 | 521 |
def execute(self, path, *args): |
504 | 522 |
#in case of account fix the args |
505 | 523 |
if path.find('=') != -1: |
... | ... | |
519 | 537 |
else: |
520 | 538 |
self.client.update_account_metadata(**meta) |
521 | 539 |
|
540 |
|
|
522 | 541 |
@cli_command('update') |
523 | 542 |
class UpdateObject(Command): |
524 | 543 |
syntax = '<container>/<object> path [key=val] [...]' |
525 | 544 |
description = 'update object metadata/data (default mode: append)' |
526 |
|
|
545 |
|
|
527 | 546 |
def add_options(self, parser): |
528 | 547 |
parser.add_option('-a', action='store_true', dest='append', |
529 | 548 |
default=True, help='append data') |
... | ... | |
542 | 561 |
help='provide the presentation style of the object') |
543 | 562 |
parser.add_option('--manifest', action='store', type='str', |
544 | 563 |
dest='x_object_manifest', default=None, |
545 |
help='use for large file support')
|
|
564 |
help='use for large file support') |
|
546 | 565 |
parser.add_option('--sharing', action='store', |
547 | 566 |
dest='x_object_sharing', default=None, |
548 | 567 |
help='define sharing object policy') |
... | ... | |
558 | 577 |
parser.add_option('--replace', action='store_true', |
559 | 578 |
dest='replace', default=False, |
560 | 579 |
help='override metadata') |
561 |
|
|
580 |
|
|
562 | 581 |
def execute(self, path, *args): |
563 | 582 |
if path.find('=') != -1: |
564 | 583 |
raise Fault('Missing path argument') |
565 |
|
|
584 |
|
|
566 | 585 |
#prepare user defined meta |
567 | 586 |
meta = {} |
568 | 587 |
for arg in args: |
569 | 588 |
key, sep, val = arg.partition('=') |
570 | 589 |
meta[key] = val |
571 |
|
|
572 |
|
|
590 |
|
|
573 | 591 |
attrs = ['content_encoding', 'content_disposition', 'x_object_sharing', |
574 | 592 |
'x_object_public', 'x_object_manifest', 'replace', 'offset', |
575 | 593 |
'content_range'] |
576 | 594 |
args = self._build_args(attrs) |
577 |
|
|
595 |
|
|
578 | 596 |
if self.no_sharing: |
579 | 597 |
args['x_object_sharing'] = '' |
580 |
|
|
598 |
|
|
581 | 599 |
container, sep, object = path.partition('/') |
582 |
|
|
600 |
|
|
583 | 601 |
f = None |
584 | 602 |
if self.srcpath: |
585 | 603 |
f = open(self.srcpath) if self.srcpath != '-' else stdin |
586 |
|
|
604 |
|
|
587 | 605 |
if self.chunked: |
588 | 606 |
self.client.update_object_using_chunks(container, object, f, |
589 |
meta=meta, **args)
|
|
607 |
meta=meta, **args) |
|
590 | 608 |
else: |
591 | 609 |
self.client.update_object(container, object, f, meta=meta, **args) |
592 | 610 |
if f: |
593 | 611 |
f.close() |
594 | 612 |
|
613 |
|
|
595 | 614 |
@cli_command('move', 'mv') |
596 | 615 |
class MoveObject(Command): |
597 | 616 |
syntax = '<src container>/<src object> [<dst container>/]<dst object>' |
598 | 617 |
description = 'move an object to a different location' |
599 |
|
|
618 |
|
|
600 | 619 |
def add_options(self, parser): |
601 | 620 |
parser.add_option('--public', action='store_true', |
602 | 621 |
dest='public', default=False, |
... | ... | |
610 | 629 |
parser.add_option('-r', action='store_true', |
611 | 630 |
dest='recursive', default=False, |
612 | 631 |
help='mass move objects with delimiter /') |
613 |
|
|
632 |
|
|
614 | 633 |
def execute(self, src, dst, *args): |
615 | 634 |
src_container, sep, src_object = src.partition('/') |
616 | 635 |
dst_container, sep, dst_object = dst.partition('/') |
617 | 636 |
if not sep: |
618 | 637 |
dst_container = src_container |
619 | 638 |
dst_object = dst |
620 |
|
|
639 |
|
|
621 | 640 |
#prepare user defined meta |
622 | 641 |
meta = {} |
623 | 642 |
for arg in args: |
624 | 643 |
key, sep, val = arg.partition('=') |
625 | 644 |
meta[key] = val |
626 |
|
|
627 |
args = {'content_type':self.content_type} if self.content_type else {} |
|
645 |
|
|
646 |
args = {'content_type': self.content_type} if self.content_type else {}
|
|
628 | 647 |
if self.delimiter: |
629 |
args['delimiter'] = self.delimiter
|
|
648 |
args['delimiter'] = self.delimiter
|
|
630 | 649 |
elif self.recursive: |
631 |
args['delimiter'] = '/'
|
|
650 |
args['delimiter'] = '/'
|
|
632 | 651 |
self.client.move_object(src_container, src_object, dst_container, |
633 | 652 |
dst_object, meta, self.public, **args) |
634 | 653 |
|
654 |
|
|
635 | 655 |
@cli_command('unset') |
636 | 656 |
class UnsetObject(Command): |
637 | 657 |
syntax = '<container>/[<object>] key [key] [...]' |
638 | 658 |
description = 'delete metadata info' |
639 |
|
|
659 |
|
|
640 | 660 |
def execute(self, path, *args): |
641 | 661 |
#in case of account fix the args |
642 | 662 |
if len(args) == 0: |
... | ... | |
655 | 675 |
else: |
656 | 676 |
self.client.delete_account_metadata(meta) |
657 | 677 |
|
678 |
|
|
658 | 679 |
@cli_command('group') |
659 | 680 |
class CreateGroup(Command): |
660 | 681 |
syntax = 'key=val [key=val] [...]' |
661 | 682 |
description = 'create account groups' |
662 |
|
|
683 |
|
|
663 | 684 |
def execute(self, *args): |
664 | 685 |
groups = {} |
665 | 686 |
for arg in args: |
... | ... | |
667 | 688 |
groups[key] = val |
668 | 689 |
self.client.set_account_groups(**groups) |
669 | 690 |
|
691 |
|
|
670 | 692 |
@cli_command('ungroup') |
671 | 693 |
class DeleteGroup(Command): |
672 | 694 |
syntax = 'key [key] [...]' |
673 | 695 |
description = 'delete account groups' |
674 |
|
|
696 |
|
|
675 | 697 |
def execute(self, *args): |
676 | 698 |
groups = [] |
677 | 699 |
for arg in args: |
678 | 700 |
groups.append(arg) |
679 | 701 |
self.client.unset_account_groups(groups) |
680 | 702 |
|
703 |
|
|
681 | 704 |
@cli_command('policy') |
682 | 705 |
class SetPolicy(Command): |
683 | 706 |
syntax = 'container key=val [key=val] [...]' |
684 | 707 |
description = 'set container policies' |
685 |
|
|
708 |
|
|
686 | 709 |
def execute(self, path, *args): |
687 | 710 |
if path.find('=') != -1: |
688 | 711 |
raise Fault('Missing container argument') |
689 |
|
|
712 |
|
|
690 | 713 |
container, sep, object = path.partition('/') |
691 |
|
|
714 |
|
|
692 | 715 |
if object: |
693 | 716 |
raise Fault('Only containers have policies') |
694 |
|
|
717 |
|
|
695 | 718 |
policies = {} |
696 | 719 |
for arg in args: |
697 | 720 |
key, sep, val = arg.partition('=') |
698 | 721 |
policies[key] = val |
699 |
|
|
722 |
|
|
700 | 723 |
self.client.set_container_policies(container, **policies) |
701 | 724 |
|
725 |
|
|
702 | 726 |
@cli_command('publish') |
703 | 727 |
class PublishObject(Command): |
704 | 728 |
syntax = '<container>/<object>' |
705 | 729 |
description = 'publish an object' |
706 |
|
|
730 |
|
|
707 | 731 |
def execute(self, src): |
708 | 732 |
src_container, sep, src_object = src.partition('/') |
709 |
|
|
733 |
|
|
710 | 734 |
self.client.publish_object(src_container, src_object) |
711 | 735 |
|
736 |
|
|
712 | 737 |
@cli_command('unpublish') |
713 | 738 |
class UnpublishObject(Command): |
714 | 739 |
syntax = '<container>/<object>' |
715 | 740 |
description = 'unpublish an object' |
716 |
|
|
741 |
|
|
717 | 742 |
def execute(self, src): |
718 | 743 |
src_container, sep, src_object = src.partition('/') |
719 |
|
|
744 |
|
|
720 | 745 |
self.client.unpublish_object(src_container, src_object) |
721 | 746 |
|
747 |
|
|
722 | 748 |
@cli_command('sharing') |
723 | 749 |
class SharingObject(Command): |
724 | 750 |
syntax = 'list users sharing objects with the user' |
725 | 751 |
description = 'list user accounts sharing objects with the user' |
726 |
|
|
752 |
|
|
727 | 753 |
def add_options(self, parser): |
728 | 754 |
parser.add_option('-l', action='store_true', dest='detail', |
729 | 755 |
default=False, help='show detailed output') |
... | ... | |
732 | 758 |
parser.add_option('--marker', action='store', type='str', |
733 | 759 |
dest='marker', default=None, |
734 | 760 |
help='show output greater then marker') |
735 |
|
|
736 |
|
|
761 |
|
|
737 | 762 |
def execute(self): |
738 | 763 |
attrs = ['limit', 'marker'] |
739 | 764 |
args = self._build_args(attrs) |
740 | 765 |
args['format'] = 'json' if self.detail else 'text' |
741 |
|
|
766 |
|
|
742 | 767 |
print_list(self.client.list_shared_by_others(**args)) |
743 | 768 |
|
769 |
|
|
744 | 770 |
@cli_command('send') |
745 | 771 |
class Send(Command): |
746 | 772 |
syntax = '<file> <container>[/<prefix>]' |
747 | 773 |
description = 'upload file to container (using prefix)' |
748 |
|
|
774 |
|
|
749 | 775 |
def execute(self, file, path): |
750 | 776 |
container, sep, prefix = path.partition('/') |
751 | 777 |
upload(self.client, file, container, prefix) |
752 | 778 |
|
779 |
|
|
753 | 780 |
@cli_command('receive') |
754 | 781 |
class Receive(Command): |
755 | 782 |
syntax = '<container>/<object> <file>' |
756 | 783 |
description = 'download object to file' |
757 |
|
|
784 |
|
|
758 | 785 |
def execute(self, path, file): |
759 | 786 |
container, sep, object = path.partition('/') |
760 | 787 |
download(self.client, container, object, file) |
761 | 788 |
|
789 |
|
|
762 | 790 |
def print_usage(): |
763 | 791 |
cmd = Command('', []) |
764 | 792 |
parser = cmd.parser |
765 | 793 |
parser.usage = '%prog <command> [options]' |
766 | 794 |
parser.print_help() |
767 |
|
|
795 |
|
|
768 | 796 |
commands = [] |
769 | 797 |
for cls in set(_cli_commands.values()): |
770 | 798 |
name = ', '.join(cls.commands) |
... | ... | |
772 | 800 |
commands.append(' %s %s' % (name.ljust(12), description)) |
773 | 801 |
print '\nCommands:\n' + '\n'.join(sorted(commands)) |
774 | 802 |
|
803 |
|
|
775 | 804 |
def print_dict(d, header='name', f=stdout, detail=True): |
776 | 805 |
header = header if header in d else 'subdir' |
777 | 806 |
if header and header in d: |
778 |
f.write('%s\n' %d.pop(header).encode('utf8')) |
|
807 |
f.write('%s\n' % d.pop(header).encode('utf8'))
|
|
779 | 808 |
if detail: |
780 | 809 |
patterns = ['^x_(account|container|object)_meta_(\w+)$'] |
781 | 810 |
patterns.append(patterns[0].replace('_', '-')) |
782 | 811 |
for key, val in sorted(d.items()): |
783 | 812 |
f.write('%s: %s\n' % (key.rjust(30), val)) |
784 | 813 |
|
814 |
|
|
785 | 815 |
def print_list(l, verbose=False, f=stdout, detail=True): |
786 | 816 |
for elem in l: |
787 | 817 |
#if it's empty string continue |
788 | 818 |
if not elem: |
789 | 819 |
continue |
790 |
if type(elem) == types.DictionaryType:
|
|
820 |
if isinstance(elem, types.DictionaryType):
|
|
791 | 821 |
print_dict(elem, f=f, detail=detail) |
792 |
elif type(elem) == types.StringType:
|
|
822 |
elif isinstance(elem, types.StringType):
|
|
793 | 823 |
if not verbose: |
794 | 824 |
elem = elem.split('Traceback')[0] |
795 | 825 |
f.write('%s\n' % elem) |
796 | 826 |
else: |
797 | 827 |
f.write('%s\n' % elem) |
798 | 828 |
|
829 |
|
|
799 | 830 |
def print_versions(data, f=stdout): |
800 | 831 |
if 'versions' not in data: |
801 |
f.write('%s\n' %data) |
|
832 |
f.write('%s\n' % data)
|
|
802 | 833 |
return |
803 | 834 |
f.write('versions:\n') |
804 | 835 |
for id, t in data['versions']: |
805 |
f.write('%s @ %s\n' % (str(id).rjust(30), datetime.fromtimestamp(float(t)))) |
|
836 |
f.write('%s @ %s\n' % (str(id).rjust(30), |
|
837 |
datetime.fromtimestamp(float(t)))) |
|
806 | 838 |
|
807 | 839 |
|
808 | 840 |
def main(): |
... | ... | |
812 | 844 |
except (IndexError, KeyError): |
813 | 845 |
print_usage() |
814 | 846 |
exit(1) |
815 |
|
|
847 |
|
|
816 | 848 |
cmd = cls(name, argv[2:]) |
817 |
|
|
849 |
|
|
818 | 850 |
try: |
819 | 851 |
cmd.execute(*cmd.args) |
820 | 852 |
except TypeError, e: |
Also available in: Unified diff