Why Gemfury? Push, build, and install  RubyGems npm packages Python packages Maven artifacts PHP packages Go Modules Debian packages RPM packages NuGet packages

Repository URL to install this package:

Details    
Size: Mime:
# vim: tabstop=4 shiftwidth=4 softtabstop=4

# Copyright (c) 2014 TrilioData, Inc.
# All Rights Reserved.



import logging
import signal
import socket

from optparse import OptionParser, OptionGroup, SUPPRESS_HELP
from os import environ, walk, _exit as os_exit
from os.path import isfile, isdir, join
from six import text_type
from sys import argv as sys_argv, exit, stderr
from time import gmtime, strftime

from swiftclient import RequestException
from swiftclient.utils import config_true_value, generate_temp_url, prt_bytes
from swiftclient.multithreading import OutputManager
from swiftclient.exceptions import ClientException
from swiftclient import __version__ as client_version
from swiftclient.service import SwiftService, SwiftError, \
    SwiftUploadObject, get_conn
from swiftclient.command_helpers import print_account_stats, \
    print_container_stats, print_object_stats

try:
    from shlex import quote as sh_quote
except ImportError:
    from pipes import quote as sh_quote

BASENAME = 'swift'
commands = ('delete', 'download', 'list', 'post', 'stat', 'upload',
            'capabilities', 'info', 'tempurl', 'auth')


"""
{'verbose': 1, 'os_username': 'demo', 'os_user_domain_name': None, 'os_cacert': None, 'os_tenant_name': 'demo', 'os_user_domain_id': None, 'prefix': None, 'auth_version': u'2.0', 'ssl_compression': True, 'os_password': 'project1', 'os_user_id': None, 'os_project_id': None, 'long': False, 'totals': False, 'snet': False, 'os_tenant_id': None, 'os_project_name': None, 'os_service_type': None, 'insecure': False, 'os_help': None, 'os_project_domain_id': None, 'os_storage_url': None, 'human': False, 'auth': 'http://192.168.1.138:5000/v2.0', 'os_auth_url': 'http://192.168.1.138:5000/v2.0', 'user': 'demo', 'key': 'project1', 'os_region_name': 'RegionOne', 'info': False, 'retries': 5, 'os_auth_token': None, 'delimiter': None, 'os_options': {u'project_name': None, u'region_name': 'RegionOne', u'tenant_name': 'demo', u'user_domain_name': None, u'endpoint_type': None, u'object_storage_url': None, u'project_domain_id': None, u'user_id': None, u'user_domain_id': None, u'tenant_id': None, u'service_type': None, u'project_id': None, u'auth_token': None, u'project_domain_name': None}, 'debug': False, 'os_project_domain_name': None, 'os_endpoint_type': None}
"""

swift_list = None
swift_stat = None
swift_upload = None
swift_download = None
swift_delete = None
swift_post = None
swift_cap = None


def immediate_exit(signum, frame):
    stderr.write(" Aborted\n")
    os_exit(2)


def st_delete(args, options):
    _opts = dict(options)
    global swift_delete
    if swift_delete is None:
        swift_delete = SwiftService(options=_opts)

    try:
        if not args:
            del_iter = swift_delete.delete(options=_opts)
        else:
            container = args[0]
            if '/' in container:
                raise Exception(
                    'WARNING: / in container name; you '
                    "might have meant '%s' instead of '%s'." %
                    (container.replace('/', ' ', 1), container)
                )
                return
            objects = args[1:]
            if objects:
                del_iter = swift_delete.delete(container=container,
                                               objects=objects, options=_opts)
            else:
                del_iter = swift_delete.delete(
                    container=container, options=_opts)

        for r in del_iter:
            c = r.get('container', '')
            o = r.get('object', '')
            a = r.get('attempts')

        if r['success']:
            if options.verbose:
                a = ' [after {0} attempts]'.format(a) if a > 1 else ''

                if r['action'] == 'delete_object':
                    if options.yes_all:
                        p = '{0}/{1}'.format(c, o)
                    else:
                        p = o
                elif r['action'] == 'delete_segment':
                    p = '{0}/{1}'.format(c, o)
                elif r['action'] == 'delete_container':
                    p = c

                print('{0}{1}'.format(p, a))
        else:
            p = '{0}/{1}'.format(c, o) if o else c
            raise Exception('Error Deleting: {0}: {1}'
                            .format(p, r['error']))
    except SwiftError as err:
        raise Exception(err.value)


def st_download(args, options):
    global swift_download
    if options.out_file == '-':
        options.verbose = 0

    if options.out_file and len(args) != 2:
        raise Exception('-o option only allowed for single file downloads')

    if not options.prefix:
        options.remove_prefix = False

    if options.out_directory and len(args) == 2:
        raise Exception(
            'Please use -o option for single file downloads and renames')

    if (not args and not options.yes_all) or (args and options.yes_all):
        raise Exception('Usage: %s download %s\n%s', BASENAME,
                        st_download_options, st_download_help)
        return

    _opts = dict(options).copy()
    if swift_download is None:
        swift_download = SwiftService(options=_opts)
    try:
        if not args:
            down_iter = swift_download.download(options=_opts)
        else:
            container = args[0]
            if '/' in container:
                raise Exception(
                    'WARNING: / in container name; you '
                    "might have meant '%s' instead of '%s'." %
                    (container.replace('/', ' ', 1), container)
                )
                return
            objects = args[1:]
            if not objects:
                down_iter = swift_download.download(container, options=_opts)
            else:
                down_iter = swift_download.download(
                    container, objects, options=_opts)

        for down in down_iter:
            if options.out_file == '-' and 'contents' in down:
                contents = down['contents']
                for chunk in contents:
                    print(chunk)
            else:
                if down['success']:
                    if options.verbose:
                        start_time = down['start_time']
                        headers_receipt = \
                            down['headers_receipt'] - start_time
                        auth_time = down['auth_end_time'] - start_time
                        finish_time = down['finish_time']
                        read_length = down['read_length']
                        attempts = down['attempts']
                        total_time = finish_time - start_time
                        down_time = total_time - auth_time
                        _mega = 1000000
                        if down['pseudodir']:
                            time_str = (
                                'auth %.3fs, headers %.3fs, total %.3fs, '
                                'pseudo' % (
                                    auth_time, headers_receipt,
                                    total_time
                                )
                            )
                        else:
                            speed = float(read_length) / down_time / _mega
                            time_str = (
                                'auth %.3fs, headers %.3fs, total %.3fs, '
                                '%.3f MB/s' % (
                                    auth_time, headers_receipt,
                                    total_time, speed
                                )
                            )
                        path = down['path']
                        if attempts > 1:
                            print('%s [%s after %d attempts]' % (
                                path, time_str, attempts))
                        else:
                            print('%s [%s]' % (path, time_str))
                else:
                    error = down['error']
                    path = down['path']
                    container = down['container']
                    obj = down['object']
                    if isinstance(error, ClientException):
                        if error.http_status == 304 and \
                                options.skip_identical:
                            print("Skipped identical file '%s'" % path)
                            continue
                        if error.http_status == 404:
                            raise Exception(
                                "Object '%s/%s' not found", container, obj)
                            continue
                    raise Exception(
                        "Error downloading object '%s/%s': %s",
                        container, obj, error)

    except SwiftError as e:
        raise
    except Exception as e:
        raise


def st_list(args, options):
    global swift_list
    global swift_cap

    def _print_stats(options, stats):
        total_count = total_bytes = 0
        container = stats.get("container", None)
        for item in stats["listing"]:
            item_name = item.get('name')
            if not options.long and not options.human:
                print(item.get('name', item.get('subdir')))
            else:
                if not container:    # listing containers
                    item_bytes = item.get('bytes')
                    byte_str = prt_bytes(item_bytes, options.human)
                    count = item.get('count')
                    total_count += count
                    try:
                        meta = item.get('meta')
                        utc = gmtime(float(meta.get('x-timestamp')))
                        datestamp = strftime('%Y-%m-%d %H:%M:%S', utc)
                    except TypeError:
                        datestamp = '????-??-?? ??:??:??'
                    if not options.totals:
                        print(
                            "%5s %s %s %s" % (count, byte_str,
                                              datestamp, item_name))
                else:    # list container contents
                    subdir = item.get('subdir')
                    content_type = item.get('content_type')
                    if subdir is None:
                        item_bytes = item.get('bytes')
                        byte_str = prt_bytes(item_bytes, options.human)
                        date, xtime = item.get('last_modified').split('T')
                        xtime = xtime.split('.')[0]
                    else:
                        item_bytes = 0
                        byte_str = prt_bytes(item_bytes, options.human)
                        date = xtime = ''
                        item_name = subdir
                    if not options.totals:
                        print(
                            "%s %10s %8s %24s %s" %
                            (byte_str, date, xtime, content_type, item_name))
                total_bytes += item_bytes

        # report totals
        if options.long or options.human:
            if not container:
                print(
                    "%5s %s" % (prt_bytes(total_count, True),
                                prt_bytes(total_bytes, options.human)))
            else:
                print(
                    prt_bytes(total_bytes, options.human))

    if options.delimiter and not args:
        raise Exception('-d option only allowed for container listings')

    _opts = dict(options).copy()
    if _opts['human']:
        _opts.pop('human')
        _opts['long'] = True

    if options.totals and not options.long and not options.human:
        raise Exception(
            "Listing totals only works with -l or --lh.")
        return

    if swift_cap is None:
        swift_cap = SwiftService(options=_opts)

    try:
        capabilities_result = swift_cap.capabilities()
        capabilities = capabilities_result['capabilities']
        account_listing_limit = capabilities['swift']['account_listing_limit']
    except SwiftError as e:
        raise

    # Use a 'marker' to iterate over containers that have
    # more objects than the listing limit and store the
    # found objects in a set so that there are no duplicates.
    _opts['marker'] = ''
    container = None
    item_set = set()
    if swift_list is None:
        swift_list = SwiftService(options=_opts)
    try:
        if args:
            container = args[0]
            args = args[1:]
            if "/" in container or args:
                raise Exception('Usage error')
                return

        while True:
            stats_parts_gen = swift_list.list(container=container, options=_opts)
            gen_empty = True
            for stats in stats_parts_gen:
                if stats["success"]:
                    gen_empty = False
                    for item in stats["listing"]:
                        item_set.add(item.get('name'))
                    if len(stats["listing"]) < account_listing_limit:
                        return list(item_set)
                    else:
                        # If more than the listing_limit items were returned,
                        # we need to set the 'marker' to the last one before
                        # calling again.
                        _opts['marker'] = item.get('name')
                        continue
                else:
                    raise stats["error"]
            if gen_empty:
                return list(item_set)
        return []

    except SwiftError as e:
        raise


def st_stat(args, options):
    global swift_stat
    _opts = dict(options).copy()

    if swift_stat is None:
        swift_stat = SwiftService(options=_opts)

    try:
        if not args:
            stat_result = swift_stat.stat(options=_opts)
            if not stat_result['success']:
                raise stat_result['error']
            return stat_result
        else:
            container = args[0]
            if '/' in container:
                raise Exception(
                    'WARNING: / in container name; you might have '
                    "meant '%s' instead of '%s'." %
                    (container.replace('/', ' ', 1), container))
                return
            args = args[1:]
            if not args:
                stat_result = swift_stat.stat(
                    container=container, options=_opts)
                if not stat_result['success']:
                    raise stat_result['error']
                return stat_result
            else:
                if len(args) == 1:
                    objects = [args[0]]
                    stat_results = swift_stat.stat(
                        container=container, objects=objects, options=_opts)
                    for stat_result in stat_results:  # only 1 result
                        if stat_result["success"]:
                            return stat_result
                        else:
                            raise stat_result
                else:
                    raise Exception(
                        'Usage: %s stat %s\n%s', BASENAME,
                        st_stat_options, st_stat_help)

    except SwiftError as e:
        raise


def st_post(args, options):
    global swift_post
    if (options.read_acl or options.write_acl or options.sync_to or
            options.sync_key) and not args:
        raise Exception(
            '-r, -w, -t, and -k options only allowed for containers')

    _opts = dict(options).copy()

    if swift_post is None:
        swift_post = SwiftService(options=_opts)

    try:
        if not args:
            result = swift_post.post(options=_opts)
        else:
            container = args[0]
            if '/' in container:
                raise Exception(
                    'WARNING: / in container name; you might have '
                    "meant '%s' instead of '%s'." %
                    (args[0].replace('/', ' ', 1), args[0]))
                return
            args = args[1:]
            if args:
                if len(args) == 1:
                    objects = [args[0]]
                    results_iterator = swift_post.post(
                        container=container, objects=objects, options=_opts
                    )
                    result = next(results_iterator)
                else:
                    raise Exception(
                        'Usage: %s post %s\n%s', BASENAME,
                        st_post_options, st_post_help)
                    return
            else:
                result = swift_post.post(container=container, options=_opts)
        if not result["success"]:
            raise result

    except SwiftError as e:
        raise


def st_upload(args, options):
    global swift_upload

    container = args[0]
    files = args[1:]
    if options.object_name is not None:
        if len(files) > 1:
            raise Exception('object-name only be used with 1 file or dir')
            return
        else:
            orig_path = files[0]
    if options.segment_size:
        try:
            # If segment size only has digits assume it is bytes
            int(options.segment_size)
        except ValueError:
            try:
                size_mod = "BKMG".index(options.segment_size[-1].upper())
                multiplier = int(options.segment_size[:-1])
            except ValueError:
                raise Exception("Invalid segment size")
                return

            options.segment_size = str((1024 ** size_mod) * multiplier)
        if int(options.segment_size) <= 0:
            raise Exception("segment-size should be positive")
            return
    _opts = dict(options).copy()
    if swift_upload is None:
        swift_upload = SwiftService(options=_opts)

    try:
        objs = []
        dir_markers = []
        for f in files:
            if isfile(f):
                objs.append(f)
            elif isdir(f):
                for (_dir, _ds, _fs) in walk(f):
                    if not (_ds + _fs):
                        dir_markers.append(_dir)
                    else:
                        objs.extend([join(_dir, _f) for _f in _fs])
            else:
                raise Exception("Local file '%s' not found" % f)

        # Now that we've collected all the required files and dir markers
        # build the tuples for the call to upload
        if options.object_name is not None:
            objs = [
                SwiftUploadObject(
                    o, object_name=o.replace(
                        orig_path, options.object_name, 1
                    )
                ) for o in objs
            ]
            dir_markers = [
                SwiftUploadObject(
                    None, object_name=d.replace(
                        orig_path, options.object_name, 1
                    ), options={'dir_marker': True}
                ) for d in dir_markers
            ]

        for r in swift_upload.upload(
                container,
                objs + dir_markers,
                options=_opts):
            if r['success']:
                if options.verbose:
                    if 'attempts' in r and r['attempts'] > 1:
                        if 'object' in r:
                            print(
                                '%s [after %d attempts]' %
                                (r['object'],
                                 r['attempts'])
                            )
                        else:
                            if 'object' in r:
                                print (r['object'])
                            elif 'for_object' in r:
                                print(
                                    '%s segment %s' % (r['for_object'],
                                                       r['segment_index'])
                                )
                else:
                    error = r['error']
                    if 'action' in r and r['action'] == "create_container":
                            # it is not an error to be unable to create the
                            # container so print a warning and carry on
                        if isinstance(error, ClientException):
                            if (r['headers'] and
                                    'X-Storage-Policy' in r['headers']):
                                msg = ' with Storage Policy %s' % \
                                      r['headers']['X-Storage-Policy'].strip()
                            else:
                                msg = ' '.join(str(x) for x in (
                                    error.http_status, error.http_reason)
                                )
                                if error.http_response_content:
                                    if msg:
                                        msg += ': '
                                    msg += error.http_response_content[:60]
                                msg = ': %s' % msg
                        else:
                            msg = ': %s' % error
                        raise Exception(
                            'Warning: failed to create container '
                            "'%s'%s", container, msg
                        )
                    else:
                        raise Exception("%s" % error)
                        too_large = (isinstance(error, ClientException) and
                                     error.http_status == 413)
                        if too_large and options.verbose > 0:
                            raise Exception(
                                "Consider using the --segment-size option "
                                "to chunk the object")

    except SwiftError as e:
        raise


def st_capabilities(args, options):
    global swift_cap

    def _print_compo_cap(name, capabilities):
        for feature, options in sorted(list(capabilities.items()),
                                       key=lambda x: x[0]):
            print("%s: %s" % (name, feature))
            if options:
                print(" Options:")
                for key, value in sorted(list(options.items()),
                                         key=lambda x: x[0]):
                    print("  %s: %s" % (key, value))

    if args and len(args) > 2:
        raise Exception('Usage: %s capabilities %s\n%s',
                        BASENAME,
                        st_capabilities_options, st_capabilities_help)
        return

    _opts = dict(options).copy()
    if swift_cap is None:
        swift_cap = SwiftService(options=_opts)

    try:
        if len(args) == 2:
            url = args[1]
            capabilities_result = swift_cap.capabilities(url)
            capabilities = capabilities_result['capabilities']
        else:
            capabilities_result = swift_cap.capabilities()
            capabilities = capabilities_result['capabilities']

        _print_compo_cap('Core', {'swift': capabilities['swift']})
        del capabilities['swift']
        _print_compo_cap('Additional middleware', capabilities)
    except SwiftError as e:
        raise


def st_auth(args, options):
    _opts = vars(options)
    if options.verbose > 1:
        if options.auth_version in ('1', '1.0'):
            print('export ST_AUTH=%s' % sh_quote(options.auth))
            print('export ST_USER=%s' % sh_quote(options.user))
            print('export ST_KEY=%s' % sh_quote(options.key))
        else:
            print('export OS_IDENTITY_API_VERSION=%s' % sh_quote(
                options.auth_version))
            print('export OS_AUTH_VERSION=%s' % sh_quote(options.auth_version))
            print('export OS_AUTH_URL=%s' % sh_quote(options.auth))
            for k, v in sorted(_opts.items()):
                if v and k.startswith('os_') and \
                        k not in ('os_auth_url', 'os_options'):
                    print('export %s=%s' % (k.upper(), sh_quote(v)))
    else:
        conn = get_conn(_opts)
        url, token = conn.get_auth()
        print('export OS_STORAGE_URL=%s' % sh_quote(url))
        print('export OS_AUTH_TOKEN=%s' % sh_quote(token))


def st_tempurl(args, options):
    args = args[1:]
    if len(args) < 4:
        raise Exception('Usage: %s tempurl %s\n%s', BASENAME,
                        st_tempurl_options, st_tempurl_help)
        return
    method, seconds, path, key = args[:4]
    try:
        seconds = int(seconds)
    except ValueError:
        raise Exception('Seconds must be an integer')
        return
    if method.upper() not in ['GET', 'PUT', 'HEAD', 'POST', 'DELETE']:
        print ('WARNING: Non default HTTP method %s for '
               'tempurl specified, possibly an error' %
               method.upper())
    url = generate_temp_url(path, seconds, key, method,
                            absolute=options.absolute_expiry)
    print(url)