Upgrade swifclient to 2.2.0

This commit is contained in:
Matthew Jones
2014-08-06 15:37:02 -04:00
parent 07bfe7c275
commit 15c2627968
7 changed files with 1776 additions and 89 deletions

View File

@@ -46,7 +46,7 @@ prettytable==0.7.2 (prettytable.py)
pyrax==1.9.0 (pyrax/*) pyrax==1.9.0 (pyrax/*)
python-dateutil==2.2 (dateutil/*) python-dateutil==2.2 (dateutil/*)
python-novaclient==2.18.1 (novaclient/*, excluded bin/nova) python-novaclient==2.18.1 (novaclient/*, excluded bin/nova)
python-swiftclient==2.0.3 (swiftclient/*, excluded bin/swift) python-swiftclient==2.2.0 (swiftclient/*, excluded bin/swift)
pytz==2014.4 (pytz/*) pytz==2014.4 (pytz/*)
rackspace-auth-openstack==1.3 (rackspace_auth_openstack/*) rackspace-auth-openstack==1.3 (rackspace_auth_openstack/*)
rackspace-novaclient==1.4 (no files) rackspace-novaclient==1.4 (no files)

View File

@@ -1,4 +1,4 @@
# -*- encoding: utf-8 -*- # -*- coding: utf-8 -*-
# Copyright (c) 2012 Rackspace # Copyright (c) 2012 Rackspace
# flake8: noqa # flake8: noqa
# Licensed under the Apache License, Version 2.0 (the "License"); # Licensed under the Apache License, Version 2.0 (the "License");
@@ -14,7 +14,7 @@
# See the License for the specific language governing permissions and # See the License for the specific language governing permissions and
# limitations under the License. # limitations under the License.
"""" """
OpenStack Swift Python client binding. OpenStack Swift Python client binding.
""" """
from .client import * from .client import *

View File

@@ -22,14 +22,17 @@ import requests
import sys import sys
import logging import logging
import warnings import warnings
import functools
from distutils.version import StrictVersion from distutils.version import StrictVersion
from requests.exceptions import RequestException, SSLError from requests.exceptions import RequestException, SSLError
from urllib import quote as _quote from six.moves.urllib.parse import quote as _quote
from urlparse import urlparse, urlunparse from six.moves.urllib.parse import urlparse, urlunparse
from time import sleep, time from time import sleep, time
import six
from swiftclient.exceptions import ClientException, InvalidHeadersException from swiftclient import version as swiftclient_version
from swiftclient.exceptions import ClientException
from swiftclient.utils import LengthWrapper from swiftclient.utils import LengthWrapper
try: try:
@@ -85,8 +88,8 @@ def http_log(args, kwargs, resp, body):
else: else:
log_method = logger.info log_method = logger.info
log_method("REQ: %s" % "".join(string_parts)) log_method("REQ: %s", "".join(string_parts))
log_method("RESP STATUS: %s %s" % (resp.status, resp.reason)) log_method("RESP STATUS: %s %s", resp.status, resp.reason)
log_method("RESP HEADERS: %s", resp.getheaders()) log_method("RESP HEADERS: %s", resp.getheaders())
if body: if body:
log_method("RESP BODY: %s", body) log_method("RESP BODY: %s", body)
@@ -94,30 +97,20 @@ def http_log(args, kwargs, resp, body):
def quote(value, safe='/'): def quote(value, safe='/'):
""" """
Patched version of urllib.quote that encodes utf8 strings before quoting Patched version of urllib.quote that encodes utf8 strings before quoting.
On Python 3, call directly urllib.parse.quote().
""" """
if six.PY3:
return _quote(value, safe=safe)
value = encode_utf8(value) value = encode_utf8(value)
if isinstance(value, str): if isinstance(value, bytes):
return _quote(value, safe) return _quote(value, safe)
else: else:
return value return value
def validate_headers(headers):
if headers:
for key, raw_value in headers.iteritems():
value = str(encode_utf8(raw_value))
if '\n' in value:
raise InvalidHeadersException("%r header contained a "
"newline" % key)
if '\r' in value:
raise InvalidHeadersException("%r header contained a "
"carriage return" % key)
def encode_utf8(value): def encode_utf8(value):
if isinstance(value, unicode): if isinstance(value, six.text_type):
value = value.encode('utf8') value = value.encode('utf8')
return value return value
@@ -133,7 +126,7 @@ except ImportError:
class HTTPConnection: class HTTPConnection:
def __init__(self, url, proxy=None, cacert=None, insecure=False, def __init__(self, url, proxy=None, cacert=None, insecure=False,
ssl_compression=False): ssl_compression=False, default_user_agent=None):
""" """
Make an HTTPConnection or HTTPSConnection Make an HTTPConnection or HTTPSConnection
@@ -147,6 +140,12 @@ class HTTPConnection:
:param ssl_compression: SSL compression should be disabled by default :param ssl_compression: SSL compression should be disabled by default
and this setting is not usable as of now. The and this setting is not usable as of now. The
parameter is kept for backward compatibility. parameter is kept for backward compatibility.
:param default_user_agent: Set the User-Agent header on every request.
If set to None (default), the user agent
will be "python-swiftclient-<version>". This
may be overridden on a per-request basis by
explicitly setting the user-agent header on
a call to request().
:raises ClientException: Unable to handle protocol scheme :raises ClientException: Unable to handle protocol scheme
""" """
self.url = url self.url = url
@@ -154,6 +153,7 @@ class HTTPConnection:
self.host = self.parsed_url.netloc self.host = self.parsed_url.netloc
self.port = self.parsed_url.port self.port = self.parsed_url.port
self.requests_args = {} self.requests_args = {}
self.request_session = requests.Session()
if self.parsed_url.scheme not in ('http', 'https'): if self.parsed_url.scheme not in ('http', 'https'):
raise ClientException("Unsupported scheme") raise ClientException("Unsupported scheme")
self.requests_args['verify'] = not insecure self.requests_args['verify'] = not insecure
@@ -171,24 +171,49 @@ class HTTPConnection:
) )
} }
self.requests_args['stream'] = True self.requests_args['stream'] = True
if default_user_agent is None:
default_user_agent = \
'python-swiftclient-%s' % swiftclient_version.version_string
self.default_user_agent = default_user_agent
def _request(self, *arg, **kwarg): def _request(self, *arg, **kwarg):
""" Final wrapper before requests call, to be patched in tests """ """ Final wrapper before requests call, to be patched in tests """
return requests.request(*arg, **kwarg) return self.request_session.request(*arg, **kwarg)
def request(self, method, full_path, data=None, headers={}, files=None): def _encode_meta_headers(self, items):
"""Only encode metadata headers keys"""
ret = {}
for header, value in items:
value = encode_utf8(value)
header = header.lower()
if isinstance(header, six.string_types):
for target_type in 'container', 'account', 'object':
prefix = 'x-%s-meta-' % target_type
if header.startswith(prefix):
header = encode_utf8(header)
break
ret[header] = value
return ret
def request(self, method, full_path, data=None, headers=None, files=None):
""" Encode url and header, then call requests.request """ """ Encode url and header, then call requests.request """
headers = dict((encode_utf8(x), encode_utf8(y)) for x, y in if headers is None:
headers.iteritems()) headers = {}
url = encode_utf8("%s://%s%s" % ( else:
headers = self._encode_meta_headers(headers.items())
# set a default User-Agent header if it wasn't passed in
if 'user-agent' not in headers:
headers['user-agent'] = self.default_user_agent
url = "%s://%s%s" % (
self.parsed_url.scheme, self.parsed_url.scheme,
self.parsed_url.netloc, self.parsed_url.netloc,
full_path)) full_path)
self.resp = self._request(method, url, headers=headers, data=data, self.resp = self._request(method, url, headers=headers, data=data,
files=files, **self.requests_args) files=files, **self.requests_args)
return self.resp return self.resp
def putrequest(self, full_path, data=None, headers={}, files=None): def putrequest(self, full_path, data=None, headers=None, files=None):
""" """
Use python-requests files upload Use python-requests files upload
@@ -210,7 +235,8 @@ class HTTPConnection:
self.resp.getheaders = getheaders self.resp.getheaders = getheaders
self.resp.getheader = getheader self.resp.getheader = getheader
self.resp.read = self.resp.raw.read self.resp.read = functools.partial(self.resp.raw.read,
decode_content=True)
return self.resp return self.resp
@@ -236,9 +262,8 @@ def get_auth_1_0(url, user, key, snet, **kwargs):
# if we don't have a x-storage-url header and if we get a body. # if we don't have a x-storage-url header and if we get a body.
if resp.status < 200 or resp.status >= 300 or (body and not url): if resp.status < 200 or resp.status >= 300 or (body and not url):
raise ClientException('Auth GET failed', http_scheme=parsed.scheme, raise ClientException('Auth GET failed', http_scheme=parsed.scheme,
http_host=conn.host, http_port=conn.port, http_host=conn.host, http_path=parsed.path,
http_path=parsed.path, http_status=resp.status, http_status=resp.status, http_reason=resp.reason)
http_reason=resp.reason)
if snet: if snet:
parsed = list(urlparse(url)) parsed = list(urlparse(url))
# Second item in the list is the netloc # Second item in the list is the netloc
@@ -251,7 +276,7 @@ def get_auth_1_0(url, user, key, snet, **kwargs):
def get_keystoneclient_2_0(auth_url, user, key, os_options, **kwargs): def get_keystoneclient_2_0(auth_url, user, key, os_options, **kwargs):
""" """
Authenticate against a auth 2.0 server. Authenticate against an auth 2.0 server.
We are using the keystoneclient library for our 2.0 authentication. We are using the keystoneclient library for our 2.0 authentication.
""" """
@@ -336,7 +361,7 @@ def get_auth(auth_url, user, key, **kwargs):
if kwargs.get('tenant_name'): if kwargs.get('tenant_name'):
os_options['tenant_name'] = kwargs['tenant_name'] os_options['tenant_name'] = kwargs['tenant_name']
if (not 'tenant_name' in os_options): if not (os_options.get('tenant_name') or os_options.get('tenant_id')):
raise ClientException('No tenant specified') raise ClientException('No tenant specified')
cacert = kwargs.get('cacert', None) cacert = kwargs.get('cacert', None)
@@ -430,9 +455,9 @@ def get_account(url, token, marker=None, limit=None, prefix=None,
resp_headers[header.lower()] = value resp_headers[header.lower()] = value
if resp.status < 200 or resp.status >= 300: if resp.status < 200 or resp.status >= 300:
raise ClientException('Account GET failed', http_scheme=parsed.scheme, raise ClientException('Account GET failed', http_scheme=parsed.scheme,
http_host=conn.host, http_port=conn.port, http_host=conn.host, http_path=parsed.path,
http_path=parsed.path, http_query=qs, http_query=qs, http_status=resp.status,
http_status=resp.status, http_reason=resp.reason, http_reason=resp.reason,
http_response_content=body) http_response_content=body)
if resp.status == 204: if resp.status == 204:
return resp_headers, [] return resp_headers, []
@@ -463,9 +488,8 @@ def head_account(url, token, http_conn=None):
http_log((url, method,), {'headers': headers}, resp, body) http_log((url, method,), {'headers': headers}, resp, body)
if resp.status < 200 or resp.status >= 300: if resp.status < 200 or resp.status >= 300:
raise ClientException('Account HEAD failed', http_scheme=parsed.scheme, raise ClientException('Account HEAD failed', http_scheme=parsed.scheme,
http_host=conn.host, http_port=conn.port, http_host=conn.host, http_path=parsed.path,
http_path=parsed.path, http_status=resp.status, http_status=resp.status, http_reason=resp.reason,
http_reason=resp.reason,
http_response_content=body) http_response_content=body)
resp_headers = {} resp_headers = {}
for header, value in resp.getheaders(): for header, value in resp.getheaders():
@@ -503,7 +527,6 @@ def post_account(url, token, headers, http_conn=None, response_dict=None):
raise ClientException('Account POST failed', raise ClientException('Account POST failed',
http_scheme=parsed.scheme, http_scheme=parsed.scheme,
http_host=conn.host, http_host=conn.host,
http_port=conn.port,
http_path=parsed.path, http_path=parsed.path,
http_status=resp.status, http_status=resp.status,
http_reason=resp.reason, http_reason=resp.reason,
@@ -580,9 +603,8 @@ def get_container(url, token, container, marker=None, limit=None,
if resp.status < 200 or resp.status >= 300: if resp.status < 200 or resp.status >= 300:
raise ClientException('Container GET failed', raise ClientException('Container GET failed',
http_scheme=parsed.scheme, http_host=conn.host, http_scheme=parsed.scheme, http_host=conn.host,
http_port=conn.port, http_path=cont_path, http_path=cont_path, http_query=qs,
http_query=qs, http_status=resp.status, http_status=resp.status, http_reason=resp.reason,
http_reason=resp.reason,
http_response_content=body) http_response_content=body)
resp_headers = {} resp_headers = {}
for header, value in resp.getheaders(): for header, value in resp.getheaders():
@@ -623,8 +645,8 @@ def head_container(url, token, container, http_conn=None, headers=None):
if resp.status < 200 or resp.status >= 300: if resp.status < 200 or resp.status >= 300:
raise ClientException('Container HEAD failed', raise ClientException('Container HEAD failed',
http_scheme=parsed.scheme, http_host=conn.host, http_scheme=parsed.scheme, http_host=conn.host,
http_port=conn.port, http_path=path, http_path=path, http_status=resp.status,
http_status=resp.status, http_reason=resp.reason, http_reason=resp.reason,
http_response_content=body) http_response_content=body)
resp_headers = {} resp_headers = {}
for header, value in resp.getheaders(): for header, value in resp.getheaders():
@@ -656,7 +678,7 @@ def put_container(url, token, container, headers=None, http_conn=None,
if not headers: if not headers:
headers = {} headers = {}
headers['X-Auth-Token'] = token headers['X-Auth-Token'] = token
if not 'content-length' in (k.lower() for k in headers): if 'content-length' not in (k.lower() for k in headers):
headers['Content-Length'] = '0' headers['Content-Length'] = '0'
conn.request(method, path, '', headers) conn.request(method, path, '', headers)
resp = conn.getresponse() resp = conn.getresponse()
@@ -669,8 +691,8 @@ def put_container(url, token, container, headers=None, http_conn=None,
if resp.status < 200 or resp.status >= 300: if resp.status < 200 or resp.status >= 300:
raise ClientException('Container PUT failed', raise ClientException('Container PUT failed',
http_scheme=parsed.scheme, http_host=conn.host, http_scheme=parsed.scheme, http_host=conn.host,
http_port=conn.port, http_path=path, http_path=path, http_status=resp.status,
http_status=resp.status, http_reason=resp.reason, http_reason=resp.reason,
http_response_content=body) http_response_content=body)
@@ -696,7 +718,7 @@ def post_container(url, token, container, headers, http_conn=None,
path = '%s/%s' % (parsed.path, quote(container)) path = '%s/%s' % (parsed.path, quote(container))
method = 'POST' method = 'POST'
headers['X-Auth-Token'] = token headers['X-Auth-Token'] = token
if not 'content-length' in (k.lower() for k in headers): if 'content-length' not in (k.lower() for k in headers):
headers['Content-Length'] = '0' headers['Content-Length'] = '0'
conn.request(method, path, '', headers) conn.request(method, path, '', headers)
resp = conn.getresponse() resp = conn.getresponse()
@@ -709,8 +731,8 @@ def post_container(url, token, container, headers, http_conn=None,
if resp.status < 200 or resp.status >= 300: if resp.status < 200 or resp.status >= 300:
raise ClientException('Container POST failed', raise ClientException('Container POST failed',
http_scheme=parsed.scheme, http_host=conn.host, http_scheme=parsed.scheme, http_host=conn.host,
http_port=conn.port, http_path=path, http_path=path, http_status=resp.status,
http_status=resp.status, http_reason=resp.reason, http_reason=resp.reason,
http_response_content=body) http_response_content=body)
@@ -746,8 +768,8 @@ def delete_container(url, token, container, http_conn=None,
if resp.status < 200 or resp.status >= 300: if resp.status < 200 or resp.status >= 300:
raise ClientException('Container DELETE failed', raise ClientException('Container DELETE failed',
http_scheme=parsed.scheme, http_host=conn.host, http_scheme=parsed.scheme, http_host=conn.host,
http_port=conn.port, http_path=path, http_path=path, http_status=resp.status,
http_status=resp.status, http_reason=resp.reason, http_reason=resp.reason,
http_response_content=body) http_response_content=body)
@@ -799,8 +821,8 @@ def get_object(url, token, container, name, http_conn=None,
http_log(('%s%s' % (url.replace(parsed.path, ''), path), method,), http_log(('%s%s' % (url.replace(parsed.path, ''), path), method,),
{'headers': headers}, resp, body) {'headers': headers}, resp, body)
raise ClientException('Object GET failed', http_scheme=parsed.scheme, raise ClientException('Object GET failed', http_scheme=parsed.scheme,
http_host=conn.host, http_port=conn.port, http_host=conn.host, http_path=path,
http_path=path, http_status=resp.status, http_status=resp.status,
http_reason=resp.reason, http_reason=resp.reason,
http_response_content=body) http_response_content=body)
if resp_chunk_size: if resp_chunk_size:
@@ -847,9 +869,8 @@ def head_object(url, token, container, name, http_conn=None):
{'headers': headers}, resp, body) {'headers': headers}, resp, body)
if resp.status < 200 or resp.status >= 300: if resp.status < 200 or resp.status >= 300:
raise ClientException('Object HEAD failed', http_scheme=parsed.scheme, raise ClientException('Object HEAD failed', http_scheme=parsed.scheme,
http_host=conn.host, http_port=conn.port, http_host=conn.host, http_path=path,
http_path=path, http_status=resp.status, http_status=resp.status, http_reason=resp.reason,
http_reason=resp.reason,
http_response_content=body) http_response_content=body)
resp_headers = {} resp_headers = {}
for header, value in resp.getheaders(): for header, value in resp.getheaders():
@@ -878,11 +899,10 @@ def put_object(url, token=None, container=None, name=None, contents=None,
encoding will be used encoding will be used
:param etag: etag of contents; if None, no etag will be sent :param etag: etag of contents; if None, no etag will be sent
:param chunk_size: chunk size of data to write; it defaults to 65536; :param chunk_size: chunk size of data to write; it defaults to 65536;
used only if the the contents object has a 'read' used only if the contents object has a 'read'
method, eg. file-like objects, ignored otherwise method, e.g. file-like objects, ignored otherwise
:param content_type: value to send as content-type header; if None, no :param content_type: value to send as content-type header; if None, an
content-type will be set (remote end will likely try empty string value will be sent
to auto-detect it)
:param headers: additional headers to include in the request, if any :param headers: additional headers to include in the request, if any
:param http_conn: HTTP connection object (If None, it will create the :param http_conn: HTTP connection object (If None, it will create the
conn object) conn object)
@@ -916,11 +936,13 @@ def put_object(url, token=None, container=None, name=None, contents=None,
if content_length is not None: if content_length is not None:
headers['Content-Length'] = str(content_length) headers['Content-Length'] = str(content_length)
else: else:
for n, v in headers.iteritems(): for n, v in headers.items():
if n.lower() == 'content-length': if n.lower() == 'content-length':
content_length = int(v) content_length = int(v)
if content_type is not None: if content_type is not None:
headers['Content-Type'] = content_type headers['Content-Type'] = content_type
else: # python-requests sets application/x-www-form-urlencoded otherwise
headers['Content-Type'] = ''
if not contents: if not contents:
headers['Content-Length'] = '0' headers['Content-Length'] = '0'
if hasattr(contents, 'read'): if hasattr(contents, 'read'):
@@ -954,9 +976,8 @@ def put_object(url, token=None, container=None, name=None, contents=None,
if resp.status < 200 or resp.status >= 300: if resp.status < 200 or resp.status >= 300:
raise ClientException('Object PUT failed', http_scheme=parsed.scheme, raise ClientException('Object PUT failed', http_scheme=parsed.scheme,
http_host=conn.host, http_port=conn.port, http_host=conn.host, http_path=path,
http_path=path, http_status=resp.status, http_status=resp.status, http_reason=resp.reason,
http_reason=resp.reason,
http_response_content=body) http_response_content=body)
return resp.getheader('etag', '').strip('"') return resp.getheader('etag', '').strip('"')
@@ -994,9 +1015,8 @@ def post_object(url, token, container, name, headers, http_conn=None,
if resp.status < 200 or resp.status >= 300: if resp.status < 200 or resp.status >= 300:
raise ClientException('Object POST failed', http_scheme=parsed.scheme, raise ClientException('Object POST failed', http_scheme=parsed.scheme,
http_host=conn.host, http_port=conn.port, http_host=conn.host, http_path=path,
http_path=path, http_status=resp.status, http_status=resp.status, http_reason=resp.reason,
http_reason=resp.reason,
http_response_content=body) http_response_content=body)
@@ -1050,8 +1070,8 @@ def delete_object(url, token=None, container=None, name=None, http_conn=None,
if resp.status < 200 or resp.status >= 300: if resp.status < 200 or resp.status >= 300:
raise ClientException('Object DELETE failed', raise ClientException('Object DELETE failed',
http_scheme=parsed.scheme, http_host=conn.host, http_scheme=parsed.scheme, http_host=conn.host,
http_port=conn.port, http_path=path, http_path=path, http_status=resp.status,
http_status=resp.status, http_reason=resp.reason, http_reason=resp.reason,
http_response_content=body) http_response_content=body)
@@ -1071,9 +1091,8 @@ def get_capabilities(http_conn):
if resp.status < 200 or resp.status >= 300: if resp.status < 200 or resp.status >= 300:
raise ClientException('Capabilities GET failed', raise ClientException('Capabilities GET failed',
http_scheme=parsed.scheme, http_scheme=parsed.scheme,
http_host=conn.host, http_port=conn.port, http_host=conn.host, http_path=parsed.path,
http_path=parsed.path, http_status=resp.status, http_status=resp.status, http_reason=resp.reason,
http_reason=resp.reason,
http_response_content=body) http_response_content=body)
return json_loads(body) return json_loads(body)
@@ -1101,7 +1120,7 @@ class Connection(object):
:param max_backoff: maximum delay between retries (seconds) :param max_backoff: maximum delay between retries (seconds)
:param auth_version: OpenStack auth version, default is 1.0 :param auth_version: OpenStack auth version, default is 1.0
:param tenant_name: The tenant/account name, required when connecting :param tenant_name: The tenant/account name, required when connecting
to a auth 2.0 system. to an auth 2.0 system.
:param os_options: The OpenStack options which can have tenant_id, :param os_options: The OpenStack options which can have tenant_id,
auth_token, service_type, endpoint_type, auth_token, service_type, endpoint_type,
tenant_name, object_storage_url, region_name tenant_name, object_storage_url, region_name

View File

@@ -68,5 +68,5 @@ class ClientException(Exception):
return b and '%s: %s' % (a, b) or a return b and '%s: %s' % (a, b) or a
class InvalidHeadersException(Exception): class SkipTest(Exception):
pass pass

View File

@@ -12,10 +12,14 @@
# implied. # implied.
# See the License for the specific language governing permissions and # See the License for the specific language governing permissions and
# limitations under the License. # limitations under the License.
from __future__ import print_function
from itertools import chain from itertools import chain
import six
import sys import sys
from time import sleep from time import sleep
from Queue import Queue from six.moves.queue import Queue
from threading import Thread from threading import Thread
from traceback import format_exception from traceback import format_exception
@@ -181,7 +185,7 @@ class MultiThreadingManager(object):
(defaults to ``sys.stdout``) and the :meth:`error` method will print to the (defaults to ``sys.stdout``) and the :meth:`error` method will print to the
supplied ``error_stream`` (defaults to ``sys.stderr``). Both of these supplied ``error_stream`` (defaults to ``sys.stderr``). Both of these
printing methods will format the given string with any supplied ``*args`` printing methods will format the given string with any supplied ``*args``
(a la printf) and encode the result to utf8 if necessary. (a la printf). On Python 2, Unicode messages are encoded to utf8.
The attribute :attr:`self.error_count` is incremented once per error The attribute :attr:`self.error_count` is incremented once per error
message printed, so an application can tell if any worker threads message printed, so an application can tell if any worker threads
@@ -193,9 +197,11 @@ class MultiThreadingManager(object):
def __init__(self, print_stream=sys.stdout, error_stream=sys.stderr): def __init__(self, print_stream=sys.stdout, error_stream=sys.stderr):
""" """
:param print_stream: The stream to which :meth:`print_msg` sends :param print_stream: The stream to which :meth:`print_msg` sends
formatted messages, encoded to utf8 if necessary. formatted messages
:param error_stream: The stream to which :meth:`error` sends formatted :param error_stream: The stream to which :meth:`error` sends formatted
messages, encoded to utf8 if necessary. messages
On Python 2, Unicode messages are encoded to utf8.
""" """
self.print_stream = print_stream self.print_stream = print_stream
self.printer = QueueFunctionManager(self._print, 1, self) self.printer = QueueFunctionManager(self._print, 1, self)
@@ -256,9 +262,9 @@ class MultiThreadingManager(object):
def _print(self, item, stream=None): def _print(self, item, stream=None):
if stream is None: if stream is None:
stream = self.print_stream stream = self.print_stream
if isinstance(item, unicode): if six.PY2 and isinstance(item, unicode):
item = item.encode('utf8') item = item.encode('utf8')
print >>stream, item print(item, file=stream)
def _print_error(self, item): def _print_error(self, item):
self.error_count += 1 self.error_count += 1

File diff suppressed because it is too large Load Diff

View File

@@ -13,6 +13,12 @@
# See the License for the specific language governing permissions and # See the License for the specific language governing permissions and
# limitations under the License. # limitations under the License.
"""Miscellaneous utility functions for use with Swift.""" """Miscellaneous utility functions for use with Swift."""
import hashlib
import hmac
import logging
import time
import six
TRUE_VALUES = set(('true', '1', 'yes', 'on', 't', 'y')) TRUE_VALUES = set(('true', '1', 'yes', 'on', 't', 'y'))
@@ -24,7 +30,7 @@ def config_true_value(value):
This function come from swift.common.utils.config_true_value() This function come from swift.common.utils.config_true_value()
""" """
return value is True or \ return value is True or \
(isinstance(value, basestring) and value.lower() in TRUE_VALUES) (isinstance(value, six.string_types) and value.lower() in TRUE_VALUES)
def prt_bytes(bytes, human_flag): def prt_bytes(bytes, human_flag):
@@ -57,6 +63,51 @@ def prt_bytes(bytes, human_flag):
return(bytes) return(bytes)
def generate_temp_url(path, seconds, key, method):
""" Generates a temporary URL that gives unauthenticated access to the
Swift object.
:param path: The full path to the Swift object. Example:
/v1/AUTH_account/c/o.
:param seconds: The amount of time in seconds the temporary URL will
be valid for.
:param key: The secret temporary URL key set on the Swift cluster.
To set a key, run 'swift post -m
"Temp-URL-Key:b3968d0207b54ece87cccc06515a89d4"'
:param method: A HTTP method, typically either GET or PUT, to allow for
this temporary URL.
:raises: ValueError if seconds is not a positive integer
:raises: TypeError if seconds is not an integer
:return: the path portion of a temporary URL
"""
if seconds < 0:
raise ValueError('seconds must be a positive integer')
try:
expiration = int(time.time() + seconds)
except TypeError:
raise TypeError('seconds must be an integer')
standard_methods = ['GET', 'PUT', 'HEAD', 'POST', 'DELETE']
if method.upper() not in standard_methods:
logger = logging.getLogger("swiftclient")
logger.warning('Non default HTTP method %s for tempurl specified, '
'possibly an error', method.upper())
hmac_body = '\n'.join([method.upper(), str(expiration), path])
# Encode to UTF-8 for py3 compatibility
sig = hmac.new(key.encode(),
hmac_body.encode(),
hashlib.sha1).hexdigest()
return ('{path}?temp_url_sig='
'{sig}&temp_url_expires={exp}'.format(
path=path,
sig=sig,
exp=expiration)
)
class LengthWrapper(object): class LengthWrapper(object):
def __init__(self, readable, length): def __init__(self, readable, length):