summaryrefslogtreecommitdiff
path: root/swiftclient/client.py
diff options
context:
space:
mode:
Diffstat (limited to 'swiftclient/client.py')
-rw-r--r--swiftclient/client.py105
1 files changed, 96 insertions, 9 deletions
diff --git a/swiftclient/client.py b/swiftclient/client.py
index f556afd..988c7d9 100644
--- a/swiftclient/client.py
+++ b/swiftclient/client.py
@@ -827,7 +827,8 @@ def post_account(url, token, headers, http_conn=None, response_dict=None,
def get_container(url, token, container, marker=None, limit=None,
prefix=None, delimiter=None, end_marker=None,
path=None, http_conn=None,
- full_listing=False, service_token=None, headers=None):
+ full_listing=False, service_token=None, headers=None,
+ query_string=None):
"""
Get a listing of objects for the container.
@@ -846,6 +847,7 @@ def get_container(url, token, container, marker=None, limit=None,
of 10000 listings
:param service_token: service auth token
:param headers: additional headers to include in the request
+ :param query_string: if set will be appended with '?' to generated path
:returns: a tuple of (response headers, a list of objects) The response
headers will be a dict and all header names will be lowercase.
:raises ClientException: HTTP GET request failed
@@ -889,6 +891,8 @@ def get_container(url, token, container, marker=None, limit=None,
qs += '&end_marker=%s' % quote(end_marker)
if path:
qs += '&path=%s' % quote(path)
+ if query_string:
+ qs += '&%s' % query_string.lstrip('?')
if service_token:
headers['X-Service-Token'] = service_token
method = 'GET'
@@ -950,7 +954,7 @@ def head_container(url, token, container, http_conn=None, headers=None,
def put_container(url, token, container, headers=None, http_conn=None,
- response_dict=None, service_token=None):
+ response_dict=None, service_token=None, query_string=None):
"""
Create a container
@@ -963,6 +967,7 @@ def put_container(url, token, container, headers=None, http_conn=None,
:param response_dict: an optional dictionary into which to place
the response - status, reason and headers
:param service_token: service auth token
+ :param query_string: if set will be appended with '?' to generated path
:raises ClientException: HTTP PUT request failed
"""
if http_conn:
@@ -978,6 +983,8 @@ def put_container(url, token, container, headers=None, http_conn=None,
headers['X-Service-Token'] = service_token
if 'content-length' not in (k.lower() for k in headers):
headers['Content-Length'] = '0'
+ if query_string:
+ path += '?' + query_string.lstrip('?')
conn.request(method, path, '', headers)
resp = conn.getresponse()
body = resp.read()
@@ -1031,7 +1038,8 @@ def post_container(url, token, container, headers, http_conn=None,
def delete_container(url, token, container, http_conn=None,
- response_dict=None, service_token=None):
+ response_dict=None, service_token=None,
+ query_string=None):
"""
Delete a container
@@ -1043,6 +1051,7 @@ def delete_container(url, token, container, http_conn=None,
:param response_dict: an optional dictionary into which to place
the response - status, reason and headers
:param service_token: service auth token
+ :param query_string: if set will be appended with '?' to generated path
:raises ClientException: HTTP DELETE request failed
"""
if http_conn:
@@ -1053,6 +1062,8 @@ def delete_container(url, token, container, http_conn=None,
headers = {'X-Auth-Token': token}
if service_token:
headers['X-Service-Token'] = service_token
+ if query_string:
+ path += '?' + query_string.lstrip('?')
method = 'DELETE'
conn.request(method, path, '', headers)
resp = conn.getresponse()
@@ -1319,6 +1330,70 @@ def post_object(url, token, container, name, headers, http_conn=None,
raise ClientException.from_response(resp, 'Object POST failed', body)
+def copy_object(url, token, container, name, destination=None,
+ headers=None, fresh_metadata=None, http_conn=None,
+ response_dict=None, service_token=None):
+ """
+ Copy object
+
+ :param url: storage URL
+ :param token: auth token; if None, no token will be sent
+ :param container: container name that the source object is in
+ :param name: source object name
+ :param destination: The container and object name of the destination object
+ in the form of /container/object; if None, the copy
+ will use the source as the destination.
+ :param headers: additional headers to include in the request
+ :param fresh_metadata: Enables object creation that omits existing user
+ metadata, default None
+ :param http_conn: HTTP connection object (If None, it will create the
+ conn object)
+ :param response_dict: an optional dictionary into which to place
+ the response - status, reason and headers
+ :param service_token: service auth token
+ :raises ClientException: HTTP COPY request failed
+ """
+ if http_conn:
+ parsed, conn = http_conn
+ else:
+ parsed, conn = http_connection(url)
+
+ path = parsed.path
+ container = quote(container)
+ name = quote(name)
+ path = '%s/%s/%s' % (path.rstrip('/'), container, name)
+
+ headers = dict(headers) if headers else {}
+
+ if destination is not None:
+ headers['Destination'] = quote(destination)
+ elif container and name:
+ headers['Destination'] = '/%s/%s' % (container, name)
+
+ if token is not None:
+ headers['X-Auth-Token'] = token
+ if service_token is not None:
+ headers['X-Service-Token'] = service_token
+
+ if fresh_metadata is not None:
+ # remove potential fresh metadata headers
+ for fresh_hdr in [hdr for hdr in headers.keys()
+ if hdr.lower() == 'x-fresh-metadata']:
+ headers.pop(fresh_hdr)
+ headers['X-Fresh-Metadata'] = 'true' if fresh_metadata else 'false'
+
+ conn.request('COPY', path, '', headers)
+ resp = conn.getresponse()
+ body = resp.read()
+ http_log(('%s%s' % (url.replace(parsed.path, ''), path), 'COPY',),
+ {'headers': headers}, resp, body)
+
+ store_response(resp, response_dict)
+
+ if resp.status < 200 or resp.status >= 300:
+ raise ClientException.from_response(resp, 'Object COPY failed', body)
+
+
def delete_object(url, token=None, container=None, name=None, http_conn=None,
headers=None, proxy=None, query_string=None,
response_dict=None, service_token=None):
@@ -1625,7 +1700,7 @@ class Connection(object):
def get_container(self, container, marker=None, limit=None, prefix=None,
delimiter=None, end_marker=None, path=None,
- full_listing=False, headers=None):
+ full_listing=False, headers=None, query_string=None):
"""Wrapper for :func:`get_container`"""
# TODO(unknown): With full_listing=True this will restart the entire
# listing with each retry. Need to make a better version that just
@@ -1633,22 +1708,27 @@ class Connection(object):
return self._retry(None, get_container, container, marker=marker,
limit=limit, prefix=prefix, delimiter=delimiter,
end_marker=end_marker, path=path,
- full_listing=full_listing, headers=headers)
+ full_listing=full_listing, headers=headers,
+ query_string=query_string)
- def put_container(self, container, headers=None, response_dict=None):
+ def put_container(self, container, headers=None, response_dict=None,
+ query_string=None):
"""Wrapper for :func:`put_container`"""
return self._retry(None, put_container, container, headers=headers,
- response_dict=response_dict)
+ response_dict=response_dict,
+ query_string=query_string)
def post_container(self, container, headers, response_dict=None):
"""Wrapper for :func:`post_container`"""
return self._retry(None, post_container, container, headers,
response_dict=response_dict)
- def delete_container(self, container, response_dict=None):
+ def delete_container(self, container, response_dict=None,
+ query_string=None):
"""Wrapper for :func:`delete_container`"""
return self._retry(None, delete_container, container,
- response_dict=response_dict)
+ response_dict=response_dict,
+ query_string=query_string)
def head_object(self, container, obj, headers=None):
"""Wrapper for :func:`head_object`"""
@@ -1711,6 +1791,13 @@ class Connection(object):
return self._retry(None, post_object, container, obj, headers,
response_dict=response_dict)
+ def copy_object(self, container, obj, destination=None, headers=None,
+ fresh_metadata=None, response_dict=None):
+ """Wrapper for :func:`copy_object`"""
+ return self._retry(None, copy_object, container, obj, destination,
+ headers, fresh_metadata,
+ response_dict=response_dict)
+
def delete_object(self, container, obj, query_string=None,
response_dict=None):
"""Wrapper for :func:`delete_object`"""