1#!/usr/bin/python -u 2# Copyright (c) 2010-2012 OpenStack, LLC. 3# 4# Licensed under the Apache License, Version 2.0 (the "License"); 5# you may not use this file except in compliance with the License. 6# You may obtain a copy of the License at 7# 8# http://www.apache.org/licenses/LICENSE-2.0 9# 10# Unless required by applicable law or agreed to in writing, software 11# distributed under the License is distributed on an "AS IS" BASIS, 12# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 13# implied. 14# See the License for the specific language governing permissions and 15# limitations under the License. 16 17from __future__ import print_function, unicode_literals 18 19import argparse 20import getpass 21import io 22import json 23import logging 24import signal 25import socket 26import warnings 27 28from os import environ, walk, _exit as os_exit 29from os.path import isfile, isdir, join 30from six import text_type, PY2 31from six.moves.urllib.parse import unquote, urlparse 32from sys import argv as sys_argv, exit, stderr, stdin 33from time import gmtime, strftime 34 35from swiftclient import RequestException 36from swiftclient.utils import config_true_value, generate_temp_url, \ 37 prt_bytes, JSONableIterable 38from swiftclient.multithreading import OutputManager 39from swiftclient.exceptions import ClientException 40from swiftclient import __version__ as client_version 41from swiftclient.client import logger_settings as client_logger_settings, \ 42 parse_header_string 43from swiftclient.service import SwiftService, SwiftError, \ 44 SwiftUploadObject, get_conn, process_options 45from swiftclient.command_helpers import print_account_stats, \ 46 print_container_stats, print_object_stats 47 48try: 49 from shlex import quote as sh_quote 50except ImportError: 51 from pipes import quote as sh_quote 52 53BASENAME = 'swift' 54commands = ('delete', 'download', 'list', 'post', 'copy', 'stat', 'upload', 55 'capabilities', 'info', 'tempurl', 'auth', 'bash_completion') 56 57 58def immediate_exit(signum, frame): 59 stderr.write(" Aborted\n") 60 os_exit(2) 61 62 63st_delete_options = '''[--all] [--leave-segments] 64 [--object-threads <threads>] 65 [--container-threads <threads>] 66 [--header <header:value>] 67 [--prefix <prefix>] 68 [--versions] 69 [<container> [<object>] [--version-id <version_id>] [...]] 70''' 71 72st_delete_help = ''' 73Delete a container or objects within a container. 74 75Positional arguments: 76 [<container>] Name of container to delete from. 77 [<object>] Name of object to delete. Specify multiple times 78 for multiple objects. 79 80Optional arguments: 81 -a, --all Delete all containers and objects. Implies --versions. 82 --versions Delete all versions. 83 --leave-segments Do not delete segments of manifest objects. 84 -H, --header <header:value> 85 Adds a custom request header to use for deleting 86 objects or an entire container . 87 --object-threads <threads> 88 Number of threads to use for deleting objects. 89 Default is 10. 90 --container-threads <threads> 91 Number of threads to use for deleting containers. 92 Default is 10. 93 --prefix <prefix> Only delete objects beginning with <prefix>. 94 --version-id <version-id> 95 Delete specific version of a versioned object. 96'''.strip("\n") 97 98 99def st_delete(parser, args, output_manager, return_parser=False): 100 parser.add_argument( 101 '-a', '--all', action='store_true', dest='yes_all', 102 default=False, help='Delete all containers and objects.') 103 parser.add_argument('--versions', action='store_true', 104 help='delete all versions') 105 parser.add_argument( 106 '-p', '--prefix', dest='prefix', 107 help='Only delete items beginning with <prefix>.') 108 parser.add_argument( 109 '--version-id', action='store', default=None, 110 help='Delete a specific version of a versioned object') 111 parser.add_argument( 112 '-H', '--header', action='append', dest='header', 113 default=[], 114 help='Adds a custom request header to use for deleting objects ' 115 'or an entire container.') 116 parser.add_argument( 117 '--leave-segments', action='store_true', 118 dest='leave_segments', default=False, 119 help='Do not delete segments of manifest objects.') 120 parser.add_argument( 121 '--object-threads', type=int, 122 default=10, help='Number of threads to use for deleting objects. ' 123 'Its value must be a positive integer. Default is 10.') 124 parser.add_argument( 125 '--container-threads', type=int, 126 default=10, help='Number of threads to use for deleting containers. ' 127 'Its value must be a positive integer. Default is 10.') 128 129 # We return the parser to build up the bash_completion 130 if return_parser: 131 return parser 132 133 (options, args) = parse_args(parser, args) 134 args = args[1:] 135 if options['yes_all']: 136 options['versions'] = True 137 if (not args and not options['yes_all']) or (args and options['yes_all']): 138 output_manager.error('Usage: %s delete %s\n%s', 139 BASENAME, st_delete_options, 140 st_delete_help) 141 return 142 if options['versions'] and len(args) >= 2: 143 exit('--versions option not allowed for object deletes') 144 if options['version_id'] and len(args) < 2: 145 exit('--version-id option only allowed for object deletes') 146 147 if options['object_threads'] <= 0: 148 output_manager.error( 149 'ERROR: option --object-threads should be a positive integer.' 150 '\n\nUsage: %s delete %s\n%s', 151 BASENAME, st_delete_options, 152 st_delete_help) 153 return 154 155 if options['container_threads'] <= 0: 156 output_manager.error( 157 'ERROR: option --container-threads should be a positive integer.' 158 '\n\nUsage: %s delete %s\n%s', 159 BASENAME, st_delete_options, 160 st_delete_help) 161 return 162 163 options['object_dd_threads'] = options['object_threads'] 164 with SwiftService(options=options) as swift: 165 try: 166 if not args: 167 del_iter = swift.delete() 168 else: 169 container = args[0] 170 if '/' in container: 171 output_manager.error( 172 'WARNING: / in container name; you ' 173 "might have meant '%s' instead of '%s'." % 174 (container.replace('/', ' ', 1), container) 175 ) 176 return 177 objects = args[1:] 178 if objects: 179 del_iter = swift.delete(container=container, 180 objects=objects) 181 else: 182 del_iter = swift.delete(container=container) 183 184 for r in del_iter: 185 c = r.get('container', '') 186 o = r.get('object', '') 187 a = (' [after {0} attempts]'.format(r.get('attempts')) 188 if r.get('attempts', 1) > 1 else '') 189 190 if r['action'] == 'bulk_delete': 191 if r['success']: 192 objs = r.get('objects', []) 193 for o, err in r.get('result', {}).get('Errors', []): 194 # o will be of the form quote("/<cont>/<obj>") 195 o = unquote(o) 196 if PY2: 197 # In PY3, unquote(unicode) uses utf-8 like we 198 # want, but PY2 uses latin-1 199 o = o.encode('latin-1').decode('utf-8') 200 output_manager.error('Error Deleting: {0}: {1}' 201 .format(o[1:], err)) 202 try: 203 objs.remove(o[len(c) + 2:]) 204 except ValueError: 205 # shouldn't happen, but ignoring it won't hurt 206 pass 207 208 for o in objs: 209 if options['yes_all']: 210 p = '{0}/{1}'.format(c, o) 211 else: 212 p = o 213 output_manager.print_msg('{0}{1}'.format(p, a)) 214 else: 215 for o in r.get('objects', []): 216 output_manager.error('Error Deleting: {0}/{1}: {2}' 217 .format(c, o, r['error'])) 218 else: 219 if r['success']: 220 if options['verbose']: 221 if r['action'] == 'delete_object': 222 if options['yes_all']: 223 p = '{0}/{1}'.format(c, o) 224 else: 225 p = o 226 elif r['action'] == 'delete_segment': 227 p = '{0}/{1}'.format(c, o) 228 elif r['action'] == 'delete_container': 229 p = c 230 231 output_manager.print_msg('{0}{1}'.format(p, a)) 232 else: 233 p = '{0}/{1}'.format(c, o) if o else c 234 output_manager.error('Error Deleting: {0}: {1}' 235 .format(p, r['error'])) 236 except SwiftError as err: 237 output_manager.error(err.value) 238 239 240st_download_options = '''[--all] [--marker <marker>] [--prefix <prefix>] 241 [--output <out_file>] [--output-dir <out_directory>] 242 [--object-threads <threads>] [--ignore-checksum] 243 [--container-threads <threads>] [--no-download] 244 [--skip-identical] [--remove-prefix] 245 [--version-id <version_id>] 246 [--header <header:value>] [--no-shuffle] 247 [<container> [<object>] [...]] 248''' 249 250st_download_help = ''' 251Download objects from containers. 252 253Positional arguments: 254 [<container>] Name of container to download from. To download a 255 whole account, omit this and specify --all. 256 [<object>] Name of object to download. Specify multiple times 257 for multiple objects. Omit this to download all 258 objects from the container. 259 260Optional arguments: 261 -a, --all Indicates that you really want to download 262 everything in the account. 263 -m, --marker <marker> Marker to use when starting a container or account 264 download. 265 -p, --prefix <prefix> Only download items beginning with <prefix> 266 -r, --remove-prefix An optional flag for --prefix <prefix>, use this 267 option to download items without <prefix> 268 -o, --output <out_file> 269 For a single file download, stream the output to 270 <out_file>. Specifying "-" as <out_file> will 271 redirect to stdout. 272 -D, --output-dir <out_directory> 273 An optional directory to which to store objects. 274 By default, all objects are recreated in the current 275 directory. 276 --object-threads <threads> 277 Number of threads to use for downloading objects. 278 Default is 10. 279 --container-threads <threads> 280 Number of threads to use for downloading containers. 281 Default is 10. 282 --no-download Perform download(s), but don't actually write anything 283 to disk. 284 -H, --header <header:value> 285 Adds a customized request header to the query, like 286 "Range" or "If-Match". This option may be repeated. 287 Example: --header "content-type:text/plain" 288 --skip-identical Skip downloading files that are identical on both 289 sides. 290 --version-id <version-id> 291 Download specific version of a versioned object. 292 --ignore-checksum Turn off checksum validation for downloads. 293 --no-shuffle By default, when downloading a complete account or 294 container, download order is randomised in order to 295 reduce the load on individual drives when multiple 296 clients are executed simultaneously to download the 297 same set of objects (e.g. a nightly automated download 298 script to multiple servers). Enable this option to 299 submit download jobs to the thread pool in the order 300 they are listed in the object store. 301 --ignore-mtime Ignore the 'X-Object-Meta-Mtime' header when 302 downloading an object. Instead, create atime and mtime 303 with fresh timestamps. 304'''.strip("\n") 305 306 307def st_download(parser, args, output_manager, return_parser=False): 308 parser.add_argument( 309 '-a', '--all', action='store_true', dest='yes_all', 310 default=False, help='Indicates that you really want to download ' 311 'everything in the account.') 312 parser.add_argument( 313 '-m', '--marker', dest='marker', 314 default='', help='Marker to use when starting a container or ' 315 'account download.') 316 parser.add_argument( 317 '-p', '--prefix', dest='prefix', 318 help='Only download items beginning with the <prefix>.') 319 parser.add_argument( 320 '-o', '--output', dest='out_file', help='For a single ' 321 'download, stream the output to <out_file>. ' 322 'Specifying "-" as <out_file> will redirect to stdout.') 323 parser.add_argument( 324 '-D', '--output-dir', dest='out_directory', 325 help='An optional directory to which to store objects. ' 326 'By default, all objects are recreated in the current directory.') 327 parser.add_argument( 328 '-r', '--remove-prefix', action='store_true', dest='remove_prefix', 329 default=False, help='An optional flag for --prefix <prefix>, ' 330 'use this option to download items without <prefix>.') 331 parser.add_argument( 332 '--object-threads', type=int, 333 default=10, help='Number of threads to use for downloading objects. ' 334 'Its value must be a positive integer. Default is 10.') 335 parser.add_argument( 336 '--container-threads', type=int, default=10, 337 help='Number of threads to use for downloading containers. ' 338 'Its value must be a positive integer. Default is 10.') 339 parser.add_argument( 340 '--no-download', action='store_true', 341 default=False, 342 help="Perform download(s), but don't actually write anything to disk.") 343 parser.add_argument( 344 '-H', '--header', action='append', dest='header', 345 default=[], 346 help='Adds a customized request header to the query, like "Range" or ' 347 '"If-Match". This option may be repeated. ' 348 'Example: --header "content-type:text/plain"') 349 parser.add_argument( 350 '--skip-identical', action='store_true', dest='skip_identical', 351 default=False, help='Skip downloading files that are identical on ' 352 'both sides.') 353 parser.add_argument( 354 '--version-id', action='store', default=None, 355 help='Download a specific version of a versioned object') 356 parser.add_argument( 357 '--ignore-checksum', action='store_false', dest='checksum', 358 default=True, help='Turn off checksum validation for downloads.') 359 parser.add_argument( 360 '--no-shuffle', action='store_false', dest='shuffle', 361 default=True, help='By default, download order is randomised in order ' 362 'to reduce the load on individual drives when multiple clients are ' 363 'executed simultaneously to download the same set of objects (e.g. a ' 364 'nightly automated download script to multiple servers). Enable this ' 365 'option to submit download jobs to the thread pool in the order they ' 366 'are listed in the object store.') 367 parser.add_argument( 368 '--ignore-mtime', action='store_true', dest='ignore_mtime', 369 default=False, help='By default, the object-meta-mtime header is used ' 370 'to store the access and modified timestamp for the downloaded file. ' 371 'With this option, the header is ignored and the timestamps are ' 372 'created freshly.') 373 374 # We return the parser to build up the bash_completion 375 if return_parser: 376 return parser 377 378 (options, args) = parse_args(parser, args) 379 args = args[1:] 380 if options['out_file'] == '-': 381 options['verbose'] = 0 382 383 if options['out_file'] and len(args) != 2: 384 exit('-o option only allowed for single file downloads') 385 386 if not options['prefix']: 387 options['remove_prefix'] = False 388 389 if options['out_directory'] and len(args) == 2: 390 exit('Please use -o option for single file downloads and renames') 391 392 if (not args and not options['yes_all']) or (args and options['yes_all']): 393 output_manager.error('Usage: %s download %s\n%s', BASENAME, 394 st_download_options, st_download_help) 395 return 396 if options['version_id'] and len(args) < 2: 397 exit('--version-id option only allowed for object downloads') 398 399 if options['object_threads'] <= 0: 400 output_manager.error( 401 'ERROR: option --object-threads should be a positive integer.\n\n' 402 'Usage: %s download %s\n%s', BASENAME, 403 st_download_options, st_download_help) 404 return 405 406 if options['container_threads'] <= 0: 407 output_manager.error( 408 'ERROR: option --container-threads should be a positive integer.' 409 '\n\nUsage: %s download %s\n%s', BASENAME, 410 st_download_options, st_download_help) 411 return 412 413 options['object_dd_threads'] = options['object_threads'] 414 with SwiftService(options=options) as swift: 415 try: 416 if not args: 417 down_iter = swift.download() 418 else: 419 container = args[0] 420 if '/' in container: 421 output_manager.error( 422 'WARNING: / in container name; you ' 423 "might have meant '%s' instead of '%s'." % 424 (container.replace('/', ' ', 1), container) 425 ) 426 return 427 objects = args[1:] 428 if not objects: 429 down_iter = swift.download(container) 430 else: 431 down_iter = swift.download(container, objects) 432 433 for down in down_iter: 434 if options['out_file'] == '-' and 'contents' in down: 435 contents = down['contents'] 436 for chunk in contents: 437 output_manager.print_raw(chunk) 438 else: 439 if down['success']: 440 if options['verbose']: 441 start_time = down['start_time'] 442 headers_receipt = \ 443 down['headers_receipt'] - start_time 444 auth_time = down['auth_end_time'] - start_time 445 finish_time = down['finish_time'] 446 read_length = down['read_length'] 447 attempts = down['attempts'] 448 total_time = finish_time - start_time 449 down_time = total_time - auth_time 450 _mega = 1000000 451 if down['pseudodir']: 452 time_str = ( 453 'auth %.3fs, headers %.3fs, total %.3fs, ' 454 'pseudo' % ( 455 auth_time, headers_receipt, 456 total_time 457 ) 458 ) 459 else: 460 speed = float(read_length) / down_time / _mega 461 time_str = ( 462 'auth %.3fs, headers %.3fs, total %.3fs, ' 463 '%.3f MB/s' % ( 464 auth_time, headers_receipt, 465 total_time, speed 466 ) 467 ) 468 path = down['path'] 469 if attempts > 1: 470 output_manager.print_msg( 471 '%s [%s after %d attempts]', 472 path, time_str, attempts 473 ) 474 else: 475 output_manager.print_msg( 476 '%s [%s]', path, time_str 477 ) 478 else: 479 error = down['error'] 480 path = down['path'] 481 container = down['container'] 482 obj = down['object'] 483 if isinstance(error, ClientException): 484 if error.http_status == 304 and \ 485 options['skip_identical']: 486 output_manager.print_msg( 487 "Skipped identical file '%s'", path) 488 continue 489 if error.http_status == 404: 490 output_manager.error( 491 "Object '%s/%s' not found", container, obj) 492 continue 493 output_manager.error( 494 "Error downloading object '%s/%s': %s", 495 container, obj, error) 496 497 except SwiftError as e: 498 output_manager.error(e.value) 499 except Exception as e: 500 output_manager.error(e) 501 502 503st_list_options = '''[--long] [--lh] [--totals] [--prefix <prefix>] 504 [--delimiter <delimiter>] [--header <header:value>] 505 [--versions] [<container>] 506''' 507 508st_list_help = ''' 509Lists the containers for the account or the objects for a container. 510 511Positional arguments: 512 [<container>] Name of container to list object in. 513 514Optional arguments: 515 -l, --long Long listing format, similar to ls -l. 516 --lh Report sizes in human readable format similar to 517 ls -lh. 518 -t, --totals Used with -l or --lh, only report totals. 519 -p <prefix>, --prefix <prefix> 520 Only list items beginning with the prefix. 521 -d <delim>, --delimiter <delim> 522 Roll up items with the given delimiter. For containers 523 only. See OpenStack Swift API documentation for what 524 this means. 525 -j, --json Display listing information in json 526 --versions Display listing information for all versions 527 -H, --header <header:value> 528 Adds a custom request header to use for listing. 529'''.strip('\n') 530 531 532def st_list(parser, args, output_manager, return_parser=False): 533 534 def _print_stats(options, stats, human): 535 total_count = total_bytes = 0 536 container = stats.get("container", None) 537 for item in stats["listing"]: 538 item_name = item.get('name') 539 if not options['long'] and not human and not options['versions']: 540 output_manager.print_msg(item.get('name', item.get('subdir'))) 541 else: 542 if not container: # listing containers 543 item_bytes = item.get('bytes') 544 byte_str = prt_bytes(item_bytes, human) 545 count = item.get('count') 546 total_count += count 547 try: 548 meta = item.get('meta') 549 utc = gmtime(float(meta.get('x-timestamp'))) 550 datestamp = strftime('%Y-%m-%d %H:%M:%S', utc) 551 except TypeError: 552 datestamp = '????-??-?? ??:??:??' 553 if not options['totals']: 554 output_manager.print_msg( 555 "%5s %s %s %s", count, byte_str, 556 datestamp, item_name) 557 else: # list container contents 558 subdir = item.get('subdir') 559 content_type = item.get('content_type') 560 if subdir is None: 561 item_bytes = item.get('bytes') 562 byte_str = prt_bytes(item_bytes, human) 563 date, xtime = item.get('last_modified').split('T') 564 xtime = xtime.split('.')[0] 565 else: 566 item_bytes = 0 567 byte_str = prt_bytes(item_bytes, human) 568 date = xtime = '' 569 item_name = subdir 570 if not options['totals']: 571 if options['versions']: 572 output_manager.print_msg( 573 "%s %10s %8s %16s %24s %s", 574 byte_str, date, xtime, 575 item.get('version_id', 'null'), 576 content_type, item_name) 577 else: 578 output_manager.print_msg( 579 "%s %10s %8s %24s %s", 580 byte_str, date, xtime, content_type, item_name) 581 total_bytes += item_bytes 582 583 # report totals 584 if options['long'] or human: 585 if not container: 586 output_manager.print_msg( 587 "%5s %s", prt_bytes(total_count, True), 588 prt_bytes(total_bytes, human)) 589 else: 590 output_manager.print_msg( 591 prt_bytes(total_bytes, human)) 592 593 parser.add_argument( 594 '-l', '--long', dest='long', action='store_true', default=False, 595 help='Long listing format, similar to ls -l.') 596 parser.add_argument( 597 '--lh', dest='human', action='store_true', 598 default=False, help='Report sizes in human readable format, ' 599 "similar to ls -lh.") 600 parser.add_argument( 601 '-t', '--totals', dest='totals', 602 help='used with -l or --lh, only report totals.', 603 action='store_true', default=False) 604 parser.add_argument( 605 '-p', '--prefix', dest='prefix', 606 help='Only list items beginning with the prefix.') 607 parser.add_argument( 608 '-d', '--delimiter', dest='delimiter', 609 help='Roll up items with the given delimiter. For containers ' 610 'only. See OpenStack Swift API documentation for ' 611 'what this means.') 612 parser.add_argument('-j', '--json', action='store_true', 613 help='print listing information in json') 614 parser.add_argument('--versions', action='store_true', 615 help='display all versions') 616 parser.add_argument( 617 '-H', '--header', action='append', dest='header', 618 default=[], 619 help='Adds a custom request header to use for listing.') 620 621 # We return the parser to build up the bash_completion 622 if return_parser: 623 return parser 624 625 options, args = parse_args(parser, args) 626 args = args[1:] 627 if options['delimiter'] and not args: 628 exit('-d option only allowed for container listings') 629 if options['versions'] and not args: 630 exit('--versions option only allowed for container listings') 631 632 human = options.pop('human') 633 if human: 634 options['long'] = True 635 636 if options['totals'] and not options['long']: 637 output_manager.error( 638 "Listing totals only works with -l or --lh.") 639 return 640 641 with SwiftService(options=options) as swift: 642 try: 643 if not args: 644 stats_parts_gen = swift.list() 645 else: 646 container = args[0] 647 args = args[1:] 648 if "/" in container or args: 649 output_manager.error( 650 'Usage: %s list %s\n%s', BASENAME, 651 st_list_options, st_list_help) 652 return 653 else: 654 stats_parts_gen = swift.list(container=container) 655 656 if options.get('json', False): 657 def listing(stats_parts_gen=stats_parts_gen): 658 for stats in stats_parts_gen: 659 if stats["success"]: 660 for item in stats['listing']: 661 yield item 662 else: 663 raise stats["error"] 664 665 json.dump( 666 JSONableIterable(listing()), output_manager.print_stream, 667 sort_keys=True, indent=2) 668 output_manager.print_msg('') 669 return 670 for stats in stats_parts_gen: 671 if stats["success"]: 672 _print_stats(options, stats, human) 673 else: 674 raise stats["error"] 675 676 except SwiftError as e: 677 output_manager.error(e.value) 678 679 680st_stat_options = '''[--lh] [--header <header:value>] 681 [--version-id <version_id>] 682 [<container> [<object>]] 683''' 684 685st_stat_help = ''' 686Displays information for the account, container, or object. 687 688Positional arguments: 689 [<container>] Name of container to stat from. 690 [<object>] Name of object to stat. 691 692Optional arguments: 693 --lh Report sizes in human readable format similar to 694 ls -lh. 695 --version-id <version-id> 696 Report stat of specific version of a versioned object. 697 -H, --header <header:value> 698 Adds a custom request header to use for stat. 699'''.strip('\n') 700 701 702def st_stat(parser, args, output_manager, return_parser=False): 703 parser.add_argument( 704 '--lh', dest='human', action='store_true', default=False, 705 help='Report sizes in human readable format similar to ls -lh.') 706 parser.add_argument( 707 '--version-id', action='store', default=None, 708 help='Report stat of a specific version of a versioned object') 709 parser.add_argument( 710 '-H', '--header', action='append', dest='header', 711 default=[], 712 help='Adds a custom request header to use for stat.') 713 714 # We return the parser to build up the bash_completion 715 if return_parser: 716 return parser 717 718 options, args = parse_args(parser, args) 719 args = args[1:] 720 if options['version_id'] and len(args) < 2: 721 exit('--version-id option only allowed for object stats') 722 723 with SwiftService(options=options) as swift: 724 try: 725 if not args: 726 stat_result = swift.stat() 727 if not stat_result['success']: 728 raise stat_result['error'] 729 items = stat_result['items'] 730 headers = stat_result['headers'] 731 print_account_stats(items, headers, output_manager) 732 else: 733 container = args[0] 734 if '/' in container: 735 output_manager.error( 736 'WARNING: / in container name; you might have ' 737 "meant '%s' instead of '%s'." % 738 (container.replace('/', ' ', 1), container)) 739 return 740 args = args[1:] 741 if not args: 742 stat_result = swift.stat(container=container) 743 if not stat_result['success']: 744 raise stat_result['error'] 745 items = stat_result['items'] 746 headers = stat_result['headers'] 747 print_container_stats(items, headers, output_manager) 748 else: 749 if len(args) == 1: 750 objects = [args[0]] 751 stat_results = swift.stat( 752 container=container, objects=objects) 753 for stat_result in stat_results: # only 1 result 754 if stat_result["success"]: 755 items = stat_result['items'] 756 headers = stat_result['headers'] 757 print_object_stats( 758 items, headers, output_manager 759 ) 760 else: 761 raise(stat_result["error"]) 762 else: 763 output_manager.error( 764 'Usage: %s stat %s\n%s', BASENAME, 765 st_stat_options, st_stat_help) 766 767 except SwiftError as e: 768 output_manager.error(e.value) 769 770 771st_post_options = '''[--read-acl <acl>] [--write-acl <acl>] [--sync-to <sync-to>] 772 [--sync-key <sync-key>] [--meta <name:value>] 773 [--header <header>] 774 [<container> [<object>]] 775''' 776 777st_post_help = ''' 778Updates meta information for the account, container, or object. 779If the container is not found, it will be created automatically. 780 781Positional arguments: 782 [<container>] Name of container to post to. 783 [<object>] Name of object to post. 784 785Optional arguments: 786 -r, --read-acl <acl> Read ACL for containers. Quick summary of ACL syntax: 787 .r:*, .r:-.example.com, .r:www.example.com, 788 account1 (v1.0 identity API only), 789 account1:*, account2:user2 (v2.0+ identity API). 790 -w, --write-acl <acl> Write ACL for containers. Quick summary of ACL syntax: 791 account1 (v1.0 identity API only), 792 account1:*, account2:user2 (v2.0+ identity API). 793 -t, --sync-to <sync-to> 794 Sync To for containers, for multi-cluster replication. 795 -k, --sync-key <sync-key> 796 Sync Key for containers, for multi-cluster replication. 797 -m, --meta <name:value> 798 Sets a meta data item. This option may be repeated. 799 Example: -m Color:Blue -m Size:Large 800 -H, --header <header:value> 801 Adds a customized request header. 802 This option may be repeated. Example 803 -H "content-type:text/plain" -H "Content-Length: 4000" 804'''.strip('\n') 805 806 807def st_post(parser, args, output_manager, return_parser=False): 808 parser.add_argument( 809 '-r', '--read-acl', dest='read_acl', help='Read ACL for containers. ' 810 'Quick summary of ACL syntax: .r:*, .r:-.example.com, ' 811 '.r:www.example.com, account1, account2:user2') 812 parser.add_argument( 813 '-w', '--write-acl', dest='write_acl', help='Write ACL for ' 814 'containers. Quick summary of ACL syntax: account1, ' 815 'account2:user2') 816 parser.add_argument( 817 '-t', '--sync-to', dest='sync_to', help='Sets the ' 818 'Sync To for containers, for multi-cluster replication.') 819 parser.add_argument( 820 '-k', '--sync-key', dest='sync_key', help='Sets the ' 821 'Sync Key for containers, for multi-cluster replication.') 822 parser.add_argument( 823 '-m', '--meta', action='append', dest='meta', default=[], 824 help='Sets a meta data item. This option may be repeated. ' 825 'Example: -m Color:Blue -m Size:Large') 826 parser.add_argument( 827 '-H', '--header', action='append', dest='header', 828 default=[], help='Adds a customized request header. ' 829 'This option may be repeated. ' 830 'Example: -H "content-type:text/plain" ' 831 '-H "Content-Length: 4000"') 832 833 # We return the parser to build up the bash_completion 834 if return_parser: 835 return parser 836 837 (options, args) = parse_args(parser, args) 838 args = args[1:] 839 if (options['read_acl'] or options['write_acl'] or options['sync_to'] or 840 options['sync_key']) and not args: 841 exit('-r, -w, -t, and -k options only allowed for containers') 842 843 with SwiftService(options=options) as swift: 844 try: 845 if not args: 846 result = swift.post() 847 else: 848 container = args[0] 849 if '/' in container: 850 output_manager.error( 851 'WARNING: / in container name; you might have ' 852 "meant '%s' instead of '%s'." % 853 (args[0].replace('/', ' ', 1), args[0])) 854 return 855 args = args[1:] 856 if args: 857 if len(args) == 1: 858 objects = [args[0]] 859 results_iterator = swift.post( 860 container=container, objects=objects 861 ) 862 result = next(results_iterator) 863 else: 864 output_manager.error( 865 'Usage: %s post %s\n%s', BASENAME, 866 st_post_options, st_post_help) 867 return 868 else: 869 result = swift.post(container=container) 870 if not result["success"]: 871 raise(result["error"]) 872 873 except SwiftError as e: 874 output_manager.error(e.value) 875 876 877st_copy_options = '''[--destination </container/object>] [--fresh-metadata] 878 [--meta <name:value>] [--header <header>] <container> 879 <object> [<object>] [...] 880''' 881 882st_copy_help = ''' 883Copies object to new destination, optionally updates objects metadata. 884If destination is not set, will update metadata of object 885 886Positional arguments: 887 <container> Name of container to copy from. 888 <object> Name of object to copy. Specify multiple times 889 for multiple objects 890 891Optional arguments: 892 -d, --destination </container[/object]> 893 The container and name of the destination object. Name 894 of destination object can be omitted, then will be 895 same as name of source object. Supplying multiple 896 objects and destination with object name is invalid. 897 -M, --fresh-metadata Copy the object without any existing metadata, 898 If not set, metadata will be preserved or appended 899 -m, --meta <name:value> 900 Sets a meta data item. This option may be repeated. 901 Example: -m Color:Blue -m Size:Large 902 -H, --header <header:value> 903 Adds a customized request header. 904 This option may be repeated. Example 905 -H "content-type:text/plain" -H "Content-Length: 4000" 906'''.strip('\n') 907 908 909def st_copy(parser, args, output_manager, return_parser=False): 910 parser.add_argument( 911 '-d', '--destination', help='The container and name of the ' 912 'destination object') 913 parser.add_argument( 914 '-M', '--fresh-metadata', action='store_true', 915 help='Copy the object without any existing metadata', default=False) 916 parser.add_argument( 917 '-m', '--meta', action='append', dest='meta', default=[], 918 help='Sets a meta data item. This option may be repeated. ' 919 'Example: -m Color:Blue -m Size:Large') 920 parser.add_argument( 921 '-H', '--header', action='append', dest='header', 922 default=[], help='Adds a customized request header. ' 923 'This option may be repeated. ' 924 'Example: -H "content-type:text/plain" ' 925 '-H "Content-Length: 4000"') 926 927 # We return the parser to build up the bash_completion 928 if return_parser: 929 return parser 930 931 (options, args) = parse_args(parser, args) 932 args = args[1:] 933 934 with SwiftService(options=options) as swift: 935 try: 936 if len(args) >= 2: 937 container = args[0] 938 if '/' in container: 939 output_manager.error( 940 'WARNING: / in container name; you might have ' 941 "meant '%s' instead of '%s'." % 942 (args[0].replace('/', ' ', 1), args[0])) 943 return 944 objects = [arg for arg in args[1:]] 945 946 for r in swift.copy( 947 container=container, objects=objects, 948 options=options): 949 if r['success']: 950 if options['verbose']: 951 if r['action'] == 'copy_object': 952 output_manager.print_msg( 953 '%s/%s copied to %s' % ( 954 r['container'], 955 r['object'], 956 r['destination'] or '<self>')) 957 if r['action'] == 'create_container': 958 output_manager.print_msg( 959 'created container %s' % r['container'] 960 ) 961 else: 962 error = r['error'] 963 if 'action' in r and r['action'] == 'create_container': 964 # it is not an error to be unable to create the 965 # container so print a warning and carry on 966 output_manager.warning( 967 'Warning: failed to create container ' 968 "'%s': %s", container, error 969 ) 970 else: 971 output_manager.error("%s" % error) 972 else: 973 output_manager.error( 974 'Usage: %s copy %s\n%s', BASENAME, 975 st_copy_options, st_copy_help) 976 return 977 978 except SwiftError as e: 979 output_manager.error(e.value) 980 981 982st_upload_options = '''[--changed] [--skip-identical] [--segment-size <size>] 983 [--segment-container <container>] [--leave-segments] 984 [--object-threads <thread>] [--segment-threads <threads>] 985 [--meta <name:value>] [--header <header>] [--use-slo] 986 [--ignore-checksum] [--object-name <object-name>] 987 <container> <file_or_directory> [<file_or_directory>] [...] 988''' 989 990st_upload_help = ''' 991Uploads specified files and directories to the given container. 992 993Positional arguments: 994 <container> Name of container to upload to. 995 <file_or_directory> Name of file or directory to upload. Specify multiple 996 times for multiple uploads. If "-" is specified, reads 997 content from standard input (--object-name is required 998 in this case). 999 1000Optional arguments: 1001 -c, --changed Only upload files that have changed since the last 1002 upload. 1003 --skip-identical Skip uploading files that are identical on both sides. 1004 -S, --segment-size <size> 1005 Upload files in segments no larger than <size> (in 1006 Bytes) and then create a "manifest" file that will 1007 download all the segments as if it were the original 1008 file. 1009 --segment-container <container> 1010 Upload the segments into the specified container. If 1011 not specified, the segments will be uploaded to a 1012 <container>_segments container to not pollute the 1013 main <container> listings. 1014 --leave-segments Indicates that you want the older segments of manifest 1015 objects left alone (in the case of overwrites). 1016 --object-threads <threads> 1017 Number of threads to use for uploading full objects. 1018 Default is 10. 1019 --segment-threads <threads> 1020 Number of threads to use for uploading object segments. 1021 Default is 10. 1022 -m, --meta <name:value> 1023 Sets a meta data item. This option may be repeated. 1024 Example: -m Color:Blue -m Size:Large 1025 -H, --header <header:value> 1026 Adds a customized request header. This option may be 1027 repeated. Example: -H "content-type:text/plain" 1028 -H "Content-Length: 4000". 1029 --use-slo When used in conjunction with --segment-size it will 1030 create a Static Large Object instead of the default 1031 Dynamic Large Object. 1032 --object-name <object-name> 1033 Upload file and name object to <object-name> or upload 1034 dir and use <object-name> as object prefix instead of 1035 folder name. 1036 --ignore-checksum Turn off checksum validation for uploads. 1037'''.strip('\n') 1038 1039 1040def st_upload(parser, args, output_manager, return_parser=False): 1041 DEFAULT_STDIN_SEGMENT = 10 * 1024 * 1024 1042 1043 parser.add_argument( 1044 '-c', '--changed', action='store_true', dest='changed', 1045 default=False, help='Only upload files that have changed since ' 1046 'the last upload.') 1047 parser.add_argument( 1048 '--skip-identical', action='store_true', dest='skip_identical', 1049 default=False, help='Skip uploading files that are identical on ' 1050 'both sides.') 1051 parser.add_argument( 1052 '-S', '--segment-size', dest='segment_size', help='Upload files ' 1053 'in segments no larger than <size> (in Bytes) and then create a ' 1054 '"manifest" file that will download all the segments as if it were ' 1055 'the original file. Sizes may also be expressed as bytes with the ' 1056 'B suffix, kilobytes with the K suffix, megabytes with the M suffix ' 1057 'or gigabytes with the G suffix.') 1058 parser.add_argument( 1059 '-C', '--segment-container', dest='segment_container', 1060 help='Upload the segments into the specified container. ' 1061 'If not specified, the segments will be uploaded to a ' 1062 '<container>_segments container to not pollute the main ' 1063 '<container> listings.') 1064 parser.add_argument( 1065 '--leave-segments', action='store_true', 1066 dest='leave_segments', default=False, help='Indicates that you want ' 1067 'the older segments of manifest objects left alone (in the case of ' 1068 'overwrites).') 1069 parser.add_argument( 1070 '--object-threads', type=int, default=10, 1071 help='Number of threads to use for uploading full objects. ' 1072 'Its value must be a positive integer. Default is 10.') 1073 parser.add_argument( 1074 '--segment-threads', type=int, default=10, 1075 help='Number of threads to use for uploading object segments. ' 1076 'Its value must be a positive integer. Default is 10.') 1077 parser.add_argument( 1078 '-m', '--meta', action='append', dest='meta', default=[], 1079 help='Sets a meta data item. This option may be repeated. ' 1080 'Example: -m Color:Blue -m Size:Large') 1081 parser.add_argument( 1082 '-H', '--header', action='append', dest='header', 1083 default=[], help='Set request headers with the syntax header:value. ' 1084 ' This option may be repeated. Example: -H "content-type:text/plain" ' 1085 '-H "Content-Length: 4000"') 1086 parser.add_argument( 1087 '--use-slo', action='store_true', default=False, 1088 help='When used in conjunction with --segment-size, it will ' 1089 'create a Static Large Object instead of the default ' 1090 'Dynamic Large Object.') 1091 parser.add_argument( 1092 '--object-name', dest='object_name', 1093 help='Upload file and name object to <object-name> or upload dir and ' 1094 'use <object-name> as object prefix instead of folder name.') 1095 parser.add_argument( 1096 '--ignore-checksum', dest='checksum', default=True, 1097 action='store_false', help='Turn off checksum validation for uploads.') 1098 1099 # We return the parser to build up the bash_completion 1100 if return_parser: 1101 return parser 1102 1103 options, args = parse_args(parser, args) 1104 args = args[1:] 1105 if len(args) < 2: 1106 output_manager.error( 1107 'Usage: %s upload %s\n%s', BASENAME, st_upload_options, 1108 st_upload_help) 1109 return 1110 else: 1111 container = args[0] 1112 files = args[1:] 1113 from_stdin = '-' in files 1114 if from_stdin and len(files) > 1: 1115 output_manager.error( 1116 'upload from stdin cannot be used along with other files') 1117 return 1118 1119 if options['object_name'] is not None: 1120 if len(files) > 1: 1121 output_manager.error('object-name only be used with 1 file or dir') 1122 return 1123 else: 1124 orig_path = files[0] 1125 elif from_stdin: 1126 output_manager.error( 1127 'object-name must be specified with uploads from stdin') 1128 return 1129 1130 if options['segment_size']: 1131 try: 1132 # If segment size only has digits assume it is bytes 1133 int(options['segment_size']) 1134 except ValueError: 1135 try: 1136 size_mod = "BKMG".index(options['segment_size'][-1].upper()) 1137 multiplier = int(options['segment_size'][:-1]) 1138 except ValueError: 1139 output_manager.error("Invalid segment size") 1140 return 1141 1142 options['segment_size'] = str((1024 ** size_mod) * multiplier) 1143 if int(options['segment_size']) <= 0: 1144 output_manager.error("segment-size should be positive") 1145 return 1146 1147 if options['object_threads'] <= 0: 1148 output_manager.error( 1149 'ERROR: option --object-threads should be a positive integer.' 1150 '\n\nUsage: %s upload %s\n%s', BASENAME, st_upload_options, 1151 st_upload_help) 1152 return 1153 1154 if options['segment_threads'] <= 0: 1155 output_manager.error( 1156 'ERROR: option --segment-threads should be a positive integer.' 1157 '\n\nUsage: %s upload %s\n%s', BASENAME, st_upload_options, 1158 st_upload_help) 1159 return 1160 1161 if from_stdin: 1162 if not options['use_slo']: 1163 options['use_slo'] = True 1164 if not options['segment_size']: 1165 options['segment_size'] = DEFAULT_STDIN_SEGMENT 1166 1167 options['object_uu_threads'] = options['object_threads'] 1168 with SwiftService(options=options) as swift: 1169 try: 1170 objs = [] 1171 dir_markers = [] 1172 for f in files: 1173 if f == '-': 1174 fd = io.open(stdin.fileno(), mode='rb') 1175 objs.append(SwiftUploadObject( 1176 fd, object_name=options['object_name'])) 1177 # We ensure that there is exactly one "file" to upload in 1178 # this case -- stdin 1179 break 1180 1181 if isfile(f): 1182 objs.append(f) 1183 elif isdir(f): 1184 for (_dir, _ds, _fs) in walk(f): 1185 if not (_ds + _fs): 1186 dir_markers.append(_dir) 1187 else: 1188 objs.extend([join(_dir, _f) for _f in _fs]) 1189 else: 1190 output_manager.error("Local file '%s' not found" % f) 1191 1192 # Now that we've collected all the required files and dir markers 1193 # build the tuples for the call to upload 1194 if options['object_name'] is not None and not from_stdin: 1195 objs = [ 1196 SwiftUploadObject( 1197 o, object_name=o.replace( 1198 orig_path, options['object_name'], 1 1199 ) 1200 ) for o in objs 1201 ] 1202 dir_markers = [ 1203 SwiftUploadObject( 1204 None, object_name=d.replace( 1205 orig_path, options['object_name'], 1 1206 ), options={'dir_marker': True} 1207 ) for d in dir_markers 1208 ] 1209 1210 for r in swift.upload(container, objs + dir_markers): 1211 if r['success']: 1212 if options['verbose']: 1213 if 'attempts' in r and r['attempts'] > 1: 1214 if 'object' in r: 1215 output_manager.print_msg( 1216 '%s [after %d attempts]' % 1217 (r['object'], 1218 r['attempts']) 1219 ) 1220 else: 1221 if 'object' in r: 1222 output_manager.print_msg(r['object']) 1223 elif 'for_object' in r: 1224 output_manager.print_msg( 1225 '%s segment %s' % (r['for_object'], 1226 r['segment_index']) 1227 ) 1228 else: 1229 error = r['error'] 1230 if 'action' in r and r['action'] == "create_container": 1231 # it is not an error to be unable to create the 1232 # container so print a warning and carry on 1233 if isinstance(error, ClientException): 1234 if (r['headers'] and 1235 'X-Storage-Policy' in r['headers']): 1236 msg = ' with Storage Policy %s' % \ 1237 r['headers']['X-Storage-Policy'].strip() 1238 else: 1239 msg = ' '.join(str(x) for x in ( 1240 error.http_status, error.http_reason) 1241 ) 1242 if error.http_response_content: 1243 if msg: 1244 msg += ': ' 1245 msg += (error.http_response_content 1246 .decode('utf8')[:60]) 1247 msg = ': %s' % msg 1248 else: 1249 msg = ': %s' % error 1250 output_manager.warning( 1251 'Warning: failed to create container ' 1252 "'%s'%s", r['container'], msg 1253 ) 1254 else: 1255 output_manager.error("%s" % error) 1256 too_large = (isinstance(error, ClientException) and 1257 error.http_status == 413) 1258 if too_large and options['verbose'] > 0: 1259 output_manager.error( 1260 "Consider using the --segment-size option " 1261 "to chunk the object") 1262 1263 except SwiftError as e: 1264 output_manager.error(e.value) 1265 1266 1267st_capabilities_options = '''[--json] [<proxy_url>] 1268''' 1269st_info_options = st_capabilities_options 1270st_capabilities_help = ''' 1271Retrieve capability of the proxy. 1272 1273Optional positional arguments: 1274 <proxy_url> Proxy URL of the cluster to retrieve capabilities. 1275 1276Optional arguments: 1277 --json Print the cluster capabilities in JSON format. 1278'''.strip('\n') 1279st_info_help = st_capabilities_help 1280 1281 1282def st_capabilities(parser, args, output_manager, return_parser=False): 1283 def _print_compo_cap(name, capabilities): 1284 for feature, options in sorted(capabilities.items(), 1285 key=lambda x: x[0]): 1286 output_manager.print_msg("%s: %s" % (name, feature)) 1287 if options: 1288 output_manager.print_msg(" Options:") 1289 for key, value in sorted(options.items(), 1290 key=lambda x: x[0]): 1291 output_manager.print_msg(" %s: %s" % (key, value)) 1292 1293 parser.add_argument('--json', action='store_true', 1294 help='print capability information in json') 1295 1296 # We return the parser to build up the bash_completion 1297 if return_parser: 1298 return parser 1299 1300 (options, args) = parse_args(parser, args) 1301 if args and len(args) > 2: 1302 output_manager.error('Usage: %s capabilities %s\n%s', 1303 BASENAME, 1304 st_capabilities_options, st_capabilities_help) 1305 return 1306 1307 with SwiftService(options=options) as swift: 1308 try: 1309 if len(args) == 2: 1310 url = args[1] 1311 capabilities_result = swift.capabilities(url) 1312 capabilities = capabilities_result['capabilities'] 1313 else: 1314 capabilities_result = swift.capabilities() 1315 capabilities = capabilities_result['capabilities'] 1316 1317 if options['json']: 1318 output_manager.print_msg( 1319 json.dumps(capabilities, sort_keys=True, indent=2)) 1320 else: 1321 capabilities = dict(capabilities) 1322 _print_compo_cap('Core', {'swift': capabilities['swift']}) 1323 del capabilities['swift'] 1324 _print_compo_cap('Additional middleware', capabilities) 1325 except SwiftError as e: 1326 output_manager.error(e.value) 1327 1328 1329st_info = st_capabilities 1330 1331st_auth_help = ''' 1332Display auth related authentication variables in shell friendly format. 1333 1334 Commands to run to export storage url and auth token into 1335 OS_STORAGE_URL and OS_AUTH_TOKEN: 1336 1337 swift auth 1338 1339 Commands to append to a runcom file (e.g. ~/.bashrc, /etc/profile) for 1340 automatic authentication: 1341 1342 swift auth -v -U test:tester -K testing \ 1343 -A http://localhost:8080/auth/v1.0 1344 1345'''.strip('\n') 1346 1347 1348def st_auth(parser, args, thread_manager, return_parser=False): 1349 1350 # We return the parser to build up the bash_completion 1351 if return_parser: 1352 return parser 1353 1354 (options, args) = parse_args(parser, args) 1355 if options['verbose'] > 1: 1356 if options['auth_version'] in ('1', '1.0'): 1357 print('export ST_AUTH=%s' % sh_quote(options['auth'])) 1358 print('export ST_USER=%s' % sh_quote(options['user'])) 1359 print('export ST_KEY=%s' % sh_quote(options['key'])) 1360 else: 1361 print('export OS_IDENTITY_API_VERSION=%s' % sh_quote( 1362 options['auth_version'])) 1363 print('export OS_AUTH_VERSION=%s' % sh_quote( 1364 options['auth_version'])) 1365 print('export OS_AUTH_URL=%s' % sh_quote(options['auth'])) 1366 for k, v in sorted(options.items()): 1367 if v and k.startswith('os_') and \ 1368 k not in ('os_auth_url', 'os_options'): 1369 print('export %s=%s' % (k.upper(), sh_quote(v))) 1370 else: 1371 conn = get_conn(options) 1372 url, token = conn.get_auth() 1373 print('export OS_STORAGE_URL=%s' % sh_quote(url)) 1374 print('export OS_AUTH_TOKEN=%s' % sh_quote(token)) 1375 1376 1377st_tempurl_options = '''[--absolute] [--prefix-based] [--iso8601] 1378 <method> <time> <path> <key>''' 1379 1380 1381st_tempurl_help = ''' 1382Generates a temporary URL for a Swift object. 1383 1384Positional arguments: 1385 <method> An HTTP method to allow for this temporary URL. 1386 Usually 'GET' or 'PUT'. 1387 <time> The amount of time the temporary URL will be 1388 valid. The time can be specified in two ways: 1389 an integer representing the time in seconds or an 1390 ISO 8601 timestamp in a specific format. 1391 If --absolute is passed and time 1392 is an integer, the seconds are intepreted as the Unix 1393 timestamp when the temporary URL will expire. The ISO 1394 8601 timestamp can be specified in one of following 1395 formats: 1396 1397 i) Complete date: YYYY-MM-DD (eg 1997-07-16) 1398 1399 ii) Complete date plus hours, minutes and seconds: 1400 1401 YYYY-MM-DDThh:mm:ss 1402 1403 (eg 1997-07-16T19:20:30) 1404 1405 iii) Complete date plus hours, minutes and seconds with 1406 UTC designator: 1407 1408 YYYY-MM-DDThh:mm:ssZ 1409 1410 (eg 1997-07-16T19:20:30Z) 1411 1412 Please be aware that if you don't provide the UTC 1413 designator (i.e., Z) the timestamp is generated using 1414 your local timezone. If only a date is specified, 1415 the time part used will equal to 00:00:00. 1416 <path> The full path or storage URL to the Swift object. 1417 Example: /v1/AUTH_account/c/o 1418 or: http://saio:8080/v1/AUTH_account/c/o 1419 <key> The secret temporary URL key set on the Swift cluster. 1420 To set a key, run \'swift post -m 1421 "Temp-URL-Key:b3968d0207b54ece87cccc06515a89d4"\' 1422 1423Optional arguments: 1424 --absolute Interpret the <time> positional argument as a Unix 1425 timestamp rather than a number of seconds in the 1426 future. If an ISO 8601 timestamp is passed for <time>, 1427 this argument is ignored. 1428 --prefix-based If present, a prefix-based temporary URL will be 1429 generated. 1430 --iso8601 If present, the generated temporary URL will contain an 1431 ISO 8601 UTC timestamp instead of a Unix timestamp. 1432 --ip-range If present, the temporary URL will be restricted to the 1433 given ip or ip range. 1434'''.strip('\n') 1435 1436 1437def st_tempurl(parser, args, thread_manager, return_parser=False): 1438 parser.add_argument( 1439 '--absolute', action='store_true', 1440 dest='absolute_expiry', default=False, 1441 help=("If present, and time argument is an integer, " 1442 "time argument will be interpreted as a Unix " 1443 "timestamp representing when the temporary URL should expire, " 1444 "rather than an offset from the current time."), 1445 ) 1446 parser.add_argument( 1447 '--prefix-based', action='store_true', 1448 default=False, 1449 help=("If present, a prefix-based temporary URL will be generated."), 1450 ) 1451 parser.add_argument( 1452 '--iso8601', action='store_true', 1453 default=False, 1454 help=("If present, the temporary URL will contain an ISO 8601 UTC " 1455 "timestamp instead of a Unix timestamp."), 1456 ) 1457 parser.add_argument( 1458 '--ip-range', action='store', 1459 default=None, 1460 help=("If present, the temporary URL will be restricted to the " 1461 "given ip or ip range."), 1462 ) 1463 1464 # We return the parser to build up the bash_completion 1465 if return_parser: 1466 return parser 1467 1468 (options, args) = parse_args(parser, args) 1469 args = args[1:] 1470 if len(args) < 4: 1471 thread_manager.error('Usage: %s tempurl %s\n%s', BASENAME, 1472 st_tempurl_options, st_tempurl_help) 1473 return 1474 method, timestamp, path, key = args[:4] 1475 1476 parsed = urlparse(path) 1477 1478 if method.upper() not in ['GET', 'PUT', 'HEAD', 'POST', 'DELETE']: 1479 thread_manager.print_msg('WARNING: Non default HTTP method %s for ' 1480 'tempurl specified, possibly an error' % 1481 method.upper()) 1482 try: 1483 path = generate_temp_url(parsed.path, timestamp, key, method, 1484 absolute=options['absolute_expiry'], 1485 iso8601=options['iso8601'], 1486 prefix=options['prefix_based'], 1487 ip_range=options['ip_range']) 1488 except ValueError as err: 1489 thread_manager.error(err) 1490 return 1491 1492 if parsed.scheme and parsed.netloc: 1493 url = "%s://%s%s" % (parsed.scheme, parsed.netloc, path) 1494 else: 1495 url = path 1496 thread_manager.print_msg(url) 1497 1498 1499st_bash_completion_help = '''Retrieve command specific flags used by bash_completion. 1500 1501Optional positional arguments: 1502 <command> Swift client command to filter the flags by. 1503'''.strip('\n') 1504 1505 1506st_bash_completion_options = '''[command] 1507''' 1508 1509 1510def st_bash_completion(parser, args, thread_manager, return_parser=False): 1511 if return_parser: 1512 return parser 1513 1514 global commands 1515 com = args[1] if len(args) > 1 else None 1516 1517 if com: 1518 if com in commands: 1519 fn_commands = ["st_%s" % com] 1520 else: 1521 print("") 1522 return 1523 else: 1524 fn_commands = [fn for fn in globals().keys() 1525 if fn.startswith('st_') and 1526 not fn.endswith('_options') and 1527 not fn.endswith('_help')] 1528 1529 subparsers = parser.add_subparsers() 1530 subcommands = {} 1531 if not com: 1532 subcommands['base'] = parser 1533 for command in fn_commands: 1534 cmd = command[3:] 1535 if com: 1536 subparser = subparsers.add_parser( 1537 cmd, help=globals()['%s_help' % command]) 1538 add_default_args(subparser) 1539 subparser = globals()[command]( 1540 subparser, args, thread_manager, True) 1541 subcommands[cmd] = subparser 1542 else: 1543 subcommands[cmd] = None 1544 1545 cmds = set() 1546 opts = set() 1547 for sc_str, sc in list(subcommands.items()): 1548 cmds.add(sc_str) 1549 if sc: 1550 for option in sc._optionals._option_string_actions: 1551 opts.add(option) 1552 1553 for cmd_to_remove in (com, 'bash_completion', 'base'): 1554 if cmd_to_remove in cmds: 1555 cmds.remove(cmd_to_remove) 1556 print(' '.join(cmds | opts)) 1557 1558 1559class HelpFormatter(argparse.HelpFormatter): 1560 def _format_action_invocation(self, action): 1561 if not action.option_strings: 1562 default = self._get_default_metavar_for_positional(action) 1563 metavar, = self._metavar_formatter(action, default)(1) 1564 return metavar 1565 1566 else: 1567 parts = [] 1568 1569 # if the Optional doesn't take a value, format is: 1570 # -s, --long 1571 if action.nargs == 0: 1572 parts.extend(action.option_strings) 1573 1574 # if the Optional takes a value, format is: 1575 # -s=ARGS, --long=ARGS 1576 else: 1577 default = self._get_default_metavar_for_optional(action) 1578 args_string = self._format_args(action, default) 1579 for option_string in action.option_strings: 1580 parts.append('%s=%s' % (option_string, args_string)) 1581 1582 return ', '.join(parts) 1583 1584 # Back-port py3 methods 1585 def _get_default_metavar_for_optional(self, action): 1586 return action.dest.upper() 1587 1588 def _get_default_metavar_for_positional(self, action): 1589 return action.dest 1590 1591 1592def prompt_for_password(): 1593 """ 1594 Prompt the user for a password. 1595 1596 :raise SystemExit: if a password cannot be entered without it being echoed 1597 to the terminal. 1598 :return: the entered password. 1599 """ 1600 with warnings.catch_warnings(): 1601 warnings.filterwarnings('error', category=getpass.GetPassWarning, 1602 append=True) 1603 try: 1604 # temporarily set signal handling back to default to avoid user 1605 # Ctrl-c leaving terminal in weird state 1606 signal.signal(signal.SIGINT, signal.SIG_DFL) 1607 return getpass.getpass() 1608 except EOFError: 1609 return None 1610 except getpass.GetPassWarning: 1611 exit('Input stream incompatible with --prompt option') 1612 finally: 1613 signal.signal(signal.SIGINT, immediate_exit) 1614 1615 1616def parse_args(parser, args, enforce_requires=True): 1617 options, args = parser.parse_known_args(args or ['-h']) 1618 options = vars(options) 1619 if enforce_requires and (options.get('debug') or options.get('info')): 1620 logging.getLogger("swiftclient") 1621 if options.get('debug'): 1622 logging.basicConfig(level=logging.DEBUG) 1623 logging.getLogger('iso8601').setLevel(logging.WARNING) 1624 client_logger_settings['redact_sensitive_headers'] = False 1625 elif options.get('info'): 1626 logging.basicConfig(level=logging.INFO) 1627 1628 if args and options.get('help'): 1629 _help = globals().get('st_%s_help' % args[0]) 1630 _options = globals().get('st_%s_options' % args[0], "\n") 1631 if _help: 1632 print("Usage: %s %s %s\n%s" % (BASENAME, args[0], _options, _help)) 1633 else: 1634 print("no such command: %s" % args[0]) 1635 exit() 1636 1637 # Short circuit for tempurl, which doesn't need auth 1638 if args and args[0] == 'tempurl': 1639 return options, args 1640 1641 # do this before process_options sets default auth version 1642 if enforce_requires and options['prompt']: 1643 options['key'] = options['os_password'] = prompt_for_password() 1644 1645 # Massage auth version; build out os_options subdict 1646 process_options(options) 1647 1648 if len(args) > 1 and args[0] == "capabilities": 1649 return options, args 1650 1651 if (options['os_options']['object_storage_url'] and 1652 options['os_options']['auth_token']): 1653 return options, args 1654 1655 if enforce_requires: 1656 if options['os_auth_type'] and options['os_auth_type'] not in ( 1657 'password', 'v1password', 'v2password', 'v3password', 1658 'v3applicationcredential'): 1659 exit('Only "v3applicationcredential" is supported for ' 1660 '--os-auth-type') 1661 elif options['os_auth_type'] == 'v3applicationcredential': 1662 if not (options['os_application_credential_id'] and 1663 options['os_application_credential_secret']): 1664 exit('Auth version 3 (application credential) requires ' 1665 'OS_APPLICATION_CREDENTIAL_ID and ' 1666 'OS_APPLICATION_CREDENTIAL_SECRET to be set or ' 1667 'overridden with --os-application-credential-id and ' 1668 '--os-application-credential-secret respectively.') 1669 elif options['auth_version'] == '3': 1670 if not options['auth']: 1671 exit('Auth version 3 requires OS_AUTH_URL to be set or ' 1672 'overridden with --os-auth-url') 1673 if not (options['user'] or options['os_user_id']): 1674 exit('Auth version 3 requires either OS_USERNAME or ' 1675 'OS_USER_ID to be set or overridden with ' 1676 '--os-username or --os-user-id respectively.') 1677 if not options['key']: 1678 exit('Auth version 3 requires OS_PASSWORD to be set or ' 1679 'overridden with --os-password') 1680 elif not (options['auth'] and options['user'] and options['key']): 1681 exit(''' 1682Auth version 1.0 requires ST_AUTH, ST_USER, and ST_KEY environment variables 1683to be set or overridden with -A, -U, or -K. 1684 1685Auth version 2.0 requires OS_AUTH_URL, OS_USERNAME, OS_PASSWORD, and 1686OS_TENANT_NAME OS_TENANT_ID to be set or overridden with --os-auth-url, 1687--os-username, --os-password, --os-tenant-name or os-tenant-id. Note: 1688adding "-V 2" is necessary for this.'''.strip('\n')) 1689 return options, args 1690 1691 1692def add_default_args(parser): 1693 default_auth_version = '1.0' 1694 for k in ('ST_AUTH_VERSION', 'OS_AUTH_VERSION', 'OS_IDENTITY_API_VERSION'): 1695 try: 1696 default_auth_version = environ[k] 1697 break 1698 except KeyError: 1699 pass 1700 1701 parser.add_argument('--os-help', action='store_true', dest='os_help', 1702 help='Show OpenStack authentication options.') 1703 parser.add_argument('--os_help', action='store_true', 1704 help=argparse.SUPPRESS) 1705 parser.add_argument('-s', '--snet', action='store_true', dest='snet', 1706 default=False, help='Use SERVICENET internal network.') 1707 parser.add_argument('-v', '--verbose', action='count', dest='verbose', 1708 default=1, help='Print more info.') 1709 parser.add_argument('--debug', action='store_true', dest='debug', 1710 default=False, help='Show the curl commands and ' 1711 'results of all http queries regardless of result ' 1712 'status.') 1713 parser.add_argument('--info', action='store_true', dest='info', 1714 default=False, help='Show the curl commands and ' 1715 'results of all http queries which return an error.') 1716 parser.add_argument('-q', '--quiet', action='store_const', dest='verbose', 1717 const=0, default=1, help='Suppress status output.') 1718 parser.add_argument('-A', '--auth', dest='auth', 1719 default=environ.get('ST_AUTH'), 1720 help='URL for obtaining an auth token.') 1721 parser.add_argument('-V', '--auth-version', '--os-identity-api-version', 1722 dest='auth_version', 1723 default=default_auth_version, 1724 type=str, 1725 help='Specify a version for authentication. ' 1726 'Defaults to env[ST_AUTH_VERSION], ' 1727 'env[OS_AUTH_VERSION], ' 1728 'env[OS_IDENTITY_API_VERSION] or 1.0.') 1729 parser.add_argument('-U', '--user', dest='user', 1730 default=environ.get('ST_USER'), 1731 help='User name for obtaining an auth token.') 1732 parser.add_argument('-K', '--key', dest='key', 1733 default=environ.get('ST_KEY'), 1734 help='Key for obtaining an auth token.') 1735 parser.add_argument('-R', '--retries', type=int, default=5, dest='retries', 1736 help='The number of times to retry a failed ' 1737 'connection.') 1738 default_val = config_true_value(environ.get('SWIFTCLIENT_INSECURE')) 1739 parser.add_argument('--insecure', 1740 action="store_true", dest="insecure", 1741 default=default_val, 1742 help='Allow swiftclient to access servers without ' 1743 'having to verify the SSL certificate. ' 1744 'Defaults to env[SWIFTCLIENT_INSECURE] ' 1745 '(set to \'true\' to enable).') 1746 parser.add_argument('--no-ssl-compression', 1747 action='store_false', dest='ssl_compression', 1748 default=True, 1749 help='This option is deprecated and not used anymore. ' 1750 'SSL compression should be disabled by default ' 1751 'by the system SSL library.') 1752 parser.add_argument('--force-auth-retry', 1753 action='store_true', dest='force_auth_retry', 1754 default=False, 1755 help='Force a re-auth attempt on ' 1756 'any error other than 401 unauthorized') 1757 parser.add_argument('--prompt', 1758 action='store_true', dest='prompt', 1759 default=False, 1760 help='Prompt user to enter a password which overrides ' 1761 'any password supplied via --key, --os-password ' 1762 'or environment variables.') 1763 1764 os_grp = parser.add_argument_group("OpenStack authentication options") 1765 os_grp.add_argument('--os-username', 1766 metavar='<auth-user-name>', 1767 default=environ.get('OS_USERNAME'), 1768 help='OpenStack username. Defaults to ' 1769 'env[OS_USERNAME].') 1770 os_grp.add_argument('--os_username', 1771 help=argparse.SUPPRESS) 1772 os_grp.add_argument('--os-user-id', 1773 metavar='<auth-user-id>', 1774 default=environ.get('OS_USER_ID'), 1775 help='OpenStack user ID. ' 1776 'Defaults to env[OS_USER_ID].') 1777 os_grp.add_argument('--os_user_id', 1778 help=argparse.SUPPRESS) 1779 os_grp.add_argument('--os-user-domain-id', 1780 metavar='<auth-user-domain-id>', 1781 default=environ.get('OS_USER_DOMAIN_ID'), 1782 help='OpenStack user domain ID. ' 1783 'Defaults to env[OS_USER_DOMAIN_ID].') 1784 os_grp.add_argument('--os_user_domain_id', 1785 help=argparse.SUPPRESS) 1786 os_grp.add_argument('--os-user-domain-name', 1787 metavar='<auth-user-domain-name>', 1788 default=environ.get('OS_USER_DOMAIN_NAME'), 1789 help='OpenStack user domain name. ' 1790 'Defaults to env[OS_USER_DOMAIN_NAME].') 1791 os_grp.add_argument('--os_user_domain_name', 1792 help=argparse.SUPPRESS) 1793 os_grp.add_argument('--os-password', 1794 metavar='<auth-password>', 1795 default=environ.get('OS_PASSWORD'), 1796 help='OpenStack password. Defaults to ' 1797 'env[OS_PASSWORD].') 1798 os_grp.add_argument('--os_password', 1799 help=argparse.SUPPRESS) 1800 os_grp.add_argument('--os-tenant-id', 1801 metavar='<auth-tenant-id>', 1802 default=environ.get('OS_TENANT_ID'), 1803 help='OpenStack tenant ID. ' 1804 'Defaults to env[OS_TENANT_ID].') 1805 os_grp.add_argument('--os_tenant_id', 1806 help=argparse.SUPPRESS) 1807 os_grp.add_argument('--os-tenant-name', 1808 metavar='<auth-tenant-name>', 1809 default=environ.get('OS_TENANT_NAME'), 1810 help='OpenStack tenant name. ' 1811 'Defaults to env[OS_TENANT_NAME].') 1812 os_grp.add_argument('--os_tenant_name', 1813 help=argparse.SUPPRESS) 1814 os_grp.add_argument('--os-project-id', 1815 metavar='<auth-project-id>', 1816 default=environ.get('OS_PROJECT_ID'), 1817 help='OpenStack project ID. ' 1818 'Defaults to env[OS_PROJECT_ID].') 1819 os_grp.add_argument('--os_project_id', 1820 help=argparse.SUPPRESS) 1821 os_grp.add_argument('--os-project-name', 1822 metavar='<auth-project-name>', 1823 default=environ.get('OS_PROJECT_NAME'), 1824 help='OpenStack project name. ' 1825 'Defaults to env[OS_PROJECT_NAME].') 1826 os_grp.add_argument('--os_project_name', 1827 help=argparse.SUPPRESS) 1828 os_grp.add_argument('--os-project-domain-id', 1829 metavar='<auth-project-domain-id>', 1830 default=environ.get('OS_PROJECT_DOMAIN_ID'), 1831 help='OpenStack project domain ID. ' 1832 'Defaults to env[OS_PROJECT_DOMAIN_ID].') 1833 os_grp.add_argument('--os_project_domain_id', 1834 help=argparse.SUPPRESS) 1835 os_grp.add_argument('--os-project-domain-name', 1836 metavar='<auth-project-domain-name>', 1837 default=environ.get('OS_PROJECT_DOMAIN_NAME'), 1838 help='OpenStack project domain name. ' 1839 'Defaults to env[OS_PROJECT_DOMAIN_NAME].') 1840 os_grp.add_argument('--os_project_domain_name', 1841 help=argparse.SUPPRESS) 1842 os_grp.add_argument('--os-auth-url', 1843 metavar='<auth-url>', 1844 default=environ.get('OS_AUTH_URL'), 1845 help='OpenStack auth URL. Defaults to ' 1846 'env[OS_AUTH_URL].') 1847 os_grp.add_argument('--os_auth_url', 1848 help=argparse.SUPPRESS) 1849 os_grp.add_argument('--os-auth-type', 1850 metavar='<auth-type>', 1851 default=environ.get('OS_AUTH_TYPE'), 1852 help='OpenStack auth type for v3. Defaults to ' 1853 'env[OS_AUTH_TYPE].') 1854 os_grp.add_argument('--os_auth_type', 1855 help=argparse.SUPPRESS) 1856 os_grp.add_argument('--os-application-credential-id', 1857 metavar='<auth-application-credential-id>', 1858 default=environ.get('OS_APPLICATION_CREDENTIAL_ID'), 1859 help='OpenStack appplication credential id. ' 1860 'Defaults to env[OS_APPLICATION_CREDENTIAL_ID].') 1861 os_grp.add_argument('--os_application_credential_id', 1862 help=argparse.SUPPRESS) 1863 os_grp.add_argument('--os-application-credential-secret', 1864 metavar='<auth-application-credential-secret>', 1865 default=environ.get( 1866 'OS_APPLICATION_CREDENTIAL_SECRET'), 1867 help='OpenStack appplication credential secret. ' 1868 'Defaults to ' 1869 'env[OS_APPLICATION_CREDENTIAL_SECRET].') 1870 os_grp.add_argument('--os_application_credential_secret', 1871 help=argparse.SUPPRESS) 1872 os_grp.add_argument('--os-auth-token', 1873 metavar='<auth-token>', 1874 default=environ.get('OS_AUTH_TOKEN'), 1875 help='OpenStack token. Defaults to ' 1876 'env[OS_AUTH_TOKEN]. Used with --os-storage-url ' 1877 'to bypass the usual username/password ' 1878 'authentication.') 1879 os_grp.add_argument('--os_auth_token', 1880 help=argparse.SUPPRESS) 1881 os_grp.add_argument('--os-storage-url', 1882 metavar='<storage-url>', 1883 default=environ.get('OS_STORAGE_URL'), 1884 help='OpenStack storage URL. ' 1885 'Defaults to env[OS_STORAGE_URL]. ' 1886 'Overrides the storage url returned during auth. ' 1887 'Will bypass authentication when used with ' 1888 '--os-auth-token.') 1889 os_grp.add_argument('--os_storage_url', 1890 help=argparse.SUPPRESS) 1891 os_grp.add_argument('--os-region-name', 1892 metavar='<region-name>', 1893 default=environ.get('OS_REGION_NAME'), 1894 help='OpenStack region name. ' 1895 'Defaults to env[OS_REGION_NAME].') 1896 os_grp.add_argument('--os_region_name', 1897 help=argparse.SUPPRESS) 1898 os_grp.add_argument('--os-service-type', 1899 metavar='<service-type>', 1900 default=environ.get('OS_SERVICE_TYPE'), 1901 help='OpenStack Service type. ' 1902 'Defaults to env[OS_SERVICE_TYPE].') 1903 os_grp.add_argument('--os_service_type', 1904 help=argparse.SUPPRESS) 1905 os_grp.add_argument('--os-endpoint-type', 1906 metavar='<endpoint-type>', 1907 default=environ.get('OS_ENDPOINT_TYPE'), 1908 help='OpenStack Endpoint type. ' 1909 'Defaults to env[OS_ENDPOINT_TYPE].') 1910 os_grp.add_argument('--os_endpoint_type', 1911 help=argparse.SUPPRESS) 1912 os_grp.add_argument('--os-cacert', 1913 metavar='<ca-certificate>', 1914 default=environ.get('OS_CACERT'), 1915 help='Specify a CA bundle file to use in verifying a ' 1916 'TLS (https) server certificate. ' 1917 'Defaults to env[OS_CACERT].') 1918 os_grp.add_argument('--os-cert', 1919 metavar='<client-certificate-file>', 1920 default=environ.get('OS_CERT'), 1921 help='Specify a client certificate file (for client ' 1922 'auth). Defaults to env[OS_CERT].') 1923 os_grp.add_argument('--os-key', 1924 metavar='<client-certificate-key-file>', 1925 default=environ.get('OS_KEY'), 1926 help='Specify a client certificate key file (for ' 1927 'client auth). Defaults to env[OS_KEY].') 1928 1929 1930def main(arguments=None): 1931 argv = sys_argv if arguments is None else arguments 1932 1933 argv = [a if isinstance(a, text_type) else a.decode('utf-8') for a in argv] 1934 1935 parser = argparse.ArgumentParser( 1936 add_help=False, formatter_class=HelpFormatter, usage=''' 1937%(prog)s [--version] [--help] [--os-help] [--snet] [--verbose] 1938 [--debug] [--info] [--quiet] [--auth <auth_url>] 1939 [--auth-version <auth_version> | 1940 --os-identity-api-version <auth_version> ] 1941 [--user <username>] 1942 [--key <api_key>] [--retries <num_retries>] 1943 [--os-username <auth-user-name>] 1944 [--os-password <auth-password>] 1945 [--os-user-id <auth-user-id>] 1946 [--os-user-domain-id <auth-user-domain-id>] 1947 [--os-user-domain-name <auth-user-domain-name>] 1948 [--os-tenant-id <auth-tenant-id>] 1949 [--os-tenant-name <auth-tenant-name>] 1950 [--os-project-id <auth-project-id>] 1951 [--os-project-name <auth-project-name>] 1952 [--os-project-domain-id <auth-project-domain-id>] 1953 [--os-project-domain-name <auth-project-domain-name>] 1954 [--os-auth-url <auth-url>] 1955 [--os-auth-token <auth-token>] 1956 [--os-auth-type <os-auth-type>] 1957 [--os-application-credential-id 1958 <auth-application-credential-id>] 1959 [--os-application-credential-secret 1960 <auth-application-credential-secret>] 1961 [--os-storage-url <storage-url>] 1962 [--os-region-name <region-name>] 1963 [--os-service-type <service-type>] 1964 [--os-endpoint-type <endpoint-type>] 1965 [--os-cacert <ca-certificate>] 1966 [--insecure] 1967 [--os-cert <client-certificate-file>] 1968 [--os-key <client-certificate-key-file>] 1969 [--no-ssl-compression] 1970 [--force-auth-retry] 1971 <subcommand> [--help] [<subcommand options>] 1972 1973Command-line interface to the OpenStack Swift API. 1974 1975Positional arguments: 1976 <subcommand> 1977 delete Delete a container or objects within a container. 1978 download Download objects from containers. 1979 list Lists the containers for the account or the objects 1980 for a container. 1981 post Updates meta information for the account, container, 1982 or object; creates containers if not present. 1983 copy Copies object, optionally adds meta 1984 stat Displays information for the account, container, 1985 or object. 1986 upload Uploads files or directories to the given container. 1987 capabilities List cluster capabilities. 1988 tempurl Create a temporary URL. 1989 auth Display auth related environment variables. 1990 bash_completion Outputs option and flag cli data ready for 1991 bash_completion. 1992 1993Examples: 1994 %(prog)s download --help 1995 1996 %(prog)s -A https://api.example.com/v1.0 \\ 1997 -U user -K api_key stat -v 1998 1999 %(prog)s --os-auth-url https://api.example.com/v2.0 \\ 2000 --os-tenant-name tenant \\ 2001 --os-username user --os-password password list 2002 2003 %(prog)s --os-auth-url https://api.example.com/v3 --auth-version 3\\ 2004 --os-project-name project1 --os-project-domain-name domain1 \\ 2005 --os-username user --os-user-domain-name domain1 \\ 2006 --os-password password list 2007 2008 %(prog)s --os-auth-url https://api.example.com/v3 --auth-version 3\\ 2009 --os-project-id 0123456789abcdef0123456789abcdef \\ 2010 --os-user-id abcdef0123456789abcdef0123456789 \\ 2011 --os-password password list 2012 2013 %(prog)s --os-auth-url https://api.example.com/v3 --auth-version 3\\ 2014 --os-application-credential-id d78683c92f0e4f9b9b02a2e208039412 \\ 2015 --os-application-credential-secret APPLICATION_CREDENTIAL_SECRET \\ 2016 --os-auth-type v3applicationcredential list 2017 2018 %(prog)s --os-auth-token 6ee5eb33efad4e45ab46806eac010566 \\ 2019 --os-storage-url https://10.1.5.2:8080/v1/AUTH_ced809b6a4baea7aeab61a \\ 2020 list 2021 2022 %(prog)s list --lh 2023'''.strip('\n')) 2024 2025 version = client_version 2026 parser.add_argument('--version', action='version', 2027 version='python-swiftclient %s' % version) 2028 parser.add_argument('-h', '--help', action='store_true') 2029 2030 add_default_args(parser) 2031 2032 options, args = parse_args(parser, argv[1:], enforce_requires=False) 2033 2034 if options['help'] or options['os_help']: 2035 if options['help']: 2036 parser._action_groups.pop() 2037 parser.print_help() 2038 exit() 2039 2040 if not args or args[0] not in commands: 2041 parser.print_usage() 2042 if args: 2043 exit('no such command: %s' % args[0]) 2044 exit() 2045 2046 signal.signal(signal.SIGINT, immediate_exit) 2047 2048 with OutputManager() as output: 2049 parser.usage = globals()['st_%s_help' % args[0]] 2050 if options['insecure']: 2051 import requests 2052 try: 2053 from requests.packages.urllib3.exceptions import \ 2054 InsecureRequestWarning 2055 except ImportError: 2056 pass 2057 else: 2058 requests.packages.urllib3.disable_warnings( 2059 InsecureRequestWarning) 2060 try: 2061 globals()['st_%s' % args[0]](parser, argv[1:], output) 2062 except ClientException as err: 2063 trans_id = err.transaction_id 2064 err.transaction_id = None # clear it so we aren't overly noisy 2065 output.error(str(err)) 2066 if trans_id: 2067 output.error("Failed Transaction ID: %s", 2068 parse_header_string(trans_id)) 2069 except (RequestException, socket.error) as err: 2070 output.error(str(err)) 2071 2072 if output.get_error_count() > 0: 2073 exit(1) 2074 2075 2076if __name__ == '__main__': 2077 main() 2078