self.status = status
class Client(object):
- def __init__(self, host, account, api='v1', verbose=False, debug=False):
+ def __init__(self, host, token, account, api='v1', verbose=False, debug=False):
"""`host` can also include a port, e.g '127.0.0.1:8000'."""
self.host = host
self.api = api
self.verbose = verbose or debug
self.debug = debug
+ self.token = token
def _chunked_transfer(self, path, method='PUT', f=stdin, headers=None,
blocksize=1024):
# write header
path = '/%s/%s%s' % (self.api, self.account, path)
http.putrequest(method, path)
+ http.putheader('X-Auth-Token', self.token)
http.putheader('Content-Type', 'application/octet-stream')
http.putheader('Transfer-Encoding', 'chunked')
if headers:
for k,v in params.items():
if v:
full_path = '%s&%s=%s' %(full_path, k, v)
+ else:
+ full_path = '%s&%s' %(full_path, k)
conn = HTTPConnection(self.host)
#encode whitespace
kwargs = {}
kwargs['headers'] = headers or {}
+ kwargs['headers']['X-Auth-Token'] = self.token
if not headers or \
'Transfer-Encoding' not in headers \
or headers['Transfer-Encoding'] != 'chunked':
kwargs['headers']['Content-Length'] = len(body) if body else 0
if body:
kwargs['body'] = body
- kwargs['headers']['Content-Type'] = 'application/octet-stream'
- #print '****', method, full_path, kwargs
+ else:
+ kwargs['headers']['Content-Type'] = ''
+ kwargs['headers'].setdefault('Content-Type', 'application/octet-stream')
try:
conn.request(method, full_path, **kwargs)
except socket.error, e:
def head(self, path, format='text', params=None):
return self.req('HEAD', path, format=format, params=params)
- def post(self, path, body=None, format='text', headers=None):
- return self.req('POST', path, body, headers=headers, format=format)
+ def post(self, path, body=None, format='text', headers=None, params=None):
+ return self.req('POST', path, body, headers=headers, format=format,
+ params=params)
def put(self, path, body=None, format='text', headers=None):
return self.req('PUT', path, body, headers=headers, format=format)
status, headers, data = self.get(path, format=format, headers=headers,
params=params)
if detail:
- data = json.loads(data)
+ data = json.loads(data) if data else ''
else:
data = data.strip().split('\n')
return data
def _update_metadata(self, path, entity, **meta):
"""
- adds new and updates the values of previously set metadata
+ adds new and updates the values of previously set metadata
"""
- for key, val in meta.items():
- meta.pop(key)
- meta['X-%s-Meta-%s' %(entity.capitalize(), key.capitalize())] = val
- prev_meta = self._get_metadata(path)
- prev_meta.update(meta)
+ params = {'update':None}
headers = {}
- for key, val in prev_meta.items():
- headers[key.capitalize()] = val
- self.post(path, headers=headers)
+ prefix = 'x-%s-meta-' % entity
+ for k,v in meta.items():
+ k = '%s%s' % (prefix, k)
+ k = '-'.join(elem.capitalize() for elem in k.split('-'))
+ headers[k] = v
+ self.post(path, headers=headers, params=params)
def _delete_metadata(self, path, entity, meta=[]):
"""
delete previously set metadata
"""
- prev_meta = self._get_metadata(path)
+ prefix = 'x-%s-meta-' % entity
+ prev_meta = self._get_metadata(path, prefix)
headers = {}
for key, val in prev_meta.items():
- if key.split('-')[-1] in meta:
+ if key in meta:
continue
- http_key = key.capitalize()
- headers[http_key] = val
+ key = '%s%s' % (prefix, key)
+ key = '-'.join(elem.capitalize() for elem in key.split('-'))
+ headers[key] = val
self.post(path, headers=headers)
# Storage Account Services
def delete_account_metadata(self, meta=[]):
self._delete_metadata('', 'account', meta)
+ def set_account_groups(self, **groups):
+ headers = {}
+ for key, val in groups.items():
+ headers['X-Account-Group-%s' % key.capitalize()] = val
+ self.post('', headers=headers)
+
# Storage Container Services
def _filter(self, l, d):
def _filter_trashed(self, l):
return self._filter(l, {'trash':'true'})
- def list_objects(self, container, detail=False, params=None, headers=None,
- include_trashed=False):
+ def list_objects(self, container, detail=False, headers=None,
+ include_trashed=False, **params):
l = self._list('/' + container, detail, params, headers)
if not include_trashed:
l = self._filter_trashed(l)
return l
- def create_container(self, container, headers=None):
+ def create_container(self, container, headers=None, **meta):
+ for k,v in meta.items():
+ headers['X-Container-Meta-%s' %k.strip().upper()] = v.strip()
status, header, data = self.put('/' + container, headers=headers)
if status == 202:
return False
path = '/%s' % (container)
self._delete_metadata(path, 'container', meta)
+ def set_container_policies(self, container, **policies):
+ path = '/%s' % (container)
+ headers = {}
+ print ''
+ for key, val in policies.items():
+ headers['X-Container-Policy-%s' % key.capitalize()] = val
+ self.post(path, headers=headers)
+
# Storage Object Services
def retrieve_object(self, container, object, detail=False, headers=None,
h = {'Content-Type':'application/directory'}
self.create_object(container, object, f=None, headers=h)
+ def _set_public(self, headers, public=False):
+ """
+ sets the public header
+ """
+ if public == None:
+ return
+ elif public:
+ headers['X-Object-Public'] = public
+ else:
+ headers['X-Object-Public'] = ''
+
def create_object(self, container, object, f=stdin, chunked=False,
- blocksize=1024, headers=None):
+ blocksize=1024, headers={}, use_hashes=False,
+ public=None, **meta):
"""
creates an object
if f is None then creates a zero length object
if f is stdin or chunked is set then performs chunked transfer
"""
path = '/%s/%s' % (container, object)
- if not chunked and f != stdin:
+ for k,v in meta.items():
+ headers['X-Object-Meta-%s' %k.strip().upper()] = v.strip()
+ self._set_public(headers, public)
+ headers = headers if headers else None
+ if not chunked:
+ format = 'json' if use_hashes else 'text'
data = f.read() if f else None
- return self.put(path, data, headers=headers)
+ if data:
+ if format == 'json':
+ data = eval(data)
+ data = json.dumps(data)
+ return self.put(path, data, headers=headers, format=format)
else:
return self._chunked_transfer(path, 'PUT', f, headers=headers,
blocksize=1024)
def update_object(self, container, object, f=stdin, chunked=False,
- blocksize=1024, headers=None):
- if not f:
- return
+ blocksize=1024, headers={}, offset=None, public=None,
+ **meta):
+ print locals()
path = '/%s/%s' % (container, object)
+ for k,v in meta.items():
+ headers['X-Object-Meta-%s' %k.strip().upper()] = v.strip()
+ if offset:
+ headers['Content-Range'] = 'bytes %s-/*' % offset
+ else:
+ headers['Content-Range'] = 'bytes */*'
+ self._set_public(headers, public)
+ headers = headers if headers else None
if not chunked and f != stdin:
- data = f.read()
+ data = f.read() if f else None
self.post(path, data, headers=headers)
else:
self._chunked_transfer(path, 'POST', f, headers=headers,
blocksize=1024)
def _change_obj_location(self, src_container, src_object, dst_container,
- dst_object, remove=False, headers=None):
+ dst_object, remove=False, public=None, headers={}):
path = '/%s/%s' % (dst_container, dst_object)
if not headers:
headers = {}
headers['X-Move-From'] = '/%s/%s' % (src_container, src_object)
else:
headers['X-Copy-From'] = '/%s/%s' % (src_container, src_object)
+ self._set_public(headers, public)
+ self.headers = headers if headers else None
headers['Content-Length'] = 0
self.put(path, headers=headers)
def copy_object(self, src_container, src_object, dst_container,
- dst_object, headers=None):
+ dst_object, public=False, headers=None):
self._change_obj_location(src_container, src_object,
dst_container, dst_object,
- headers=headers)
+ public, headers)
def move_object(self, src_container, src_object, dst_container,
dst_object, headers=None):
self._change_obj_location(src_container, src_object,
- dst_container, dst_object, True, headers)
+ dst_container, dst_object, True,
+ public, headers)
def delete_object(self, container, object):
self.delete('/%s/%s' % (container, object))
def restore_object(self, container, object):
"""
restores a trashed object
- actualy just resets all object metadata except trash
+ actualy removes trash object metadata info
"""
self.delete_object_metadata(container, object, ['trash'])
-
+
+ def publish_object(self, container, object):
+ """
+ sets a previously created object publicly accessible
+ """
+ path = '/%s/%s' % (container, object)
+ headers = {}
+ headers['Content-Range'] = 'bytes */*'
+ self._set_public(headers, public=True)
+ self.post(path, headers=headers)
+
+ def unpublish_object(self, container, object):
+ """
+ unpublish an object
+ """
+ path = '/%s/%s' % (container, object)
+ headers = {}
+ headers['Content-Range'] = 'bytes */*'
+ self._set_public(headers, public=False)
+ self.post(path, headers=headers)