"""
Provides a direct client by using loaded Swift Proxy Server code to
work with Swift.
"""
"""
Copyright 2011-2013 Gregory Holt
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.
"""
import json
import StringIO
from swiftly.client.client import Client
from swiftly.client.utils import quote, headers_to_dict
[docs]class DirectClient(Client):
"""
Direct client by using loaded Swift Proxy Server code to work
with Swift.
:param swift_proxy: Default: None. If set, the
swift.proxy.server.Application given will be used instead of
creating a default Swift proxy application.
:param swift_proxy_storage_path: The path to the Swift account to
use (example: /v1/AUTH_test).
:param swift_proxy_cdn_path: The path to the Swift account to use
for CDN management (example: /v1/AUTH_test).
:param attempts: The number of times to try requests if a server
error occurs (5xx response). Default: 5
:param eventlet: Default: None. If True, Eventlet will be used if
installed. If False, Eventlet will not be used even if
installed. If None, the default, Eventlet will be used if
installed and its version is at least 0.11.0 when a CPU usage
bug was fixed.
:param chunk_size: Maximum size to read or write at one time.
:param verbose: Set to a ``func(msg, *args)`` that will be called
with debug messages. Constructing a string for output can be
done with msg % args.
:param verbose_id: Set to a string you wish verbose messages to
be prepended with; can help in identifying output when
multiple Clients are in use.
"""
def __init__(self, swift_proxy=None, swift_proxy_storage_path=None,
swift_proxy_cdn_path=None, attempts=5, eventlet=None,
chunk_size=65536, verbose=None, verbose_id=''):
super(DirectClient, self).__init__()
self.storage_path = swift_proxy_storage_path
self.cdn_path = swift_proxy_cdn_path
self.attempts = attempts
self.chunk_size = chunk_size
if verbose:
self.verbose = lambda m, *a, **k: verbose(
self._verbose_id + m, *a, **k)
else:
self.verbose = lambda *a, **k: None
self.verbose_id = verbose_id
self._verbose_id = self.verbose_id
if self._verbose_id:
self._verbose_id += ' '
self.swift_proxy = swift_proxy
if not swift_proxy:
self.verbose('Creating default proxy instance.')
import swift.proxy.server
from swiftly.client.localmemcache import LocalMemcache
from swiftly.client.nulllogger import NullLogger
try:
import swift.common.swob
self.Request = swift.common.swob.Request
except ImportError:
import webob
self.Request = webob.Request
self.swift_proxy = swift.proxy.server.Application(
{}, memcache=LocalMemcache(), logger=NullLogger())
if eventlet is None:
try:
import eventlet
# Eventlet 0.11.0 fixed the CPU bug
if eventlet.__version__ >= '0.11.0':
eventlet = True
except ImportError:
pass
if eventlet:
try:
import eventlet
self.sleep = eventlet.sleep
except ImportError:
import time
self.sleep = time.sleep
else:
import time
self.sleep = time.sleep
def _default_reset_func(self):
raise Exception(
'Failure and no ability to reset contents for reupload.')
[docs] def request(self, method, path, contents, headers, decode_json=False,
stream=False, query=None, cdn=False):
"""
See :py:func:`swiftly.client.client.Client.request`
"""
if query:
path += '?' + '&'.join(
('%s=%s' % (quote(k), quote(v)) if v else quote(k))
for k, v in sorted(query.iteritems()))
reset_func = self._default_reset_func
if isinstance(contents, basestring):
contents = StringIO.StringIO(contents)
tell = getattr(contents, 'tell', None)
seek = getattr(contents, 'seek', None)
if tell and seek:
try:
orig_pos = tell()
reset_func = lambda: seek(orig_pos)
except Exception:
tell = seek = None
elif not contents:
reset_func = lambda: None
status = 0
reason = 'Unknown'
attempt = 0
while attempt < self.attempts:
attempt += 1
if cdn:
conn_path = self.cdn_path
else:
conn_path = self.storage_path
titled_headers = dict((k.title(), v) for k, v in {
'User-Agent': self.user_agent}.iteritems())
if headers:
titled_headers.update(
(k.title(), v) for k, v in headers.iteritems())
resp = None
if not hasattr(contents, 'read'):
if method not in self.no_content_methods and contents and \
'Content-Length' not in titled_headers and \
'Transfer-Encoding' not in titled_headers:
titled_headers['Content-Length'] = str(
len(contents or ''))
req = self.Request.blank(
conn_path + path,
environ={'REQUEST_METHOD': method, 'swift_owner': True},
headers=titled_headers, body=contents)
verbose_headers = ' '.join(
'%s: %s' % (k, v) for k, v in titled_headers.iteritems())
self.verbose(
'> %s %s %s', method, conn_path + path, verbose_headers)
resp = req.get_response(self.swift_proxy)
else:
req = self.Request.blank(
conn_path + path,
environ={'REQUEST_METHOD': method, 'swift_owner': True},
headers=titled_headers)
content_length = None
for h, v in titled_headers.iteritems():
if h.lower() == 'content-length':
content_length = int(v)
req.headers[h] = v
if method not in self.no_content_methods and \
content_length is None:
titled_headers['Transfer-Encoding'] = 'chunked'
req.headers['Transfer-Encoding'] = 'chunked'
else:
req.content_length = content_length
req.body_file = contents
verbose_headers = ' '.join(
'%s: %s' % (k, v) for k, v in titled_headers.iteritems())
self.verbose(
'> %s %s %s', method, conn_path + path, verbose_headers)
resp = req.get_response(self.swift_proxy)
status = resp.status_int
reason = resp.status.split(' ', 1)[1]
hdrs = headers_to_dict(resp.headers.items())
if stream:
def iter_reader(self, size=-1):
if size == -1:
return ''.join(resp.app_iter)
else:
try:
return resp.app_iter.next()
except StopIteration:
return ''
iter_reader.read = iter_reader
value = iter_reader
else:
value = resp.body
self.verbose('< %s %s', status, reason)
if status and status // 100 != 5:
if not stream and decode_json and status // 100 == 2:
if value:
value = json.loads(value)
else:
value = None
return (status, reason, hdrs, value)
if reset_func:
reset_func()
self.sleep(2 ** attempt)
raise Exception('%s %s failed: %s %s' % (method, path, status, reason))
[docs] def get_account_hash(self):
"""
See :py:func:`swiftly.client.client.Client.get_account_hash`
"""
return self.storage_path.rsplit('/', 1)[1]