Revision 439826ec kamaki/cli/commands/pithos_cli.py
b/kamaki/cli/commands/pithos_cli.py | ||
---|---|---|
34 | 34 |
from kamaki.cli import command |
35 | 35 |
from kamaki.cli.command_tree import CommandTree |
36 | 36 |
from kamaki.cli.errors import raiseCLIError, CLISyntaxError |
37 |
from kamaki.cli.utils import format_size, print_dict, pretty_keys |
|
37 |
from kamaki.cli.utils import format_size, print_dict, pretty_keys, page_hold
|
|
38 | 38 |
from kamaki.cli.argument import FlagArgument, ValueArgument, IntArgument |
39 | 39 |
from kamaki.cli.argument import KeyValueArgument, DateArgument |
40 | 40 |
from kamaki.cli.argument import ProgressBarArgument |
41 | 41 |
from kamaki.cli.commands import _command_init |
42 | 42 |
from kamaki.clients.pithos import PithosClient, ClientError |
43 | 43 |
from kamaki.cli.utils import bold |
44 |
from sys import stdout |
|
44 |
from sys import stdout, stdin
|
|
45 | 45 |
from time import localtime, strftime |
46 | 46 |
from logging import getLogger |
47 | 47 |
|
... | ... | |
77 | 77 |
|
78 | 78 |
class SharingArgument(ValueArgument): |
79 | 79 |
"""Set sharing (read and/or write) groups |
80 |
|
|
80 |
. |
|
81 | 81 |
:value type: "read=term1,term2,... write=term1,term2,..." |
82 |
|
|
82 |
. |
|
83 | 83 |
:value returns: {'read':['term1', 'term2', ...], |
84 |
'write':['term1', 'term2', ...]}
|
|
84 |
. 'write':['term1', 'term2', ...]}
|
|
85 | 85 |
""" |
86 | 86 |
|
87 | 87 |
@property |
... | ... | |
117 | 117 |
|
118 | 118 |
class RangeArgument(ValueArgument): |
119 | 119 |
""" |
120 |
:value type: string of the form <start>-<end> |
|
121 |
where <start> and <end> are integers |
|
122 |
|
|
120 |
:value type: string of the form <start>-<end> where <start> and <end> are |
|
121 |
integers |
|
123 | 122 |
:value returns: the input string, after type checking <start> and <end> |
124 | 123 |
""" |
125 | 124 |
|
... | ... | |
162 | 161 |
|
163 | 162 |
def __init__(self, arguments={}): |
164 | 163 |
super(_store_account_command, self).__init__(arguments) |
165 |
self['account']=ValueArgument(
|
|
164 |
self['account'] = ValueArgument(
|
|
166 | 165 |
'Set user account (not permanent)', |
167 | 166 |
'--account') |
168 | 167 |
|
... | ... | |
175 | 174 |
class _store_container_command(_store_account_command): |
176 | 175 |
"""Base class for container level storage commands""" |
177 | 176 |
|
178 |
generic_err_details = ['Choose one of the following options:',
|
|
177 |
generic_err_details = ['To specify a container:',
|
|
179 | 178 |
' 1. Set store.container variable (permanent)', |
180 | 179 |
' /config set store.container <container>', |
181 | 180 |
' 2. --container=<container> (temporary, overrides 1)', |
182 |
' 3. Use <container>:<path> (temporary, overrides all)']
|
|
181 |
' 3. Use the container:path format (temporary, overrides all)']
|
|
183 | 182 |
container = None |
184 | 183 |
path = None |
185 | 184 |
|
186 | 185 |
def __init__(self, arguments={}): |
187 | 186 |
super(_store_container_command, self).__init__(arguments) |
188 |
self['container']=ValueArgument(
|
|
187 |
self['container'] = ValueArgument(
|
|
189 | 188 |
'Set container to work with (temporary)', |
190 | 189 |
'--container') |
191 | 190 |
|
... | ... | |
250 | 249 |
1 no parameters : containers in set account |
251 | 250 |
2. one parameter (container) or --container : contents of container |
252 | 251 |
3. <container>:<prefix> or --container=<container> <prefix>: objects in |
253 |
container starting with prefix
|
|
252 |
. container starting with prefix
|
|
254 | 253 |
""" |
255 | 254 |
|
256 | 255 |
arguments = dict( |
257 | 256 |
detail=FlagArgument('show detailed output', '-l'), |
258 |
show_size=ValueArgument('print output in chunks of size N', '-N'), |
|
259 |
limit=IntArgument('show limited output', '-n'), |
|
257 |
limit=IntArgument('limit the number of listed items', '-n'), |
|
260 | 258 |
marker=ValueArgument('show output greater that marker', '--marker'), |
261 |
prefix=ValueArgument('show output staritng with prefix', '--prefix'),
|
|
259 |
prefix=ValueArgument('show output starting with prefix', '--prefix'),
|
|
262 | 260 |
delimiter=ValueArgument('show output up to delimiter', '--delimiter'), |
263 | 261 |
path=ValueArgument( |
264 | 262 |
'show output starting with prefix up to /', |
... | ... | |
279 | 277 |
'--format'), |
280 | 278 |
shared=FlagArgument('show only shared', '--shared'), |
281 | 279 |
public=FlagArgument('show only public', '--public'), |
280 |
more=FlagArgument( |
|
281 |
'output results in pages (-n to set items per page, default 10)', |
|
282 |
'--more') |
|
282 | 283 |
) |
283 | 284 |
|
284 | 285 |
def print_objects(self, object_list): |
285 |
import sys |
|
286 |
try: |
|
287 |
limit = int(self['show_size']) |
|
288 |
except (AttributeError, TypeError): |
|
289 |
limit = len(object_list) + 1 |
|
286 |
limit = int(self['limit']) if self['limit'] > 0 else len(object_list) |
|
290 | 287 |
for index, obj in enumerate(object_list): |
291 | 288 |
if 'content_type' not in obj: |
292 | 289 |
continue |
... | ... | |
309 | 306 |
oname = '%s%s. %6s %s' % (empty_space, index, size, oname) |
310 | 307 |
oname += '/' if isDir else '' |
311 | 308 |
print(oname) |
312 |
if limit <= index < len(object_list) and index % limit == 0: |
|
313 |
print('(press "enter" to continue)') |
|
314 |
sys.stdin.read(1) |
|
309 |
if self['more']: |
|
310 |
page_hold(index, limit, len(object_list)) |
|
315 | 311 |
|
316 | 312 |
def print_containers(self, container_list): |
317 |
import sys |
|
318 |
try: |
|
319 |
limit = self['show_size'] |
|
320 |
limit = int(limit) |
|
321 |
except (AttributeError, TypeError): |
|
322 |
limit = len(container_list) + 1 |
|
313 |
limit = int(self['limit']) if self['limit'] > 0\ |
|
314 |
else len(container_list) |
|
323 | 315 |
for index, container in enumerate(container_list): |
324 | 316 |
if 'bytes' in container: |
325 | 317 |
size = format_size(container['bytes']) |
... | ... | |
337 | 329 |
% (cname, size, container['count'])) |
338 | 330 |
else: |
339 | 331 |
print(cname) |
340 |
if limit <= index < len(container_list) and index % limit == 0: |
|
341 |
print('(press "enter" to continue)') |
|
342 |
sys.stdin.read(1) |
|
332 |
if self['more']: |
|
333 |
page_hold(index + 1, limit, len(container_list)) |
|
343 | 334 |
|
344 | 335 |
def main(self, container____path__=None): |
345 | 336 |
super(self.__class__, self).main(container____path__) |
346 | 337 |
try: |
347 | 338 |
if self.container is None: |
348 | 339 |
r = self.client.account_get( |
349 |
limit=self['limit'], |
|
340 |
limit=False if self['more'] else self['limit'],
|
|
350 | 341 |
marker=self['marker'], |
351 | 342 |
if_modified_since=self['if_modified_since'], |
352 | 343 |
if_unmodified_since=self['if_unmodified_since'], |
... | ... | |
357 | 348 |
prefix = self.path if self.path\ |
358 | 349 |
else self['prefix'] |
359 | 350 |
r = self.client.container_get( |
360 |
limit=self['limit'], |
|
351 |
limit=False if self['more'] else self['limit'],
|
|
361 | 352 |
marker=self['marker'], |
362 | 353 |
prefix=prefix, |
363 | 354 |
delimiter=self['delimiter'], |
... | ... | |
370 | 361 |
self.print_objects(r.json) |
371 | 362 |
except ClientError as err: |
372 | 363 |
if err.status == 404: |
373 |
if 'Container does not exist' in ('%s' % err):
|
|
364 |
if 'container' in ('%s' % err).lower():
|
|
374 | 365 |
raiseCLIError( |
375 | 366 |
err, |
376 | 367 |
'No container %s in account %s'\ |
377 | 368 |
% (self.container, self.account), |
378 | 369 |
details=self.generic_err_details) |
379 |
elif 'Object does not exist' in ('%s' % err):
|
|
370 |
elif 'object' in ('%s' % err).lower():
|
|
380 | 371 |
raiseCLIError( |
381 | 372 |
err, |
382 | 373 |
'No object %s in %s\'s container %s'\ |
383 | 374 |
% (self.path, self.account, self.container), |
384 | 375 |
details=self.generic_err_details) |
385 | 376 |
raiseCLIError(err) |
377 |
except Exception as e: |
|
378 |
raiseCLIError(e) |
|
386 | 379 |
|
387 | 380 |
|
388 | 381 |
@command(pithos_cmds) |
... | ... | |
430 | 423 |
"""Copy an object from container to (another) container |
431 | 424 |
Use options: |
432 | 425 |
1. <container1>:<path1> <container2>[:path2] : from container1 to 2, if |
433 |
path2 is not given, target path will be container2:path1
|
|
426 |
path2 is not given, target path will be container2:path1 |
|
434 | 427 |
2. <container>:<path1> <container>:<path2> : make a copy in the same |
435 |
container
|
|
428 |
container |
|
436 | 429 |
3. Use --container= instead of <container1>, but only for the first |
437 |
parameter
|
|
430 |
parameter |
|
438 | 431 |
""" |
439 | 432 |
|
440 | 433 |
arguments = dict( |
... | ... | |
481 | 474 |
"""Copy an object |
482 | 475 |
Use options: |
483 | 476 |
1. <container1>:<path1> <container2>[:path2] : from container1 to 2, if |
484 |
path2 is not given, target path will be container2:path1
|
|
477 |
path2 is not given, target path will be container2:path1 |
|
485 | 478 |
2. <container>:<path1> <container>:<path2> : rename |
486 | 479 |
3. Use --container= instead of <container1>, but only for the first |
487 |
parameter
|
|
480 |
parameter |
|
488 | 481 |
""" |
489 | 482 |
|
490 | 483 |
arguments = dict( |
... | ... | |
571 | 564 |
arguments = dict( |
572 | 565 |
progress_bar=ProgressBarArgument( |
573 | 566 |
'do not show progress bar', |
574 |
'--no-progress-bar',
|
|
567 |
'--no-progress-bar', |
|
575 | 568 |
default=False) |
576 | 569 |
) |
577 | 570 |
|
... | ... | |
727 | 720 |
'--object-version') |
728 | 721 |
) |
729 | 722 |
|
730 |
|
|
731 | 723 |
def main(self, container___path): |
732 | 724 |
super(self.__class__, |
733 | 725 |
self).main(container___path, path_is_optional=False) |
... | ... | |
866 | 858 |
"""Delete a container [or an object]""" |
867 | 859 |
|
868 | 860 |
arguments = dict( |
869 |
until=DateArgument( 'remove history until that date', '--until'),
|
|
861 |
until=DateArgument('remove history until that date', '--until'), |
|
870 | 862 |
recursive=FlagArgument( |
871 | 863 |
'empty dir or container and delete (if dir)', |
872 | 864 |
('-r', '--recursive')) |
... | ... | |
874 | 866 |
|
875 | 867 |
def __init__(self, arguments={}): |
876 | 868 |
super(self.__class__, self).__init__(arguments) |
877 |
self['delimiter']=DelimiterArgument(
|
|
869 |
self['delimiter'] = DelimiterArgument(
|
|
878 | 870 |
self, |
879 | 871 |
parsed_name='--delimiter', |
880 | 872 |
help='delete objects prefixed with <object><delimiter>') |
... | ... | |
900 | 892 |
class store_purge(_store_container_command): |
901 | 893 |
"""Purge a container |
902 | 894 |
To completely erase a container: |
903 |
/store delete -r <container> |
|
904 |
/store purge <container |
|
895 |
. /store delete -r <container>
|
|
896 |
. /store purge <container
|
|
905 | 897 |
""" |
906 | 898 |
|
907 | 899 |
def main(self, container): |
... | ... | |
1260 | 1252 |
|
1261 | 1253 |
@command(pithos_cmds) |
1262 | 1254 |
class store_touch(_store_container_command): |
1263 |
"""Create an empty file of type application/octet-stream
|
|
1264 |
If object exists, this command will make reset it to 0 length
|
|
1255 |
"""Create an empty file |
|
1256 |
If object exists, this command will reset it to 0 length |
|
1265 | 1257 |
""" |
1266 | 1258 |
|
1267 |
def main(self, container___path, content_type='application/octet-stream'): |
|
1259 |
arguments = dict( |
|
1260 |
content_type=ValueArgument( |
|
1261 |
'Set content type (default: application/octet-stream)', |
|
1262 |
'--content-type', |
|
1263 |
default='application/octet-stream') |
|
1264 |
) |
|
1265 |
|
|
1266 |
def main(self, container___path): |
|
1268 | 1267 |
super(store_touch, self).main(container___path) |
1269 | 1268 |
try: |
1270 |
self.client.create_object(self.path, content_type)
|
|
1269 |
self.client.create_object(self.path, self['content_type'])
|
|
1271 | 1270 |
except ClientError as err: |
1272 | 1271 |
raiseCLIError(err) |
Also available in: Unified diff