2012-05-08 11:17:04 +01:00
|
|
|
#!/usr/bin/python -u
|
|
|
|
# Copyright (c) 2010-2012 OpenStack, LLC.
|
|
|
|
#
|
|
|
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
# you may not use this file except in compliance with the License.
|
|
|
|
# You may obtain a copy of the License at
|
|
|
|
#
|
|
|
|
# http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
#
|
|
|
|
# Unless required by applicable law or agreed to in writing, software
|
|
|
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
|
|
|
# implied.
|
|
|
|
# See the License for the specific language governing permissions and
|
|
|
|
# limitations under the License.
|
2012-06-18 09:46:54 -07:00
|
|
|
import signal
|
2012-05-08 11:17:04 +01:00
|
|
|
import socket
|
2012-11-16 15:23:25 +10:00
|
|
|
import logging
|
2012-05-08 11:17:04 +01:00
|
|
|
|
|
|
|
from errno import EEXIST, ENOENT
|
|
|
|
from hashlib import md5
|
2012-08-23 14:09:56 -05:00
|
|
|
from optparse import OptionParser, SUPPRESS_HELP
|
2012-06-18 09:46:54 -07:00
|
|
|
from os import environ, listdir, makedirs, utime, _exit as os_exit
|
2012-05-08 11:17:04 +01:00
|
|
|
from os.path import basename, dirname, getmtime, getsize, isdir, join
|
2012-08-16 21:39:00 -07:00
|
|
|
from random import shuffle
|
2013-06-26 22:47:49 -07:00
|
|
|
from sys import argv, exit, stderr, stdout
|
2013-05-22 18:45:42 +00:00
|
|
|
from time import sleep, time, gmtime, strftime
|
2012-06-28 23:42:50 +00:00
|
|
|
from urllib import quote, unquote
|
2012-05-08 11:17:04 +01:00
|
|
|
|
2013-03-05 15:12:04 -08:00
|
|
|
try:
|
|
|
|
import simplejson as json
|
|
|
|
except ImportError:
|
|
|
|
import json
|
|
|
|
|
2013-06-26 22:47:49 -07:00
|
|
|
from swiftclient import Connection, HTTPException
|
|
|
|
from swiftclient.utils import config_true_value
|
|
|
|
from swiftclient.multithreading import MultiThreadingManager
|
|
|
|
from swiftclient.exceptions import ClientException
|
2013-01-22 16:32:50 +01:00
|
|
|
from swiftclient.version import version_info
|
2012-05-08 11:17:04 +01:00
|
|
|
|
|
|
|
|
|
|
|
def get_conn(options):
|
|
|
|
"""
|
|
|
|
Return a connection building it from the options.
|
|
|
|
"""
|
|
|
|
return Connection(options.auth,
|
|
|
|
options.user,
|
|
|
|
options.key,
|
2013-07-08 15:32:28 -07:00
|
|
|
options.retries,
|
2012-07-04 21:46:02 +02:00
|
|
|
auth_version=options.auth_version,
|
|
|
|
os_options=options.os_options,
|
2012-12-05 13:18:27 +09:00
|
|
|
snet=options.snet,
|
2012-12-19 09:52:54 -06:00
|
|
|
cacert=options.os_cacert,
|
2013-01-18 14:17:21 +00:00
|
|
|
insecure=options.insecure,
|
|
|
|
ssl_compression=options.ssl_compression)
|
2012-05-08 11:17:04 +01:00
|
|
|
|
|
|
|
|
|
|
|
def mkdirs(path):
|
|
|
|
try:
|
|
|
|
makedirs(path)
|
2013-04-25 23:19:52 +02:00
|
|
|
except OSError as err:
|
2012-05-08 11:17:04 +01:00
|
|
|
if err.errno != EEXIST:
|
|
|
|
raise
|
|
|
|
|
|
|
|
|
2013-06-26 11:41:29 -07:00
|
|
|
def immediate_exit(signum, frame):
|
|
|
|
stderr.write(" Aborted\n")
|
|
|
|
os_exit(2)
|
|
|
|
|
2013-08-17 22:43:09 +10:00
|
|
|
st_delete_options = '''[-all] [--leave-segments]
|
|
|
|
[--object-threads <threads>]
|
|
|
|
[--container-threads <threads>]
|
|
|
|
<container> <object>
|
|
|
|
'''
|
2013-06-26 11:41:29 -07:00
|
|
|
|
2012-05-08 11:17:04 +01:00
|
|
|
st_delete_help = '''
|
2013-08-17 22:43:09 +10:00
|
|
|
Delete a container or objects within a container
|
|
|
|
|
|
|
|
Positional arguments:
|
|
|
|
<container> Name of container to delete from
|
|
|
|
<object> Name of object to delete. Specify multiple times
|
|
|
|
for multiple objects
|
|
|
|
|
|
|
|
Optional arguments:
|
|
|
|
--all Delete all containers and objects
|
|
|
|
--leave-segments Do not delete segments of manifest objects
|
|
|
|
--object-threads <threads>
|
|
|
|
Number of threads to use for deleting objects
|
|
|
|
--container-threads <threads>
|
|
|
|
Number of threads to use for deleting containers
|
|
|
|
'''.strip("\n")
|
2012-05-08 11:17:04 +01:00
|
|
|
|
|
|
|
|
2013-06-26 22:47:49 -07:00
|
|
|
def st_delete(parser, args, thread_manager):
|
2013-04-28 19:16:38 -07:00
|
|
|
parser.add_option(
|
|
|
|
'-a', '--all', action='store_true', dest='yes_all',
|
2012-05-08 11:17:04 +01:00
|
|
|
default=False, help='Indicates that you really want to delete '
|
|
|
|
'everything in the account')
|
2013-04-28 19:16:38 -07:00
|
|
|
parser.add_option(
|
|
|
|
'', '--leave-segments', action='store_true',
|
|
|
|
dest='leave_segments', default=False,
|
|
|
|
help='Indicates that you want the segments of manifest'
|
|
|
|
'objects left alone')
|
|
|
|
parser.add_option(
|
|
|
|
'', '--object-threads', type=int,
|
|
|
|
default=10, help='Number of threads to use for deleting objects')
|
2012-06-12 17:33:18 -07:00
|
|
|
parser.add_option('', '--container-threads', type=int,
|
|
|
|
default=10, help='Number of threads to use for '
|
|
|
|
'deleting containers')
|
2012-05-08 11:17:04 +01:00
|
|
|
(options, args) = parse_args(parser, args)
|
|
|
|
args = args[1:]
|
|
|
|
if (not args and not options.yes_all) or (args and options.yes_all):
|
2013-08-17 22:43:09 +10:00
|
|
|
thread_manager.error('Usage: %s delete %s\n%s',
|
|
|
|
basename(argv[0]), st_delete_options,
|
|
|
|
st_delete_help)
|
2012-05-08 11:17:04 +01:00
|
|
|
return
|
|
|
|
|
|
|
|
def _delete_segment((container, obj), conn):
|
|
|
|
conn.delete_object(container, obj)
|
|
|
|
if options.verbose:
|
|
|
|
if conn.attempts > 2:
|
2013-06-26 22:47:49 -07:00
|
|
|
thread_manager.print_msg(
|
|
|
|
'%s/%s [after %d attempts]', container,
|
|
|
|
obj, conn.attempts)
|
2012-05-08 11:17:04 +01:00
|
|
|
else:
|
2013-06-26 22:47:49 -07:00
|
|
|
thread_manager.print_msg('%s/%s', container, obj)
|
2012-05-08 11:17:04 +01:00
|
|
|
|
|
|
|
def _delete_object((container, obj), conn):
|
|
|
|
try:
|
|
|
|
old_manifest = None
|
2013-03-05 15:12:04 -08:00
|
|
|
query_string = None
|
2012-05-08 11:17:04 +01:00
|
|
|
if not options.leave_segments:
|
|
|
|
try:
|
2013-03-05 15:12:04 -08:00
|
|
|
headers = conn.head_object(container, obj)
|
|
|
|
old_manifest = headers.get('x-object-manifest')
|
2013-06-26 22:47:49 -07:00
|
|
|
if config_true_value(
|
2013-03-05 15:12:04 -08:00
|
|
|
headers.get('x-static-large-object')):
|
|
|
|
query_string = 'multipart-manifest=delete'
|
2013-04-25 23:19:52 +02:00
|
|
|
except ClientException as err:
|
2012-05-08 11:17:04 +01:00
|
|
|
if err.http_status != 404:
|
|
|
|
raise
|
2013-03-05 15:12:04 -08:00
|
|
|
conn.delete_object(container, obj, query_string=query_string)
|
2012-05-08 11:17:04 +01:00
|
|
|
if old_manifest:
|
2013-06-26 22:47:49 -07:00
|
|
|
segment_manager = thread_manager.queue_manager(
|
|
|
|
_delete_segment, options.object_threads,
|
|
|
|
connection_maker=create_connection)
|
|
|
|
segment_queue = segment_manager.queue
|
2012-05-08 11:17:04 +01:00
|
|
|
scontainer, sprefix = old_manifest.split('/', 1)
|
2012-06-28 23:42:50 +00:00
|
|
|
scontainer = unquote(scontainer)
|
2013-03-05 15:12:04 -08:00
|
|
|
sprefix = unquote(sprefix).rstrip('/') + '/'
|
2012-05-08 11:17:04 +01:00
|
|
|
for delobj in conn.get_container(scontainer,
|
|
|
|
prefix=sprefix)[1]:
|
|
|
|
segment_queue.put((scontainer, delobj['name']))
|
|
|
|
if not segment_queue.empty():
|
2013-06-26 22:47:49 -07:00
|
|
|
with segment_manager:
|
|
|
|
pass
|
2012-05-08 11:17:04 +01:00
|
|
|
if options.verbose:
|
|
|
|
path = options.yes_all and join(container, obj) or obj
|
|
|
|
if path[:1] in ('/', '\\'):
|
|
|
|
path = path[1:]
|
|
|
|
if conn.attempts > 1:
|
2013-06-26 22:47:49 -07:00
|
|
|
thread_manager.print_msg('%s [after %d attempts]', path,
|
|
|
|
conn.attempts)
|
2012-05-08 11:17:04 +01:00
|
|
|
else:
|
2013-06-26 22:47:49 -07:00
|
|
|
thread_manager.print_msg(path)
|
2013-04-25 23:19:52 +02:00
|
|
|
except ClientException as err:
|
2012-05-08 11:17:04 +01:00
|
|
|
if err.http_status != 404:
|
|
|
|
raise
|
2013-06-26 22:47:49 -07:00
|
|
|
thread_manager.error("Object '%s/%s' not found", container, obj)
|
2012-05-08 11:17:04 +01:00
|
|
|
|
2013-06-26 22:47:49 -07:00
|
|
|
def _delete_container(container, conn, object_queue):
|
2012-05-08 11:17:04 +01:00
|
|
|
try:
|
|
|
|
marker = ''
|
|
|
|
while True:
|
|
|
|
objects = [o['name'] for o in
|
|
|
|
conn.get_container(container, marker=marker)[1]]
|
|
|
|
if not objects:
|
|
|
|
break
|
|
|
|
for obj in objects:
|
|
|
|
object_queue.put((container, obj))
|
|
|
|
marker = objects[-1]
|
2013-05-23 16:39:31 -04:00
|
|
|
while not object_queue.empty():
|
|
|
|
sleep(0.05)
|
2012-05-08 11:17:04 +01:00
|
|
|
attempts = 1
|
|
|
|
while True:
|
|
|
|
try:
|
|
|
|
conn.delete_container(container)
|
|
|
|
break
|
2013-04-25 23:19:52 +02:00
|
|
|
except ClientException as err:
|
2012-05-08 11:17:04 +01:00
|
|
|
if err.http_status != 409:
|
|
|
|
raise
|
|
|
|
if attempts > 10:
|
|
|
|
raise
|
|
|
|
attempts += 1
|
|
|
|
sleep(1)
|
2013-04-25 23:19:52 +02:00
|
|
|
except ClientException as err:
|
2012-05-08 11:17:04 +01:00
|
|
|
if err.http_status != 404:
|
|
|
|
raise
|
2013-06-26 22:47:49 -07:00
|
|
|
thread_manager.error('Container %r not found', container)
|
2012-05-08 11:17:04 +01:00
|
|
|
|
|
|
|
create_connection = lambda: get_conn(options)
|
2013-06-26 22:47:49 -07:00
|
|
|
obj_manager = thread_manager.queue_manager(
|
|
|
|
_delete_object, options.object_threads,
|
|
|
|
connection_maker=create_connection)
|
|
|
|
with obj_manager as object_queue:
|
|
|
|
cont_manager = thread_manager.queue_manager(
|
|
|
|
_delete_container, options.container_threads, object_queue,
|
|
|
|
connection_maker=create_connection)
|
|
|
|
with cont_manager as container_queue:
|
|
|
|
if not args:
|
|
|
|
conn = create_connection()
|
|
|
|
try:
|
|
|
|
marker = ''
|
|
|
|
while True:
|
|
|
|
containers = [
|
|
|
|
c['name']
|
|
|
|
for c in conn.get_account(marker=marker)[1]]
|
|
|
|
if not containers:
|
|
|
|
break
|
|
|
|
for container in containers:
|
|
|
|
container_queue.put(container)
|
|
|
|
marker = containers[-1]
|
|
|
|
except ClientException as err:
|
|
|
|
if err.http_status != 404:
|
|
|
|
raise
|
|
|
|
thread_manager.error('Account not found')
|
|
|
|
elif len(args) == 1:
|
|
|
|
if '/' in args[0]:
|
|
|
|
print >> stderr, 'WARNING: / in container name; you '
|
|
|
|
'might have meant %r instead of %r.' % (
|
2013-06-26 11:41:29 -07:00
|
|
|
args[0].replace('/', ' ', 1), args[0])
|
2013-06-26 22:47:49 -07:00
|
|
|
container_queue.put(args[0])
|
|
|
|
else:
|
|
|
|
for obj in args[1:]:
|
|
|
|
object_queue.put((args[0], obj))
|
2012-05-08 11:17:04 +01:00
|
|
|
|
2013-08-17 22:43:09 +10:00
|
|
|
st_download_options = '''[--all] [--marker] [--prefix <prefix>]
|
|
|
|
[--output <out_file>] [--object-threads <threads>]
|
|
|
|
[--container-threads <threads>] [--no-download]
|
|
|
|
<container> <object>
|
|
|
|
'''
|
2012-05-08 11:17:04 +01:00
|
|
|
|
|
|
|
st_download_help = '''
|
2013-08-17 22:43:09 +10:00
|
|
|
Download objects from containers
|
|
|
|
|
|
|
|
Positional arguments:
|
|
|
|
<container> Name of container to download from
|
|
|
|
<object> Name of object to download. Specify multiple times
|
|
|
|
for multiple objects
|
|
|
|
|
|
|
|
Optional arguments:
|
|
|
|
--all Indicates that you really want to download
|
|
|
|
everything in the account
|
|
|
|
--marker Marker to use when starting a container or account
|
|
|
|
download
|
|
|
|
--prefix <prefix> Only download items beginning with <prefix>
|
|
|
|
--output <out_file> For a single file download, stream the output to
|
|
|
|
<out_file>. Specifying "-" as <out_file> will
|
|
|
|
redirect to stdout
|
|
|
|
--object-threads <threads>
|
|
|
|
Number of threads to use for downloading objects
|
|
|
|
--container-threads <threads>
|
|
|
|
Number of threads to use for deleting containers
|
|
|
|
--no-download Perform download(s), but don't actually write anything
|
|
|
|
to disk
|
|
|
|
'''.strip("\n")
|
2012-05-08 11:17:04 +01:00
|
|
|
|
|
|
|
|
2013-06-26 22:47:49 -07:00
|
|
|
def st_download(parser, args, thread_manager):
|
2013-04-28 19:16:38 -07:00
|
|
|
parser.add_option(
|
|
|
|
'-a', '--all', action='store_true', dest='yes_all',
|
2012-05-08 11:17:04 +01:00
|
|
|
default=False, help='Indicates that you really want to download '
|
|
|
|
'everything in the account')
|
2013-04-28 19:16:38 -07:00
|
|
|
parser.add_option(
|
|
|
|
'-m', '--marker', dest='marker',
|
2012-05-08 11:17:04 +01:00
|
|
|
default='', help='Marker to use when starting a container or '
|
|
|
|
'account download')
|
2013-06-26 11:41:29 -07:00
|
|
|
parser.add_option(
|
|
|
|
'-p', '--prefix', dest='prefix',
|
|
|
|
help='Will only download items beginning with the prefix')
|
2013-04-28 19:16:38 -07:00
|
|
|
parser.add_option(
|
|
|
|
'-o', '--output', dest='out_file', help='For a single '
|
2012-05-08 11:17:04 +01:00
|
|
|
'file download, stream the output to an alternate location ')
|
2013-04-28 19:16:38 -07:00
|
|
|
parser.add_option(
|
|
|
|
'', '--object-threads', type=int,
|
|
|
|
default=10, help='Number of threads to use for downloading objects')
|
|
|
|
parser.add_option(
|
|
|
|
'', '--container-threads', type=int, default=10,
|
|
|
|
help='Number of threads to use for listing containers')
|
|
|
|
parser.add_option(
|
|
|
|
'', '--no-download', action='store_true',
|
|
|
|
default=False,
|
|
|
|
help="Perform download(s), but don't actually write anything to disk")
|
2012-05-08 11:17:04 +01:00
|
|
|
(options, args) = parse_args(parser, args)
|
|
|
|
args = args[1:]
|
|
|
|
if options.out_file == '-':
|
|
|
|
options.verbose = 0
|
|
|
|
if options.out_file and len(args) != 2:
|
|
|
|
exit('-o option only allowed for single file downloads')
|
|
|
|
if (not args and not options.yes_all) or (args and options.yes_all):
|
2013-08-17 22:43:09 +10:00
|
|
|
thread_manager.error('Usage: %s download %s\n%s', basename(argv[0]),
|
|
|
|
st_download_options, st_download_help)
|
2012-05-08 11:17:04 +01:00
|
|
|
return
|
|
|
|
|
|
|
|
def _download_object(queue_arg, conn):
|
|
|
|
if len(queue_arg) == 2:
|
|
|
|
container, obj = queue_arg
|
|
|
|
out_file = None
|
|
|
|
elif len(queue_arg) == 3:
|
|
|
|
container, obj, out_file = queue_arg
|
|
|
|
else:
|
|
|
|
raise Exception("Invalid queue_arg length of %s" % len(queue_arg))
|
|
|
|
try:
|
2012-08-16 21:30:54 -07:00
|
|
|
start_time = time()
|
2012-05-08 11:17:04 +01:00
|
|
|
headers, body = \
|
|
|
|
conn.get_object(container, obj, resp_chunk_size=65536)
|
2012-08-16 21:30:54 -07:00
|
|
|
header_receipt = time()
|
2012-05-08 11:17:04 +01:00
|
|
|
content_type = headers.get('content-type')
|
|
|
|
if 'content-length' in headers:
|
|
|
|
content_length = int(headers.get('content-length'))
|
|
|
|
else:
|
|
|
|
content_length = None
|
|
|
|
etag = headers.get('etag')
|
|
|
|
path = options.yes_all and join(container, obj) or obj
|
|
|
|
if path[:1] in ('/', '\\'):
|
|
|
|
path = path[1:]
|
|
|
|
md5sum = None
|
2012-08-17 10:51:25 -07:00
|
|
|
make_dir = not options.no_download and out_file != "-"
|
2012-05-08 11:17:04 +01:00
|
|
|
if content_type.split(';', 1)[0] == 'text/directory':
|
|
|
|
if make_dir and not isdir(path):
|
|
|
|
mkdirs(path)
|
|
|
|
read_length = 0
|
2013-05-13 07:37:15 -07:00
|
|
|
if 'x-object-manifest' not in headers and \
|
|
|
|
'x-static-large-object' not in headers:
|
2012-05-08 11:17:04 +01:00
|
|
|
md5sum = md5()
|
|
|
|
for chunk in body:
|
|
|
|
read_length += len(chunk)
|
|
|
|
if md5sum:
|
|
|
|
md5sum.update(chunk)
|
|
|
|
else:
|
|
|
|
dirpath = dirname(path)
|
|
|
|
if make_dir and dirpath and not isdir(dirpath):
|
|
|
|
mkdirs(dirpath)
|
2012-08-17 10:51:25 -07:00
|
|
|
if not options.no_download:
|
|
|
|
if out_file == "-":
|
|
|
|
fp = stdout
|
|
|
|
elif out_file:
|
|
|
|
fp = open(out_file, 'wb')
|
|
|
|
else:
|
|
|
|
fp = open(path, 'wb')
|
2012-05-08 11:17:04 +01:00
|
|
|
read_length = 0
|
2013-05-13 07:37:15 -07:00
|
|
|
if 'x-object-manifest' not in headers and \
|
|
|
|
'x-static-large-object' not in headers:
|
2012-05-08 11:17:04 +01:00
|
|
|
md5sum = md5()
|
|
|
|
for chunk in body:
|
2012-08-17 10:51:25 -07:00
|
|
|
if not options.no_download:
|
|
|
|
fp.write(chunk)
|
2012-05-08 11:17:04 +01:00
|
|
|
read_length += len(chunk)
|
|
|
|
if md5sum:
|
|
|
|
md5sum.update(chunk)
|
2012-08-17 10:51:25 -07:00
|
|
|
if not options.no_download:
|
|
|
|
fp.close()
|
2012-05-08 11:17:04 +01:00
|
|
|
if md5sum and md5sum.hexdigest() != etag:
|
2013-06-26 22:47:49 -07:00
|
|
|
thread_manager.error('%s: md5sum != etag, %s != %s',
|
|
|
|
path, md5sum.hexdigest(), etag)
|
2012-05-08 11:17:04 +01:00
|
|
|
if content_length is not None and read_length != content_length:
|
2013-06-26 22:47:49 -07:00
|
|
|
thread_manager.error(
|
|
|
|
'%s: read_length != content_length, %d != %d',
|
|
|
|
path, read_length, content_length)
|
2012-08-17 10:51:25 -07:00
|
|
|
if 'x-object-meta-mtime' in headers and not options.out_file \
|
|
|
|
and not options.no_download:
|
|
|
|
|
2012-05-08 11:17:04 +01:00
|
|
|
mtime = float(headers['x-object-meta-mtime'])
|
|
|
|
utime(path, (mtime, mtime))
|
|
|
|
if options.verbose:
|
2012-08-16 21:30:54 -07:00
|
|
|
finish_time = time()
|
2013-08-28 16:24:40 +01:00
|
|
|
time_str = 'headers %.3fs, total %.3fs, %.3f MB/s' % (
|
2012-08-16 21:30:54 -07:00
|
|
|
header_receipt - start_time, finish_time - start_time,
|
|
|
|
float(read_length) / (finish_time - start_time) / 1000000)
|
2012-05-08 11:17:04 +01:00
|
|
|
if conn.attempts > 1:
|
2013-06-26 22:47:49 -07:00
|
|
|
thread_manager.print_msg('%s [%s after %d attempts]', path,
|
|
|
|
time_str, conn.attempts)
|
2012-05-08 11:17:04 +01:00
|
|
|
else:
|
2013-06-26 22:47:49 -07:00
|
|
|
thread_manager.print_msg('%s [%s]', path, time_str)
|
2013-04-25 23:19:52 +02:00
|
|
|
except ClientException as err:
|
2012-05-08 11:17:04 +01:00
|
|
|
if err.http_status != 404:
|
|
|
|
raise
|
2013-06-26 22:47:49 -07:00
|
|
|
thread_manager.error("Object '%s/%s' not found", container, obj)
|
2012-05-08 11:17:04 +01:00
|
|
|
|
2013-06-26 22:47:49 -07:00
|
|
|
def _download_container(queue_arg, conn):
|
|
|
|
if len(queue_arg) == 2:
|
|
|
|
container, object_queue = queue_arg
|
|
|
|
prefix = None
|
|
|
|
elif len(queue_arg) == 3:
|
|
|
|
container, object_queue, prefix = queue_arg
|
|
|
|
else:
|
|
|
|
raise Exception("Invalid queue_arg length of %s" % len(queue_arg))
|
2012-05-08 11:17:04 +01:00
|
|
|
try:
|
|
|
|
marker = options.marker
|
|
|
|
while True:
|
2013-06-26 11:41:29 -07:00
|
|
|
objects = [
|
|
|
|
o['name'] for o in
|
|
|
|
conn.get_container(container, marker=marker,
|
|
|
|
prefix=prefix)[1]]
|
2012-05-08 11:17:04 +01:00
|
|
|
if not objects:
|
|
|
|
break
|
2012-08-16 21:39:00 -07:00
|
|
|
marker = objects[-1]
|
|
|
|
shuffle(objects)
|
2012-05-08 11:17:04 +01:00
|
|
|
for obj in objects:
|
|
|
|
object_queue.put((container, obj))
|
2013-04-25 23:19:52 +02:00
|
|
|
except ClientException as err:
|
2012-05-08 11:17:04 +01:00
|
|
|
if err.http_status != 404:
|
|
|
|
raise
|
2013-06-26 22:47:49 -07:00
|
|
|
thread_manager.error('Container %r not found', container)
|
2012-05-08 11:17:04 +01:00
|
|
|
|
|
|
|
create_connection = lambda: get_conn(options)
|
2013-06-26 22:47:49 -07:00
|
|
|
obj_manager = thread_manager.queue_manager(
|
|
|
|
_download_object, options.object_threads,
|
|
|
|
connection_maker=create_connection)
|
|
|
|
with obj_manager as object_queue:
|
|
|
|
cont_manager = thread_manager.queue_manager(
|
|
|
|
_download_container, options.container_threads,
|
|
|
|
connection_maker=create_connection)
|
|
|
|
with cont_manager as container_queue:
|
|
|
|
if not args:
|
|
|
|
# --all case
|
|
|
|
conn = create_connection()
|
|
|
|
try:
|
|
|
|
marker = options.marker
|
|
|
|
while True:
|
|
|
|
containers = [
|
|
|
|
c['name'] for c in conn.get_account(
|
|
|
|
marker=marker, prefix=options.prefix)[1]]
|
|
|
|
if not containers:
|
|
|
|
break
|
|
|
|
marker = containers[-1]
|
|
|
|
shuffle(containers)
|
|
|
|
for container in containers:
|
|
|
|
container_queue.put((container, object_queue))
|
|
|
|
except ClientException as err:
|
|
|
|
if err.http_status != 404:
|
|
|
|
raise
|
|
|
|
thread_manager.error('Account not found')
|
|
|
|
elif len(args) == 1:
|
|
|
|
if '/' in args[0]:
|
|
|
|
print >> stderr, ('WARNING: / in container name; you '
|
|
|
|
'might have meant %r instead of %r.' % (
|
2013-06-26 11:41:29 -07:00
|
|
|
args[0].replace('/', ' ', 1), args[0]))
|
2013-06-26 22:47:49 -07:00
|
|
|
container_queue.put((args[0], object_queue, options.prefix))
|
2013-06-26 11:41:29 -07:00
|
|
|
else:
|
2013-06-26 22:47:49 -07:00
|
|
|
if len(args) == 2:
|
|
|
|
obj = args[1]
|
|
|
|
object_queue.put((args[0], obj, options.out_file))
|
|
|
|
else:
|
|
|
|
for obj in args[1:]:
|
|
|
|
object_queue.put((args[0], obj))
|
2012-05-08 11:17:04 +01:00
|
|
|
|
|
|
|
|
2013-05-22 18:45:42 +00:00
|
|
|
def prt_bytes(bytes, human_flag):
|
|
|
|
"""
|
|
|
|
convert a number > 1024 to printable format, either in 4 char -h format as
|
2013-06-21 19:48:36 +00:00
|
|
|
with ls -lh or return as 12 char right justified string
|
2013-05-22 18:45:42 +00:00
|
|
|
"""
|
|
|
|
|
|
|
|
if human_flag:
|
|
|
|
suffix = ''
|
|
|
|
mods = 'KMGTPEZY'
|
|
|
|
temp = float(bytes)
|
|
|
|
if temp > 0:
|
|
|
|
while (temp > 1023):
|
|
|
|
temp /= 1024.0
|
|
|
|
suffix = mods[0]
|
|
|
|
mods = mods[1:]
|
|
|
|
if suffix != '':
|
2013-06-21 19:48:36 +00:00
|
|
|
if temp >= 10:
|
2013-05-22 18:45:42 +00:00
|
|
|
bytes = '%3d%s' % (temp, suffix)
|
|
|
|
else:
|
|
|
|
bytes = '%.1f%s' % (temp, suffix)
|
|
|
|
if suffix == '': # must be < 1024
|
|
|
|
bytes = '%4s' % bytes
|
|
|
|
else:
|
|
|
|
bytes = '%12s' % bytes
|
|
|
|
|
|
|
|
return(bytes)
|
|
|
|
|
2013-08-17 22:43:09 +10:00
|
|
|
st_list_options = '''[--long] [--lh] [--totals]
|
|
|
|
[--container-threads <threads>]
|
|
|
|
'''
|
2012-05-08 11:17:04 +01:00
|
|
|
st_list_help = '''
|
2013-08-17 22:43:09 +10:00
|
|
|
Lists the containers for the account or the objects for a container
|
|
|
|
|
|
|
|
Positional arguments:
|
|
|
|
<container> Name of container to list object in
|
|
|
|
|
|
|
|
Optional arguments:
|
|
|
|
--long Long listing format, similar to ls -l
|
|
|
|
--lh Report sizes in human readable format similar to ls -lh
|
|
|
|
--totals Used with -l or --ls, only report totals
|
|
|
|
--prefix Only list items beginning with the prefix
|
|
|
|
--delimiter Roll up items with the given delimiter. For containers
|
|
|
|
only. See OpenStack Swift API documentation for what
|
|
|
|
this means.
|
2012-05-08 11:17:04 +01:00
|
|
|
'''.strip('\n')
|
|
|
|
|
|
|
|
|
2013-06-26 22:47:49 -07:00
|
|
|
def st_list(parser, args, thread_manager):
|
2013-05-22 18:45:42 +00:00
|
|
|
parser.add_option(
|
|
|
|
'-l', '--long', dest='long', help='Long listing '
|
|
|
|
'similar to ls -l command', action='store_true', default=False)
|
|
|
|
parser.add_option(
|
|
|
|
'--lh', dest='human', help='report sizes as human '
|
|
|
|
"similar to ls -lh switch, but -h taken", action='store_true',
|
|
|
|
default=False)
|
2013-06-21 19:48:36 +00:00
|
|
|
parser.add_option(
|
2013-08-17 22:43:09 +10:00
|
|
|
'-t', '--totals', dest='totals', help='used with -l or --ls, '
|
|
|
|
'only report totals',
|
2013-06-21 19:48:36 +00:00
|
|
|
action='store_true', default=False)
|
2013-04-28 19:16:38 -07:00
|
|
|
parser.add_option(
|
|
|
|
'-p', '--prefix', dest='prefix',
|
|
|
|
help='Will only list items beginning with the prefix')
|
|
|
|
parser.add_option(
|
|
|
|
'-d', '--delimiter', dest='delimiter',
|
|
|
|
help='Will roll up items with the given delimiter'
|
2013-06-26 11:41:29 -07:00
|
|
|
' (see OpenStack Swift API documentation for what this means)')
|
2012-05-08 11:17:04 +01:00
|
|
|
(options, args) = parse_args(parser, args)
|
|
|
|
args = args[1:]
|
|
|
|
if options.delimiter and not args:
|
|
|
|
exit('-d option only allowed for container listings')
|
2013-02-24 19:04:39 -08:00
|
|
|
if len(args) > 1 or len(args) == 1 and args[0].find('/') >= 0:
|
2013-08-17 22:43:09 +10:00
|
|
|
thread_manager.error('Usage: %s list %s\n%s', basename(argv[0]),
|
|
|
|
st_list_options, st_list_help)
|
2012-05-08 11:17:04 +01:00
|
|
|
return
|
|
|
|
|
|
|
|
conn = get_conn(options)
|
|
|
|
try:
|
|
|
|
marker = ''
|
2013-06-21 19:48:36 +00:00
|
|
|
total_count = total_bytes = 0
|
2012-05-08 11:17:04 +01:00
|
|
|
while True:
|
|
|
|
if not args:
|
|
|
|
items = \
|
|
|
|
conn.get_account(marker=marker, prefix=options.prefix)[1]
|
|
|
|
else:
|
2013-04-28 19:16:38 -07:00
|
|
|
items = conn.get_container(
|
|
|
|
args[0], marker=marker,
|
2012-05-08 11:17:04 +01:00
|
|
|
prefix=options.prefix, delimiter=options.delimiter)[1]
|
|
|
|
if not items:
|
|
|
|
break
|
|
|
|
for item in items:
|
2013-05-22 18:45:42 +00:00
|
|
|
item_name = item.get('name')
|
|
|
|
|
|
|
|
if not options.long and not options.human:
|
2013-06-26 22:47:49 -07:00
|
|
|
thread_manager.print_msg(
|
|
|
|
item.get('name', item.get('subdir')))
|
2013-05-22 18:45:42 +00:00
|
|
|
else:
|
2013-06-21 19:48:36 +00:00
|
|
|
item_bytes = item.get('bytes')
|
|
|
|
total_bytes += item_bytes
|
2013-05-22 18:45:42 +00:00
|
|
|
if len(args) == 0: # listing containers
|
2013-06-26 22:47:49 -07:00
|
|
|
byte_str = prt_bytes(item_bytes, options.human)
|
2013-05-22 18:45:42 +00:00
|
|
|
count = item.get('count')
|
2013-06-21 19:48:36 +00:00
|
|
|
total_count += count
|
2013-05-22 18:45:42 +00:00
|
|
|
try:
|
|
|
|
meta = conn.head_container(item_name)
|
|
|
|
utc = gmtime(float(meta.get('x-timestamp')))
|
|
|
|
datestamp = strftime('%Y-%m-%d %H:%M:%S', utc)
|
|
|
|
except ClientException:
|
|
|
|
datestamp = '????-??-?? ??:??:??'
|
2013-06-21 19:48:36 +00:00
|
|
|
if not options.totals:
|
2013-06-26 22:47:49 -07:00
|
|
|
thread_manager.print_msg("%5s %s %s %s", count,
|
|
|
|
byte_str, datestamp,
|
|
|
|
item_name)
|
2013-05-22 18:45:42 +00:00
|
|
|
else: # list container contents
|
|
|
|
subdir = item.get('subdir')
|
|
|
|
if subdir is None:
|
2013-06-26 22:47:49 -07:00
|
|
|
byte_str = prt_bytes(item_bytes, options.human)
|
2013-05-22 18:45:42 +00:00
|
|
|
date, xtime = item.get('last_modified').split('T')
|
|
|
|
xtime = xtime.split('.')[0]
|
|
|
|
else:
|
2013-06-26 22:47:49 -07:00
|
|
|
byte_str = prt_bytes(0, options.human)
|
2013-05-22 18:45:42 +00:00
|
|
|
date = xtime = ''
|
|
|
|
item_name = subdir
|
2013-06-21 19:48:36 +00:00
|
|
|
if not options.totals:
|
2013-06-26 22:47:49 -07:00
|
|
|
thread_manager.print_msg("%s %10s %8s %s",
|
|
|
|
byte_str, date, xtime,
|
|
|
|
item_name)
|
2013-06-21 19:48:36 +00:00
|
|
|
|
|
|
|
marker = items[-1].get('name', items[-1].get('subdir'))
|
|
|
|
|
|
|
|
# report totals
|
|
|
|
if options.long or options.human:
|
|
|
|
if len(args) == 0:
|
2013-06-26 22:47:49 -07:00
|
|
|
thread_manager.print_msg(
|
|
|
|
"%5s %s", prt_bytes(total_count, True),
|
|
|
|
prt_bytes(total_bytes, options.human))
|
2013-06-21 19:48:36 +00:00
|
|
|
else:
|
2013-06-26 22:47:49 -07:00
|
|
|
thread_manager.print_msg(prt_bytes(total_bytes, options.human))
|
2013-05-22 18:45:42 +00:00
|
|
|
|
2013-04-25 23:19:52 +02:00
|
|
|
except ClientException as err:
|
2012-05-08 11:17:04 +01:00
|
|
|
if err.http_status != 404:
|
|
|
|
raise
|
|
|
|
if not args:
|
2013-06-26 22:47:49 -07:00
|
|
|
thread_manager.error('Account not found')
|
2012-05-08 11:17:04 +01:00
|
|
|
else:
|
2013-06-26 22:47:49 -07:00
|
|
|
thread_manager.error('Container %r not found', args[0])
|
2012-05-08 11:17:04 +01:00
|
|
|
|
2013-08-17 22:43:09 +10:00
|
|
|
st_stat_options = '''[--lh]
|
|
|
|
<container> <object>
|
|
|
|
'''
|
|
|
|
|
2012-05-08 11:17:04 +01:00
|
|
|
st_stat_help = '''
|
2013-08-17 22:43:09 +10:00
|
|
|
Displays information for the account, container, or object
|
|
|
|
|
|
|
|
Positional arguments:
|
|
|
|
<container> Name of container to stat from
|
|
|
|
<object> Name of object to stat. Specify multiple times
|
|
|
|
for multiple objects
|
|
|
|
|
|
|
|
Optional arguments:
|
|
|
|
--lh Report sizes in human readable format similar to ls -lh
|
|
|
|
'''.strip('\n')
|
2012-05-08 11:17:04 +01:00
|
|
|
|
|
|
|
|
2013-06-26 22:47:49 -07:00
|
|
|
def st_stat(parser, args, thread_manager):
|
2013-06-21 19:48:36 +00:00
|
|
|
parser.add_option(
|
|
|
|
'--lh', dest='human', help="report totals like 'list --lh'",
|
|
|
|
action='store_true', default=False)
|
2012-05-08 11:17:04 +01:00
|
|
|
(options, args) = parse_args(parser, args)
|
|
|
|
args = args[1:]
|
|
|
|
conn = get_conn(options)
|
|
|
|
if not args:
|
|
|
|
try:
|
|
|
|
headers = conn.head_account()
|
|
|
|
if options.verbose > 1:
|
2013-06-26 22:47:49 -07:00
|
|
|
thread_manager.print_msg('''
|
2012-05-08 11:17:04 +01:00
|
|
|
StorageURL: %s
|
|
|
|
Auth Token: %s
|
2013-06-26 22:47:49 -07:00
|
|
|
'''.strip('\n'), conn.url, conn.token)
|
2012-05-08 11:17:04 +01:00
|
|
|
container_count = int(headers.get('x-account-container-count', 0))
|
2013-06-21 19:48:36 +00:00
|
|
|
object_count = prt_bytes(headers.get('x-account-object-count', 0),
|
|
|
|
options.human).lstrip()
|
|
|
|
bytes_used = prt_bytes(headers.get('x-account-bytes-used', 0),
|
|
|
|
options.human).lstrip()
|
2013-06-26 22:47:49 -07:00
|
|
|
thread_manager.print_msg('''
|
2012-05-08 11:17:04 +01:00
|
|
|
Account: %s
|
|
|
|
Containers: %d
|
2013-06-21 19:48:36 +00:00
|
|
|
Objects: %s
|
2013-06-26 22:47:49 -07:00
|
|
|
Bytes: %s'''.strip('\n'), conn.url.rsplit('/', 1)[-1], container_count,
|
|
|
|
object_count, bytes_used)
|
2012-05-08 11:17:04 +01:00
|
|
|
for key, value in headers.items():
|
|
|
|
if key.startswith('x-account-meta-'):
|
2013-06-26 22:47:49 -07:00
|
|
|
thread_manager.print_msg(
|
|
|
|
'%10s: %s',
|
|
|
|
'Meta %s' % key[len('x-account-meta-'):].title(),
|
|
|
|
value)
|
2012-05-08 11:17:04 +01:00
|
|
|
for key, value in headers.items():
|
|
|
|
if not key.startswith('x-account-meta-') and key not in (
|
|
|
|
'content-length', 'date', 'x-account-container-count',
|
|
|
|
'x-account-object-count', 'x-account-bytes-used'):
|
2013-06-26 22:47:49 -07:00
|
|
|
thread_manager.print_msg('%10s: %s', key.title(), value)
|
2013-04-25 23:19:52 +02:00
|
|
|
except ClientException as err:
|
2012-05-08 11:17:04 +01:00
|
|
|
if err.http_status != 404:
|
|
|
|
raise
|
2013-06-26 22:47:49 -07:00
|
|
|
thread_manager.error('Account not found')
|
2012-05-08 11:17:04 +01:00
|
|
|
elif len(args) == 1:
|
|
|
|
if '/' in args[0]:
|
|
|
|
print >> stderr, 'WARNING: / in container name; you might have ' \
|
|
|
|
'meant %r instead of %r.' % \
|
|
|
|
(args[0].replace('/', ' ', 1), args[0])
|
|
|
|
try:
|
|
|
|
headers = conn.head_container(args[0])
|
2013-06-21 19:48:36 +00:00
|
|
|
object_count = prt_bytes(
|
|
|
|
headers.get('x-container-object-count', 0),
|
|
|
|
options.human).lstrip()
|
|
|
|
bytes_used = prt_bytes(headers.get('x-container-bytes-used', 0),
|
|
|
|
options.human).lstrip()
|
2013-06-26 22:47:49 -07:00
|
|
|
thread_manager.print_msg('''
|
2012-05-08 11:17:04 +01:00
|
|
|
Account: %s
|
|
|
|
Container: %s
|
2013-06-21 19:48:36 +00:00
|
|
|
Objects: %s
|
|
|
|
Bytes: %s
|
2012-05-08 11:17:04 +01:00
|
|
|
Read ACL: %s
|
|
|
|
Write ACL: %s
|
|
|
|
Sync To: %s
|
2013-06-26 22:47:49 -07:00
|
|
|
Sync Key: %s'''.strip('\n'), conn.url.rsplit('/', 1)[-1], args[0],
|
|
|
|
object_count, bytes_used,
|
|
|
|
headers.get('x-container-read', ''),
|
|
|
|
headers.get('x-container-write', ''),
|
|
|
|
headers.get('x-container-sync-to', ''),
|
|
|
|
headers.get('x-container-sync-key', ''))
|
2012-05-08 11:17:04 +01:00
|
|
|
for key, value in headers.items():
|
|
|
|
if key.startswith('x-container-meta-'):
|
2013-06-26 22:47:49 -07:00
|
|
|
thread_manager.print_msg(
|
|
|
|
'%9s: %s',
|
|
|
|
'Meta %s' % key[len('x-container-meta-'):].title(),
|
|
|
|
value)
|
2012-05-08 11:17:04 +01:00
|
|
|
for key, value in headers.items():
|
|
|
|
if not key.startswith('x-container-meta-') and key not in (
|
|
|
|
'content-length', 'date', 'x-container-object-count',
|
|
|
|
'x-container-bytes-used', 'x-container-read',
|
|
|
|
'x-container-write', 'x-container-sync-to',
|
|
|
|
'x-container-sync-key'):
|
2013-06-26 22:47:49 -07:00
|
|
|
thread_manager.print_msg('%9s: %s', key.title(), value)
|
2013-04-25 23:19:52 +02:00
|
|
|
except ClientException as err:
|
2012-05-08 11:17:04 +01:00
|
|
|
if err.http_status != 404:
|
|
|
|
raise
|
2013-06-26 22:47:49 -07:00
|
|
|
thread_manager.error('Container %r not found', args[0])
|
2012-05-08 11:17:04 +01:00
|
|
|
elif len(args) == 2:
|
|
|
|
try:
|
|
|
|
headers = conn.head_object(args[0], args[1])
|
2013-06-26 22:47:49 -07:00
|
|
|
thread_manager.print_msg('''
|
2012-05-08 11:17:04 +01:00
|
|
|
Account: %s
|
|
|
|
Container: %s
|
|
|
|
Object: %s
|
2013-06-26 22:47:49 -07:00
|
|
|
Content Type: %s'''.strip('\n'), conn.url.rsplit('/', 1)[-1], args[0],
|
|
|
|
args[1], headers.get('content-type'))
|
2012-05-08 11:17:04 +01:00
|
|
|
if 'content-length' in headers:
|
2013-06-26 22:47:49 -07:00
|
|
|
thread_manager.print_msg('Content Length: %s',
|
|
|
|
prt_bytes(headers['content-length'],
|
|
|
|
options.human).lstrip())
|
2012-05-08 11:17:04 +01:00
|
|
|
if 'last-modified' in headers:
|
2013-06-26 22:47:49 -07:00
|
|
|
thread_manager.print_msg(' Last Modified: %s',
|
|
|
|
headers['last-modified'])
|
2012-05-08 11:17:04 +01:00
|
|
|
if 'etag' in headers:
|
2013-06-26 22:47:49 -07:00
|
|
|
thread_manager.print_msg(' ETag: %s', headers['etag'])
|
2012-05-08 11:17:04 +01:00
|
|
|
if 'x-object-manifest' in headers:
|
2013-06-26 22:47:49 -07:00
|
|
|
thread_manager.print_msg(' Manifest: %s',
|
|
|
|
headers['x-object-manifest'])
|
2012-05-08 11:17:04 +01:00
|
|
|
for key, value in headers.items():
|
|
|
|
if key.startswith('x-object-meta-'):
|
2013-06-26 22:47:49 -07:00
|
|
|
thread_manager.print_msg(
|
|
|
|
'%14s: %s',
|
|
|
|
'Meta %s' % key[len('x-object-meta-'):].title(),
|
|
|
|
value)
|
2012-05-08 11:17:04 +01:00
|
|
|
for key, value in headers.items():
|
|
|
|
if not key.startswith('x-object-meta-') and key not in (
|
|
|
|
'content-type', 'content-length', 'last-modified',
|
|
|
|
'etag', 'date', 'x-object-manifest'):
|
2013-06-26 22:47:49 -07:00
|
|
|
thread_manager.print_msg('%14s: %s', key.title(), value)
|
2013-04-25 23:19:52 +02:00
|
|
|
except ClientException as err:
|
2012-05-08 11:17:04 +01:00
|
|
|
if err.http_status != 404:
|
|
|
|
raise
|
2013-06-26 22:47:49 -07:00
|
|
|
thread_manager.error("Object %s/%s not found", args[0], args[1])
|
2012-05-08 11:17:04 +01:00
|
|
|
else:
|
2013-08-17 22:43:09 +10:00
|
|
|
thread_manager.error('Usage: %s stat %s\n%s', basename(argv[0]),
|
|
|
|
st_stat_options, st_stat_help)
|
|
|
|
|
2012-05-08 11:17:04 +01:00
|
|
|
|
2013-08-17 22:43:09 +10:00
|
|
|
st_post_options = '''[--read-acl <acl>] [--write-acl <acl>] [--sync-to]
|
|
|
|
[--sync-key <sync-key>] [--meta <name:value>]
|
|
|
|
[--header <header>]
|
|
|
|
<container> <object>
|
|
|
|
'''
|
2012-05-08 11:17:04 +01:00
|
|
|
|
|
|
|
st_post_help = '''
|
2013-08-17 22:43:09 +10:00
|
|
|
Updates meta information for the account, container, or object.
|
|
|
|
If the container is not found, it will be created automatically.
|
|
|
|
|
|
|
|
Positional arguments:
|
|
|
|
<container> Name of container to post to
|
|
|
|
<object> Name of object to post. Specify multiple times
|
|
|
|
for multiple objects
|
|
|
|
|
|
|
|
Optional arguments:
|
|
|
|
--read-acl <acl> Read ACL for containers. Quick summary of ACL syntax:
|
|
|
|
.r:*, .r:-.example.com, .r:www.example.com, account1,
|
|
|
|
account2:user2
|
|
|
|
--write-acl <acl> Write ACL for containers. Quick summary of ACL syntax:
|
|
|
|
account1 account2:user2
|
|
|
|
--sync-to <sync-to> Sync To for containers, for multi-cluster replication
|
|
|
|
--sync-key <sync-key> Sync Key for containers, for multi-cluster replication
|
|
|
|
--meta <name:value> Sets a meta data item. This option may be repeated.
|
|
|
|
Example: -m Color:Blue -m Size:Large
|
|
|
|
--header <header> Set request headers. This option may be repeated.
|
|
|
|
Example -H "content-type:text/plain"
|
|
|
|
'''.strip('\n')
|
2012-05-08 11:17:04 +01:00
|
|
|
|
|
|
|
|
2013-06-26 22:47:49 -07:00
|
|
|
def st_post(parser, args, thread_manager):
|
2013-04-28 19:16:38 -07:00
|
|
|
parser.add_option(
|
|
|
|
'-r', '--read-acl', dest='read_acl', help='Sets the '
|
2012-05-08 11:17:04 +01:00
|
|
|
'Read ACL for containers. Quick summary of ACL syntax: .r:*, '
|
|
|
|
'.r:-.example.com, .r:www.example.com, account1, account2:user2')
|
2013-04-28 19:16:38 -07:00
|
|
|
parser.add_option(
|
|
|
|
'-w', '--write-acl', dest='write_acl', help='Sets the '
|
2012-05-08 11:17:04 +01:00
|
|
|
'Write ACL for containers. Quick summary of ACL syntax: account1, '
|
|
|
|
'account2:user2')
|
2013-04-28 19:16:38 -07:00
|
|
|
parser.add_option(
|
|
|
|
'-t', '--sync-to', dest='sync_to', help='Sets the '
|
2012-05-08 11:17:04 +01:00
|
|
|
'Sync To for containers, for multi-cluster replication.')
|
2013-04-28 19:16:38 -07:00
|
|
|
parser.add_option(
|
|
|
|
'-k', '--sync-key', dest='sync_key', help='Sets the '
|
2012-05-08 11:17:04 +01:00
|
|
|
'Sync Key for containers, for multi-cluster replication.')
|
2013-04-28 19:16:38 -07:00
|
|
|
parser.add_option(
|
|
|
|
'-m', '--meta', action='append', dest='meta', default=[],
|
2012-05-08 11:17:04 +01:00
|
|
|
help='Sets a meta data item with the syntax name:value. This option '
|
|
|
|
'may be repeated. Example: -m Color:Blue -m Size:Large')
|
2013-04-28 19:16:38 -07:00
|
|
|
parser.add_option(
|
|
|
|
'-H', '--header', action='append', dest='header',
|
2013-03-18 09:47:59 -04:00
|
|
|
default=[], help='Set request headers with the syntax header:value. '
|
2013-08-17 22:43:09 +10:00
|
|
|
' This option may be repeated. Example -H "content-type:text/plain" '
|
2013-03-18 09:47:59 -04:00
|
|
|
'-H "Content-Length: 4000"')
|
2012-05-08 11:17:04 +01:00
|
|
|
(options, args) = parse_args(parser, args)
|
|
|
|
args = args[1:]
|
|
|
|
if (options.read_acl or options.write_acl or options.sync_to or
|
2013-04-28 19:16:38 -07:00
|
|
|
options.sync_key) and not args:
|
2012-05-08 11:17:04 +01:00
|
|
|
exit('-r, -w, -t, and -k options only allowed for containers')
|
|
|
|
conn = get_conn(options)
|
|
|
|
if not args:
|
2013-06-26 22:47:49 -07:00
|
|
|
headers = split_headers(
|
|
|
|
options.meta, 'X-Account-Meta-', thread_manager)
|
2012-05-08 11:17:04 +01:00
|
|
|
try:
|
|
|
|
conn.post_account(headers=headers)
|
2013-04-25 23:19:52 +02:00
|
|
|
except ClientException as err:
|
2012-05-08 11:17:04 +01:00
|
|
|
if err.http_status != 404:
|
|
|
|
raise
|
2013-06-26 22:47:49 -07:00
|
|
|
thread_manager.error('Account not found')
|
2012-05-08 11:17:04 +01:00
|
|
|
elif len(args) == 1:
|
|
|
|
if '/' in args[0]:
|
|
|
|
print >> stderr, 'WARNING: / in container name; you might have ' \
|
|
|
|
'meant %r instead of %r.' % \
|
|
|
|
(args[0].replace('/', ' ', 1), args[0])
|
2013-06-26 22:47:49 -07:00
|
|
|
headers = split_headers(options.meta, 'X-Container-Meta-',
|
|
|
|
thread_manager)
|
2012-05-08 11:17:04 +01:00
|
|
|
if options.read_acl is not None:
|
|
|
|
headers['X-Container-Read'] = options.read_acl
|
|
|
|
if options.write_acl is not None:
|
|
|
|
headers['X-Container-Write'] = options.write_acl
|
|
|
|
if options.sync_to is not None:
|
|
|
|
headers['X-Container-Sync-To'] = options.sync_to
|
|
|
|
if options.sync_key is not None:
|
|
|
|
headers['X-Container-Sync-Key'] = options.sync_key
|
|
|
|
try:
|
|
|
|
conn.post_container(args[0], headers=headers)
|
2013-04-25 23:19:52 +02:00
|
|
|
except ClientException as err:
|
2012-05-08 11:17:04 +01:00
|
|
|
if err.http_status != 404:
|
|
|
|
raise
|
|
|
|
conn.put_container(args[0], headers=headers)
|
|
|
|
elif len(args) == 2:
|
2013-06-26 22:47:49 -07:00
|
|
|
headers = split_headers(options.meta, 'X-Object-Meta-', thread_manager)
|
2013-03-18 09:47:59 -04:00
|
|
|
# add header options to the headers object for the request.
|
2013-06-26 22:47:49 -07:00
|
|
|
headers.update(split_headers(options.header, '', thread_manager))
|
2012-05-08 11:17:04 +01:00
|
|
|
try:
|
|
|
|
conn.post_object(args[0], args[1], headers=headers)
|
2013-04-25 23:19:52 +02:00
|
|
|
except ClientException as err:
|
2012-05-08 11:17:04 +01:00
|
|
|
if err.http_status != 404:
|
|
|
|
raise
|
2013-06-26 22:47:49 -07:00
|
|
|
thread_manager.error("Object '%s/%s' not found", args[0], args[1])
|
2012-05-08 11:17:04 +01:00
|
|
|
else:
|
2013-08-17 22:43:09 +10:00
|
|
|
thread_manager.error('Usage: %s post %s\n%s', basename(argv[0]),
|
|
|
|
st_post_options, st_post_help)
|
2012-05-08 11:17:04 +01:00
|
|
|
|
2013-08-17 22:43:09 +10:00
|
|
|
st_upload_options = '''[--changed] [--segment-size <size>]
|
|
|
|
[--segment-container <container>] [--leave-segments]
|
|
|
|
[--object-threads <thread>] [--segment-threads <threads>]
|
|
|
|
[--header <header>] [--use-slo]
|
|
|
|
<container> <file_or_directory>
|
|
|
|
'''
|
2012-05-08 11:17:04 +01:00
|
|
|
|
|
|
|
st_upload_help = '''
|
2013-08-17 22:43:09 +10:00
|
|
|
Uploads specified files and directories to the given container
|
|
|
|
|
|
|
|
Positional arguments:
|
|
|
|
<container> Name of container to upload to
|
|
|
|
<file_or_directory> Name of file or directory to upload. Specify multiple
|
|
|
|
times for multiple uploads
|
|
|
|
|
|
|
|
Optional arguments:
|
|
|
|
--changed Only upload files that have changed since the last
|
|
|
|
upload
|
|
|
|
--segment-size <size> Upload files in segments no larger than <size> and
|
|
|
|
then create a "manifest" file that will download all
|
|
|
|
the segments as if it were the original file
|
|
|
|
--segment-container <container>
|
|
|
|
Upload the segments into the specified container. If
|
|
|
|
not specified, the segments will be uploaded to a
|
|
|
|
<container>_segments container so as to not pollute the
|
|
|
|
main <container> listings.
|
|
|
|
--leave-segments Indicates that you want the older segments of manifest
|
|
|
|
objects left alone (in the case of overwrites)
|
|
|
|
--object-threads <threads>
|
|
|
|
Number of threads to use for uploading full objects.
|
|
|
|
Default is 10.
|
|
|
|
--segment-threads <threads>
|
|
|
|
Number of threads to use for uploading object segments.
|
|
|
|
Default is 10.
|
|
|
|
--header <header> Set request headers with the syntax header:value.
|
|
|
|
This option may be repeated.
|
|
|
|
Example -H "content-type:text/plain".
|
|
|
|
--use-slo When used in conjunction with --segment-size will
|
|
|
|
create a Static Large Object instead of the default
|
|
|
|
Dynamic Large Object.
|
2012-05-08 11:17:04 +01:00
|
|
|
'''.strip('\n')
|
|
|
|
|
|
|
|
|
2013-06-26 22:47:49 -07:00
|
|
|
def st_upload(parser, args, thread_manager):
|
2013-04-28 19:16:38 -07:00
|
|
|
parser.add_option(
|
|
|
|
'-c', '--changed', action='store_true', dest='changed',
|
2012-05-08 11:17:04 +01:00
|
|
|
default=False, help='Will only upload files that have changed since '
|
|
|
|
'the last upload')
|
2013-04-28 19:16:38 -07:00
|
|
|
parser.add_option(
|
|
|
|
'-S', '--segment-size', dest='segment_size', help='Will '
|
2012-05-08 11:17:04 +01:00
|
|
|
'upload files in segments no larger than <size> and then create a '
|
|
|
|
'"manifest" file that will download all the segments as if it were '
|
2013-02-20 11:19:04 +08:00
|
|
|
'the original file.')
|
2013-04-28 19:16:38 -07:00
|
|
|
parser.add_option(
|
|
|
|
'-C', '--segment-container', dest='segment_container',
|
2013-02-20 11:19:04 +08:00
|
|
|
help='Will upload the segments into the specified container.'
|
|
|
|
'If not specified, the segments will be uploaded to '
|
2012-05-08 11:17:04 +01:00
|
|
|
'<container>_segments container so as to not pollute the main '
|
|
|
|
'<container> listings.')
|
2013-04-28 19:16:38 -07:00
|
|
|
parser.add_option(
|
|
|
|
'', '--leave-segments', action='store_true',
|
2012-05-08 11:17:04 +01:00
|
|
|
dest='leave_segments', default=False, help='Indicates that you want '
|
|
|
|
'the older segments of manifest objects left alone (in the case of '
|
|
|
|
'overwrites)')
|
2013-04-28 19:16:38 -07:00
|
|
|
parser.add_option(
|
|
|
|
'', '--object-threads', type=int, default=10,
|
|
|
|
help='Number of threads to use for uploading full objects')
|
|
|
|
parser.add_option(
|
|
|
|
'', '--segment-threads', type=int, default=10,
|
|
|
|
help='Number of threads to use for uploading object segments')
|
|
|
|
parser.add_option(
|
|
|
|
'-H', '--header', action='append', dest='header',
|
2013-03-18 09:47:59 -04:00
|
|
|
default=[], help='Set request headers with the syntax header:value. '
|
2013-08-17 22:43:09 +10:00
|
|
|
' This option may be repeated. Example -H "content-type:text/plain" '
|
2013-03-18 09:47:59 -04:00
|
|
|
'-H "Content-Length: 4000"')
|
2013-08-17 22:43:09 +10:00
|
|
|
parser.add_option(
|
|
|
|
'', '--use-slo', action='store_true', default=False,
|
|
|
|
help='When used in conjunction with --segment-size will '
|
|
|
|
'create a Static Large Object instead of the default '
|
|
|
|
'Dynamic Large Object.')
|
2012-05-08 11:17:04 +01:00
|
|
|
(options, args) = parse_args(parser, args)
|
|
|
|
args = args[1:]
|
|
|
|
if len(args) < 2:
|
2013-06-26 22:47:49 -07:00
|
|
|
thread_manager.error(
|
2013-08-17 22:43:09 +10:00
|
|
|
'Usage: %s upload %s\n%s', basename(argv[0]), st_upload_options,
|
|
|
|
st_upload_help)
|
2012-05-08 11:17:04 +01:00
|
|
|
return
|
|
|
|
|
|
|
|
def _segment_job(job, conn):
|
|
|
|
if job.get('delete', False):
|
|
|
|
conn.delete_object(job['container'], job['obj'])
|
|
|
|
else:
|
|
|
|
fp = open(job['path'], 'rb')
|
|
|
|
fp.seek(job['segment_start'])
|
2013-04-28 19:16:38 -07:00
|
|
|
seg_container = args[0] + '_segments'
|
2013-02-20 11:19:04 +08:00
|
|
|
if options.segment_container:
|
2013-04-28 19:16:38 -07:00
|
|
|
seg_container = options.segment_container
|
2013-03-05 15:12:04 -08:00
|
|
|
etag = conn.put_object(job.get('container', seg_container),
|
2013-04-28 19:16:38 -07:00
|
|
|
job['obj'], fp,
|
|
|
|
content_length=job['segment_size'])
|
2013-03-05 15:12:04 -08:00
|
|
|
job['segment_location'] = '/%s/%s' % (seg_container, job['obj'])
|
|
|
|
job['segment_etag'] = etag
|
2012-05-08 11:17:04 +01:00
|
|
|
if options.verbose and 'log_line' in job:
|
|
|
|
if conn.attempts > 1:
|
2013-06-26 22:47:49 -07:00
|
|
|
thread_manager.print_msg('%s [after %d attempts]',
|
|
|
|
job['log_line'], conn.attempts)
|
2012-05-08 11:17:04 +01:00
|
|
|
else:
|
2013-06-26 22:47:49 -07:00
|
|
|
thread_manager.print_msg(job['log_line'])
|
2013-03-05 15:12:04 -08:00
|
|
|
return job
|
2012-05-08 11:17:04 +01:00
|
|
|
|
|
|
|
def _object_job(job, conn):
|
|
|
|
path = job['path']
|
|
|
|
container = job.get('container', args[0])
|
|
|
|
dir_marker = job.get('dir_marker', False)
|
|
|
|
try:
|
|
|
|
obj = path
|
|
|
|
if obj.startswith('./') or obj.startswith('.\\'):
|
|
|
|
obj = obj[2:]
|
|
|
|
if obj.startswith('/'):
|
|
|
|
obj = obj[1:]
|
2012-11-18 11:42:18 +00:00
|
|
|
put_headers = {'x-object-meta-mtime': "%f" % getmtime(path)}
|
2012-05-08 11:17:04 +01:00
|
|
|
if dir_marker:
|
|
|
|
if options.changed:
|
|
|
|
try:
|
|
|
|
headers = conn.head_object(container, obj)
|
|
|
|
ct = headers.get('content-type')
|
|
|
|
cl = int(headers.get('content-length'))
|
|
|
|
et = headers.get('etag')
|
|
|
|
mt = headers.get('x-object-meta-mtime')
|
|
|
|
if ct.split(';', 1)[0] == 'text/directory' and \
|
|
|
|
cl == 0 and \
|
|
|
|
et == 'd41d8cd98f00b204e9800998ecf8427e' and \
|
|
|
|
mt == put_headers['x-object-meta-mtime']:
|
|
|
|
return
|
2013-04-25 23:19:52 +02:00
|
|
|
except ClientException as err:
|
2012-05-08 11:17:04 +01:00
|
|
|
if err.http_status != 404:
|
|
|
|
raise
|
|
|
|
conn.put_object(container, obj, '', content_length=0,
|
|
|
|
content_type='text/directory',
|
|
|
|
headers=put_headers)
|
|
|
|
else:
|
|
|
|
# We need to HEAD all objects now in case we're overwriting a
|
|
|
|
# manifest object and need to delete the old segments
|
|
|
|
# ourselves.
|
|
|
|
old_manifest = None
|
2013-03-05 15:12:04 -08:00
|
|
|
old_slo_manifest_paths = []
|
|
|
|
new_slo_manifest_paths = set()
|
2012-05-08 11:17:04 +01:00
|
|
|
if options.changed or not options.leave_segments:
|
|
|
|
try:
|
|
|
|
headers = conn.head_object(container, obj)
|
|
|
|
cl = int(headers.get('content-length'))
|
|
|
|
mt = headers.get('x-object-meta-mtime')
|
|
|
|
if options.changed and cl == getsize(path) and \
|
|
|
|
mt == put_headers['x-object-meta-mtime']:
|
|
|
|
return
|
|
|
|
if not options.leave_segments:
|
|
|
|
old_manifest = headers.get('x-object-manifest')
|
2013-06-26 22:47:49 -07:00
|
|
|
if config_true_value(
|
2013-03-05 15:12:04 -08:00
|
|
|
headers.get('x-static-large-object')):
|
|
|
|
headers, manifest_data = conn.get_object(
|
|
|
|
container, obj,
|
|
|
|
query_string='multipart-manifest=get')
|
|
|
|
for old_seg in json.loads(manifest_data):
|
2013-09-10 13:12:29 -07:00
|
|
|
seg_path = old_seg['name'].lstrip('/')
|
2013-03-05 15:12:04 -08:00
|
|
|
if isinstance(seg_path, unicode):
|
|
|
|
seg_path = seg_path.encode('utf-8')
|
|
|
|
old_slo_manifest_paths.append(seg_path)
|
2013-04-25 23:19:52 +02:00
|
|
|
except ClientException as err:
|
2012-05-08 11:17:04 +01:00
|
|
|
if err.http_status != 404:
|
|
|
|
raise
|
2013-03-18 09:47:59 -04:00
|
|
|
# Merge the command line header options to the put_headers
|
|
|
|
put_headers.update(split_headers(options.header, '',
|
2013-06-26 22:47:49 -07:00
|
|
|
thread_manager))
|
2012-11-26 15:20:39 +08:00
|
|
|
# Don't do segment job if object is not big enough
|
2012-05-08 11:17:04 +01:00
|
|
|
if options.segment_size and \
|
2012-11-26 15:20:39 +08:00
|
|
|
getsize(path) > int(options.segment_size):
|
2013-02-20 11:19:04 +08:00
|
|
|
seg_container = container + '_segments'
|
|
|
|
if options.segment_container:
|
|
|
|
seg_container = options.segment_container
|
2012-05-08 11:17:04 +01:00
|
|
|
full_size = getsize(path)
|
2013-06-26 22:47:49 -07:00
|
|
|
|
|
|
|
slo_segments = []
|
|
|
|
error_counter = [0]
|
|
|
|
segment_manager = thread_manager.queue_manager(
|
|
|
|
_segment_job, options.segment_threads,
|
|
|
|
store_results=slo_segments,
|
|
|
|
error_counter=error_counter,
|
|
|
|
connection_maker=create_connection)
|
|
|
|
with segment_manager as segment_queue:
|
2013-06-26 11:41:29 -07:00
|
|
|
segment = 0
|
|
|
|
segment_start = 0
|
|
|
|
while segment_start < full_size:
|
|
|
|
segment_size = int(options.segment_size)
|
|
|
|
if segment_start + segment_size > full_size:
|
|
|
|
segment_size = full_size - segment_start
|
|
|
|
if options.use_slo:
|
|
|
|
segment_name = '%s/slo/%s/%s/%s/%08d' % (
|
|
|
|
obj, put_headers['x-object-meta-mtime'],
|
|
|
|
full_size, options.segment_size, segment)
|
|
|
|
else:
|
|
|
|
segment_name = '%s/%s/%s/%s/%08d' % (
|
|
|
|
obj, put_headers['x-object-meta-mtime'],
|
|
|
|
full_size, options.segment_size, segment)
|
|
|
|
segment_queue.put(
|
|
|
|
{'path': path, 'obj': segment_name,
|
|
|
|
'segment_start': segment_start,
|
|
|
|
'segment_size': segment_size,
|
|
|
|
'segment_index': segment,
|
|
|
|
'log_line': '%s segment %s' % (obj, segment)})
|
|
|
|
segment += 1
|
|
|
|
segment_start += segment_size
|
2013-06-26 22:47:49 -07:00
|
|
|
if error_counter[0]:
|
|
|
|
raise ClientException(
|
|
|
|
'Aborting manifest creation '
|
|
|
|
'because not all segments could be uploaded. %s/%s'
|
|
|
|
% (container, obj))
|
2013-03-05 15:12:04 -08:00
|
|
|
if options.use_slo:
|
|
|
|
slo_segments.sort(key=lambda d: d['segment_index'])
|
|
|
|
for seg in slo_segments:
|
|
|
|
seg_loc = seg['segment_location'].lstrip('/')
|
|
|
|
if isinstance(seg_loc, unicode):
|
|
|
|
seg_loc = seg_loc.encode('utf-8')
|
|
|
|
new_slo_manifest_paths.add(seg_loc)
|
|
|
|
|
|
|
|
manifest_data = json.dumps([
|
|
|
|
{'path': d['segment_location'],
|
|
|
|
'etag': d['segment_etag'],
|
|
|
|
'size_bytes': d['segment_size']}
|
|
|
|
for d in slo_segments])
|
|
|
|
|
|
|
|
put_headers['x-static-large-object'] = 'true'
|
|
|
|
conn.put_object(container, obj, manifest_data,
|
|
|
|
headers=put_headers,
|
|
|
|
query_string='multipart-manifest=put')
|
|
|
|
else:
|
|
|
|
new_object_manifest = '%s/%s/%s/%s/%s/' % (
|
|
|
|
quote(seg_container), quote(obj),
|
|
|
|
put_headers['x-object-meta-mtime'], full_size,
|
2013-04-28 19:16:38 -07:00
|
|
|
options.segment_size)
|
2013-03-05 15:12:04 -08:00
|
|
|
if old_manifest and old_manifest.rstrip('/') == \
|
|
|
|
new_object_manifest.rstrip('/'):
|
|
|
|
old_manifest = None
|
|
|
|
put_headers['x-object-manifest'] = new_object_manifest
|
|
|
|
conn.put_object(container, obj, '', content_length=0,
|
|
|
|
headers=put_headers)
|
2012-05-08 11:17:04 +01:00
|
|
|
else:
|
2013-04-28 19:16:38 -07:00
|
|
|
conn.put_object(
|
|
|
|
container, obj, open(path, 'rb'),
|
2012-05-08 11:17:04 +01:00
|
|
|
content_length=getsize(path), headers=put_headers)
|
2013-03-05 15:12:04 -08:00
|
|
|
if old_manifest or old_slo_manifest_paths:
|
2013-06-26 22:47:49 -07:00
|
|
|
segment_manager = thread_manager.queue_manager(
|
|
|
|
_segment_job, options.segment_threads,
|
|
|
|
connection_maker=create_connection)
|
|
|
|
segment_queue = segment_manager.queue
|
2013-03-05 15:12:04 -08:00
|
|
|
if old_manifest:
|
|
|
|
scontainer, sprefix = old_manifest.split('/', 1)
|
|
|
|
scontainer = unquote(scontainer)
|
|
|
|
sprefix = unquote(sprefix).rstrip('/') + '/'
|
|
|
|
for delobj in conn.get_container(scontainer,
|
|
|
|
prefix=sprefix)[1]:
|
2013-04-28 19:16:38 -07:00
|
|
|
segment_queue.put(
|
|
|
|
{'delete': True,
|
|
|
|
'container': scontainer,
|
|
|
|
'obj': delobj['name']})
|
2013-03-05 15:12:04 -08:00
|
|
|
if old_slo_manifest_paths:
|
|
|
|
for seg_to_delete in old_slo_manifest_paths:
|
|
|
|
if seg_to_delete in new_slo_manifest_paths:
|
|
|
|
continue
|
|
|
|
scont, sobj = \
|
|
|
|
seg_to_delete.split('/', 1)
|
2013-04-28 19:16:38 -07:00
|
|
|
segment_queue.put(
|
|
|
|
{'delete': True,
|
|
|
|
'container': scont, 'obj': sobj})
|
2012-05-08 11:17:04 +01:00
|
|
|
if not segment_queue.empty():
|
2013-06-26 22:47:49 -07:00
|
|
|
with segment_manager:
|
|
|
|
pass
|
2012-05-08 11:17:04 +01:00
|
|
|
if options.verbose:
|
|
|
|
if conn.attempts > 1:
|
2013-06-26 22:47:49 -07:00
|
|
|
thread_manager.print_msg('%s [after %d attempts]', obj,
|
|
|
|
conn.attempts)
|
2012-05-08 11:17:04 +01:00
|
|
|
else:
|
2013-06-26 22:47:49 -07:00
|
|
|
thread_manager.print_msg(obj)
|
2013-04-25 23:19:52 +02:00
|
|
|
except OSError as err:
|
2012-05-08 11:17:04 +01:00
|
|
|
if err.errno != ENOENT:
|
|
|
|
raise
|
2013-06-26 22:47:49 -07:00
|
|
|
thread_manager.error('Local file %r not found', path)
|
2012-05-08 11:17:04 +01:00
|
|
|
|
2013-06-26 22:47:49 -07:00
|
|
|
def _upload_dir(path, object_queue):
|
2012-05-08 11:17:04 +01:00
|
|
|
names = listdir(path)
|
|
|
|
if not names:
|
|
|
|
object_queue.put({'path': path, 'dir_marker': True})
|
|
|
|
else:
|
|
|
|
for name in listdir(path):
|
|
|
|
subpath = join(path, name)
|
|
|
|
if isdir(subpath):
|
2013-06-26 22:47:49 -07:00
|
|
|
_upload_dir(subpath, object_queue)
|
2012-05-08 11:17:04 +01:00
|
|
|
else:
|
|
|
|
object_queue.put({'path': subpath})
|
|
|
|
|
|
|
|
create_connection = lambda: get_conn(options)
|
|
|
|
conn = create_connection()
|
2013-06-26 22:47:49 -07:00
|
|
|
|
2012-05-08 11:17:04 +01:00
|
|
|
# Try to create the container, just in case it doesn't exist. If this
|
|
|
|
# fails, it might just be because the user doesn't have container PUT
|
|
|
|
# permissions, so we'll ignore any error. If there's really a problem,
|
|
|
|
# it'll surface on the first object PUT.
|
|
|
|
try:
|
|
|
|
conn.put_container(args[0])
|
|
|
|
if options.segment_size is not None:
|
2013-02-20 11:19:04 +08:00
|
|
|
seg_container = args[0] + '_segments'
|
|
|
|
if options.segment_container:
|
2013-04-28 19:16:38 -07:00
|
|
|
seg_container = options.segment_container
|
2013-02-20 11:19:04 +08:00
|
|
|
conn.put_container(seg_container)
|
2013-04-25 23:19:52 +02:00
|
|
|
except ClientException as err:
|
2012-05-08 11:17:04 +01:00
|
|
|
msg = ' '.join(str(x) for x in (err.http_status, err.http_reason))
|
|
|
|
if err.http_response_content:
|
|
|
|
if msg:
|
|
|
|
msg += ': '
|
|
|
|
msg += err.http_response_content[:60]
|
2013-06-26 22:47:49 -07:00
|
|
|
thread_manager.error(
|
|
|
|
'Error trying to create container %r: %s', args[0],
|
|
|
|
msg)
|
2013-04-25 23:19:52 +02:00
|
|
|
except Exception as err:
|
2013-06-26 22:47:49 -07:00
|
|
|
thread_manager.error(
|
|
|
|
'Error trying to create container %r: %s', args[0],
|
|
|
|
err)
|
|
|
|
|
|
|
|
object_manager = thread_manager.queue_manager(
|
|
|
|
_object_job, options.object_threads,
|
|
|
|
connection_maker=create_connection)
|
|
|
|
with object_manager as object_queue:
|
|
|
|
try:
|
|
|
|
for arg in args[1:]:
|
|
|
|
if isdir(arg):
|
|
|
|
_upload_dir(arg, object_queue)
|
|
|
|
else:
|
|
|
|
object_queue.put({'path': arg})
|
|
|
|
except ClientException as err:
|
|
|
|
if err.http_status != 404:
|
|
|
|
raise
|
|
|
|
thread_manager.error('Account not found')
|
2012-05-08 11:17:04 +01:00
|
|
|
|
|
|
|
|
2013-06-26 22:47:49 -07:00
|
|
|
def split_headers(options, prefix='', thread_manager=None):
|
2012-05-08 11:17:04 +01:00
|
|
|
"""
|
|
|
|
Splits 'Key: Value' strings and returns them as a dictionary.
|
|
|
|
|
|
|
|
:param options: An array of 'Key: Value' strings
|
|
|
|
:param prefix: String to prepend to all of the keys in the dictionary.
|
2013-06-26 22:47:49 -07:00
|
|
|
:param thread_manager: MultiThreadingManager for thread safe error
|
|
|
|
reporting.
|
2012-05-08 11:17:04 +01:00
|
|
|
"""
|
|
|
|
headers = {}
|
|
|
|
for item in options:
|
|
|
|
split_item = item.split(':', 1)
|
|
|
|
if len(split_item) == 2:
|
2013-03-18 09:47:59 -04:00
|
|
|
headers[(prefix + split_item[0]).title()] = split_item[1]
|
2012-05-08 11:17:04 +01:00
|
|
|
else:
|
|
|
|
error_string = "Metadata parameter %s must contain a ':'.\n%s" \
|
|
|
|
% (item, st_post_help)
|
2013-06-26 22:47:49 -07:00
|
|
|
if thread_manager:
|
|
|
|
thread_manager.error(error_string)
|
2012-05-08 11:17:04 +01:00
|
|
|
else:
|
|
|
|
exit(error_string)
|
|
|
|
return headers
|
|
|
|
|
|
|
|
|
|
|
|
def parse_args(parser, args, enforce_requires=True):
|
|
|
|
if not args:
|
|
|
|
args = ['-h']
|
|
|
|
(options, args) = parser.parse_args(args)
|
|
|
|
|
2012-08-22 18:06:39 +08:00
|
|
|
if (not (options.auth and options.user and options.key)):
|
2012-05-08 11:17:04 +01:00
|
|
|
# Use 2.0 auth if none of the old args are present
|
2012-05-08 14:05:21 +01:00
|
|
|
options.auth_version = '2.0'
|
2012-05-08 11:17:04 +01:00
|
|
|
|
|
|
|
# Use new-style args if old ones not present
|
|
|
|
if not options.auth and options.os_auth_url:
|
|
|
|
options.auth = options.os_auth_url
|
|
|
|
if not options.user and options.os_username:
|
|
|
|
options.user = options.os_username
|
|
|
|
if not options.key and options.os_password:
|
|
|
|
options.key = options.os_password
|
|
|
|
|
2012-07-04 21:46:02 +02:00
|
|
|
# Specific OpenStack options
|
|
|
|
options.os_options = {
|
|
|
|
'tenant_id': options.os_tenant_id,
|
|
|
|
'tenant_name': options.os_tenant_name,
|
|
|
|
'service_type': options.os_service_type,
|
2012-08-28 10:25:42 -04:00
|
|
|
'endpoint_type': options.os_endpoint_type,
|
2012-07-04 21:46:02 +02:00
|
|
|
'auth_token': options.os_auth_token,
|
|
|
|
'object_storage_url': options.os_storage_url,
|
2012-09-05 15:55:53 +01:00
|
|
|
'region_name': options.os_region_name,
|
2012-07-04 21:46:02 +02:00
|
|
|
}
|
|
|
|
|
2013-02-27 13:52:24 +01:00
|
|
|
if (options.os_options.get('object_storage_url') and
|
2013-04-28 19:16:38 -07:00
|
|
|
options.os_options.get('auth_token') and
|
|
|
|
options.auth_version == '2.0'):
|
2013-02-27 13:52:24 +01:00
|
|
|
return options, args
|
|
|
|
|
2012-05-08 11:17:04 +01:00
|
|
|
if enforce_requires and \
|
|
|
|
not (options.auth and options.user and options.key):
|
|
|
|
exit('''
|
2012-06-22 11:12:21 -04:00
|
|
|
Auth version 1.0 requires ST_AUTH, ST_USER, and ST_KEY environment variables
|
|
|
|
to be set or overridden with -A, -U, or -K.
|
|
|
|
|
|
|
|
Auth version 2.0 requires OS_AUTH_URL, OS_USERNAME, OS_PASSWORD, and
|
2012-08-23 14:09:56 -05:00
|
|
|
OS_TENANT_NAME OS_TENANT_ID to be set or overridden with --os-auth-url,
|
2013-07-07 16:17:28 +08:00
|
|
|
--os-username, --os-password, --os-tenant-name or os-tenant-id. Note:
|
|
|
|
adding "-V 2" is necessary for this.'''.strip('\n'))
|
2012-05-08 11:17:04 +01:00
|
|
|
return options, args
|
|
|
|
|
|
|
|
|
|
|
|
if __name__ == '__main__':
|
2013-01-22 16:32:50 +01:00
|
|
|
version = version_info.version_string()
|
|
|
|
parser = OptionParser(version='%%prog %s' % version,
|
|
|
|
usage='''
|
2013-08-17 22:43:09 +10:00
|
|
|
usage: %%prog [--version] [--help] [--snet] [--verbose]
|
|
|
|
[--debug] [--quiet] [--auth <auth_url>]
|
|
|
|
[--auth-version <auth_version>] [--user <username>]
|
|
|
|
[--key <api_key>] [--retries <num_retries>]
|
|
|
|
[--os-username <auth-user-name>] [--os-password <auth-password>]
|
|
|
|
[--os-tenant-id <auth-tenant-id>]
|
|
|
|
[--os-tenant-name <auth-tenant-name>]
|
|
|
|
[--os-auth-url <auth-url>] [--os-auth-token <auth-token>]
|
|
|
|
[--os-storage-url <storage-url>] [--os-region-name <region-name>]
|
|
|
|
[--os-service-type <service-type>]
|
|
|
|
[--os-endpoint-type <endpoint-type>]
|
|
|
|
[--os-cacert <ca-certificate>] [--insecure]
|
|
|
|
[--no-ssl-compression]
|
|
|
|
<subcommand> ...
|
|
|
|
|
|
|
|
Command-line interface to the OpenStack Swift API.
|
|
|
|
|
|
|
|
Positional arguments:
|
|
|
|
<subcommand>
|
|
|
|
delete Delete a container or objects within a container
|
|
|
|
downlad Download objects from containers
|
|
|
|
list Lists the containers for the account or the objects
|
|
|
|
for a container
|
|
|
|
post Updates meta information for the account, container,
|
|
|
|
or object
|
|
|
|
stat Displays information for the account, container,
|
|
|
|
or object
|
|
|
|
upload Uploads files or directories to the given container
|
2012-05-08 11:17:04 +01:00
|
|
|
|
2013-04-04 15:03:57 -05:00
|
|
|
Examples:
|
2013-08-17 22:43:09 +10:00
|
|
|
%%prog -A https://auth.api.rackspacecloud.com/v1.0 -U user -K api_key stat -v
|
2013-04-04 15:03:57 -05:00
|
|
|
|
|
|
|
%%prog --os-auth-url https://api.example.com/v2.0 --os-tenant-name tenant \\
|
2013-06-28 21:26:54 -07:00
|
|
|
--os-username user --os-password password list
|
2013-04-04 15:03:57 -05:00
|
|
|
|
|
|
|
%%prog --os-auth-token 6ee5eb33efad4e45ab46806eac010566 \\
|
|
|
|
--os-storage-url https://10.1.5.2:8080/v1/AUTH_ced809b6a4baea7aeab61a \\
|
|
|
|
list
|
2013-05-22 18:45:42 +00:00
|
|
|
|
|
|
|
%%prog list --lh
|
2012-05-08 11:17:04 +01:00
|
|
|
'''.strip('\n') % globals())
|
|
|
|
parser.add_option('-s', '--snet', action='store_true', dest='snet',
|
|
|
|
default=False, help='Use SERVICENET internal network')
|
|
|
|
parser.add_option('-v', '--verbose', action='count', dest='verbose',
|
|
|
|
default=1, help='Print more info')
|
2012-11-16 15:23:25 +10:00
|
|
|
parser.add_option('--debug', action='store_true', dest='debug',
|
|
|
|
default=False, help='Show the curl commands of all http '
|
|
|
|
'queries.')
|
2012-05-08 11:17:04 +01:00
|
|
|
parser.add_option('-q', '--quiet', action='store_const', dest='verbose',
|
|
|
|
const=0, default=1, help='Suppress status output')
|
|
|
|
parser.add_option('-A', '--auth', dest='auth',
|
|
|
|
default=environ.get('ST_AUTH'),
|
|
|
|
help='URL for obtaining an auth token')
|
|
|
|
parser.add_option('-V', '--auth-version',
|
|
|
|
dest='auth_version',
|
|
|
|
default=environ.get('ST_AUTH_VERSION', '1.0'),
|
|
|
|
type=str,
|
2012-11-13 08:41:42 +01:00
|
|
|
help='Specify a version for authentication. '
|
|
|
|
'Defaults to 1.0.')
|
2012-05-08 11:17:04 +01:00
|
|
|
parser.add_option('-U', '--user', dest='user',
|
|
|
|
default=environ.get('ST_USER'),
|
2012-11-13 08:41:42 +01:00
|
|
|
help='User name for obtaining an auth token.')
|
2012-05-08 11:17:04 +01:00
|
|
|
parser.add_option('-K', '--key', dest='key',
|
|
|
|
default=environ.get('ST_KEY'),
|
2012-11-13 08:41:42 +01:00
|
|
|
help='Key for obtaining an auth token.')
|
2013-07-08 15:32:28 -07:00
|
|
|
parser.add_option('-R', '--retries', type=int, default=5, dest='retries',
|
|
|
|
help='The number of times to retry a failed connection.')
|
2012-08-23 14:09:56 -05:00
|
|
|
parser.add_option('--os-username',
|
|
|
|
metavar='<auth-user-name>',
|
2012-05-08 11:17:04 +01:00
|
|
|
default=environ.get('OS_USERNAME'),
|
2012-06-22 11:12:21 -04:00
|
|
|
help='Openstack username. Defaults to env[OS_USERNAME].')
|
2012-08-23 14:09:56 -05:00
|
|
|
parser.add_option('--os_username',
|
|
|
|
help=SUPPRESS_HELP)
|
|
|
|
parser.add_option('--os-password',
|
|
|
|
metavar='<auth-password>',
|
2012-07-04 21:46:02 +02:00
|
|
|
default=environ.get('OS_PASSWORD'),
|
|
|
|
help='Openstack password. Defaults to env[OS_PASSWORD].')
|
2012-08-23 14:09:56 -05:00
|
|
|
parser.add_option('--os_password',
|
|
|
|
help=SUPPRESS_HELP)
|
|
|
|
parser.add_option('--os-tenant-id',
|
|
|
|
metavar='<auth-tenant-id>',
|
2012-07-04 21:46:02 +02:00
|
|
|
default=environ.get('OS_TENANT_ID'),
|
2013-02-27 13:52:24 +01:00
|
|
|
help='OpenStack tenant ID. '
|
|
|
|
'Defaults to env[OS_TENANT_ID]')
|
2012-08-23 14:09:56 -05:00
|
|
|
parser.add_option('--os_tenant_id',
|
|
|
|
help=SUPPRESS_HELP)
|
|
|
|
parser.add_option('--os-tenant-name',
|
|
|
|
metavar='<auth-tenant-name>',
|
2012-05-08 11:17:04 +01:00
|
|
|
default=environ.get('OS_TENANT_NAME'),
|
2012-11-13 08:41:42 +01:00
|
|
|
help='Openstack tenant name. '
|
2012-06-22 11:12:21 -04:00
|
|
|
'Defaults to env[OS_TENANT_NAME].')
|
2012-08-23 14:09:56 -05:00
|
|
|
parser.add_option('--os_tenant_name',
|
|
|
|
help=SUPPRESS_HELP)
|
|
|
|
parser.add_option('--os-auth-url',
|
|
|
|
metavar='<auth-url>',
|
2012-07-04 21:46:02 +02:00
|
|
|
default=environ.get('OS_AUTH_URL'),
|
|
|
|
help='Openstack auth URL. Defaults to env[OS_AUTH_URL].')
|
2012-08-23 14:09:56 -05:00
|
|
|
parser.add_option('--os_auth_url',
|
|
|
|
help=SUPPRESS_HELP)
|
|
|
|
parser.add_option('--os-auth-token',
|
|
|
|
metavar='<auth-token>',
|
2012-07-04 21:46:02 +02:00
|
|
|
default=environ.get('OS_AUTH_TOKEN'),
|
2013-04-04 15:03:57 -05:00
|
|
|
help='Openstack token. Defaults to env[OS_AUTH_TOKEN]. '
|
|
|
|
'Used with --os-storage-url to bypass the '
|
|
|
|
'usual username/password authentication.')
|
2012-08-23 14:09:56 -05:00
|
|
|
parser.add_option('--os_auth_token',
|
|
|
|
help=SUPPRESS_HELP)
|
|
|
|
parser.add_option('--os-storage-url',
|
|
|
|
metavar='<storage-url>',
|
2012-07-04 21:46:02 +02:00
|
|
|
default=environ.get('OS_STORAGE_URL'),
|
2012-11-13 08:41:42 +01:00
|
|
|
help='Openstack storage URL. '
|
2013-04-04 15:03:57 -05:00
|
|
|
'Defaults to env[OS_STORAGE_URL]. '
|
2013-06-28 21:26:54 -07:00
|
|
|
'Overrides the storage url returned during auth. '
|
|
|
|
'Will bypass authentication when used with '
|
|
|
|
'--os-auth-token.')
|
2012-08-23 14:09:56 -05:00
|
|
|
parser.add_option('--os_storage_url',
|
|
|
|
help=SUPPRESS_HELP)
|
2012-09-05 15:55:53 +01:00
|
|
|
parser.add_option('--os-region-name',
|
|
|
|
metavar='<region-name>',
|
|
|
|
default=environ.get('OS_REGION_NAME'),
|
|
|
|
help='Openstack region name. '
|
|
|
|
'Defaults to env[OS_REGION_NAME]')
|
|
|
|
parser.add_option('--os_region_name',
|
|
|
|
help=SUPPRESS_HELP)
|
2012-08-23 14:09:56 -05:00
|
|
|
parser.add_option('--os-service-type',
|
|
|
|
metavar='<service-type>',
|
2012-07-04 21:46:02 +02:00
|
|
|
default=environ.get('OS_SERVICE_TYPE'),
|
2012-11-13 08:41:42 +01:00
|
|
|
help='Openstack Service type. '
|
2012-07-04 21:46:02 +02:00
|
|
|
'Defaults to env[OS_SERVICE_TYPE]')
|
2012-08-23 14:09:56 -05:00
|
|
|
parser.add_option('--os_service_type',
|
|
|
|
help=SUPPRESS_HELP)
|
2012-08-28 10:25:42 -04:00
|
|
|
parser.add_option('--os-endpoint-type',
|
|
|
|
metavar='<endpoint-type>',
|
|
|
|
default=environ.get('OS_ENDPOINT_TYPE'),
|
2012-11-16 15:23:25 +10:00
|
|
|
help='Openstack Endpoint type. '
|
2012-08-28 10:25:42 -04:00
|
|
|
'Defaults to env[OS_ENDPOINT_TYPE]')
|
2012-12-19 09:52:54 -06:00
|
|
|
parser.add_option('--os-cacert',
|
|
|
|
metavar='<ca-certificate>',
|
|
|
|
default=environ.get('OS_CACERT'),
|
|
|
|
help='Specify a CA bundle file to use in verifying a '
|
|
|
|
'TLS (https) server certificate. '
|
|
|
|
'Defaults to env[OS_CACERT]')
|
2013-06-26 22:47:49 -07:00
|
|
|
default_val = config_true_value(environ.get('SWIFTCLIENT_INSECURE'))
|
2012-12-05 13:18:27 +09:00
|
|
|
parser.add_option('--insecure',
|
2013-01-10 12:59:57 +09:00
|
|
|
action="store_true", dest="insecure",
|
|
|
|
default=default_val,
|
2012-12-05 13:18:27 +09:00
|
|
|
help='Allow swiftclient to access insecure keystone '
|
|
|
|
'server. The keystone\'s certificate will not '
|
2013-01-10 12:59:57 +09:00
|
|
|
'be verified. '
|
|
|
|
'Defaults to env[SWIFTCLIENT_INSECURE] '
|
|
|
|
'(set to \'true\' to enable).')
|
2013-01-18 14:17:21 +00:00
|
|
|
parser.add_option('--no-ssl-compression',
|
|
|
|
action='store_false', dest='ssl_compression',
|
|
|
|
default=True,
|
|
|
|
help='Disable SSL compression when using https. '
|
|
|
|
'This may increase performance.')
|
2012-05-08 11:17:04 +01:00
|
|
|
parser.disable_interspersed_args()
|
|
|
|
(options, args) = parse_args(parser, argv[1:], enforce_requires=False)
|
|
|
|
parser.enable_interspersed_args()
|
|
|
|
|
|
|
|
commands = ('delete', 'download', 'list', 'post', 'stat', 'upload')
|
|
|
|
if not args or args[0] not in commands:
|
|
|
|
parser.print_usage()
|
|
|
|
if args:
|
|
|
|
exit('no such command: %s' % args[0])
|
|
|
|
exit()
|
|
|
|
|
2013-05-23 16:39:31 -04:00
|
|
|
signal.signal(signal.SIGINT, immediate_exit)
|
2012-06-18 09:46:54 -07:00
|
|
|
|
2012-11-16 15:23:25 +10:00
|
|
|
if options.debug:
|
|
|
|
logger = logging.getLogger("swiftclient")
|
|
|
|
logging.basicConfig(level=logging.DEBUG)
|
|
|
|
|
2013-06-26 22:47:49 -07:00
|
|
|
had_error = False
|
2012-05-08 11:17:04 +01:00
|
|
|
|
2013-06-26 22:47:49 -07:00
|
|
|
with MultiThreadingManager() as thread_manager:
|
|
|
|
parser.usage = globals()['st_%s_help' % args[0]]
|
|
|
|
try:
|
|
|
|
globals()['st_%s' % args[0]](parser, argv[1:], thread_manager)
|
|
|
|
except (ClientException, HTTPException, socket.error) as err:
|
|
|
|
thread_manager.error(str(err))
|
2012-05-08 11:17:04 +01:00
|
|
|
|
2013-06-26 22:47:49 -07:00
|
|
|
had_error = thread_manager.error_count
|
2012-05-08 11:17:04 +01:00
|
|
|
|
2013-06-26 22:47:49 -07:00
|
|
|
if had_error:
|
2013-05-23 16:39:31 -04:00
|
|
|
exit(1)
|