from colors import bold
from sys import stdout, exit
import signal
+from time import localtime, strftime
from progress.bar import IncrementalBar
print_dict(acc, exclude='name', ident=18)
if not getattr(self.args, 'detail'):
print
+
+@command()
+class store_versions(_store_container_command):
+ """Get the version list of an object"""
+
+ def main(self, container___path):
+ super(store_versions, self).main(container___path)
+ try:
+ versions = self.client.get_object_versionlist(self.path)
+ except ClientError as err:
+ raise CLIError(err)
+
+ print('%s:%s versions'%(self.container,self.path))
+ for vitem in versions:
+ t = localtime(float(vitem[1]))
+ vid = bold(unicode(vitem[0]))
+ print('\t%s \t(%s)'%(vid, strftime('%d-%m-%Y %H:%M:%S', t)))
\ No newline at end of file
+++ /dev/null
-#!/usr/bin/env python
-
-# Copyright 2011-2012 GRNET S.A. All rights reserved.
-#
-# Redistribution and use in source and binary forms, with or
-# without modification, are permitted provided that the following
-# conditions are met:
-#
-# 1. Redistributions of source code must retain the above
-# copyright notice, this list of conditions and the following
-# disclaimer.
-#
-# 2. Redistributions in binary form must reproduce the above
-# copyright notice, this list of conditions and the following
-# disclaimer in the documentation and/or other materials
-# provided with the distribution.
-#
-# THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
-# OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
-# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
-# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR
-# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
-# USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
-# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
-# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
-# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
-# POSSIBILITY OF SUCH DAMAGE.
-#
-# The views and conclusions contained in the software and
-# documentation are those of the authors and should not be
-# interpreted as representing official policies, either expressed
-# or implied, of GRNET S.A.
-
-from kamaki.cli import command, CLIError
-from kamaki.cli.utils import print_list, dict2file, list2file
-from .pithos_cli import _store_container_command, _store_account_command
-from colors import bold
-
-from sys import stdout#argv, exit, stdin, stdout
-
-from kamaki.clients.pithos_sh_lib.client import Pithos_Client, Fault
-from kamaki.clients.pithos_sh_lib.transfer import download, cat#,upload
-
-class _pithos_sh_account_command(_store_account_command):
-
- def update_parser(self, parser):
- super(_pithos_sh_account_command, self).update_parser(parser)
-
- def main(self):
- super(_pithos_sh_account_command, self).main()
- self.client = Pithos_Client(self.base_url, self.token, self.account)
-
-class _pithos_sh_container_command(_store_container_command):
-
- def update_parser(self, parser):
- super(_pithos_sh_container_command, self).update_parser(parser)
-
- def main(self, container_with_path, path_is_optional=True):
- super(_pithos_sh_container_command, self).main(container_with_path, path_is_optional)
- self.client = Pithos_Client(self.base_url, self.token, self.account)
-
-def _build_args(arglist, attrs):
- args = {}
- for a in [a for a in attrs if getattr(arglist, a)]:
- args[a] = getattr(arglist, a)
- return args
-
-@command()
-class store_versions(_pithos_sh_container_command):
- """Get the version list of an object"""
-
- def main(self, container___path):
- super(store_versions, self).main(container___path)
- try:
- data = self.client.retrieve_object_versionlist(self.container, self.path)
- except Fault as err:
- raise CLIError(message=unicode(err), status=err.status)
- from time import localtime, strftime
- print('%s:%s version ids:'%(self.container,self.path))
- for vitem in data['versions']:
- t = localtime(float(vitem[1]))
- vid = bold(unicode(vitem[0]))
- print('\t%s \t(%s)'%(vid, strftime('%d-%m-%Y %H:%M:%S', t)))
-
-"""
-@command()
-class store_sharers(_pithos_sh_account_command):
- ""list accounts who share objects with current account""
-
- def update_parser(self, parser):
- super(store_sharers, self).update_parser(parser)
- parser.add_argument('-l', action='store_true', dest='detail', default=False,
- help='show detailed output')
- parser.add_argument('-n', action='store', dest='limit', default=10000,
- help='show limited output')
- parser.add_argument('--marker', action='store', dest='marker', default=None,
- help='show output greater then marker')
-
- def main(self):
- super(store_sharers, self).main()
- attrs = ['limit', 'marker']
- args = _build_args(self.args, attrs)
- args['format'] = 'json' if getattr(self.args, 'detail') else 'text'
-
- try:
- print_list(self.client.list_shared_by_others(**args))
- except Fault as err:
- raise CLIError(message=unicode(err), status=err.status, importance=err.status/100)
-"""
\ No newline at end of file
path = ''
success = kwargs.pop('success', (200, 204))
r = self.get(path, *args, success = success, **kwargs)
- return r.json
\ No newline at end of file
+ return r.json
+
+ def get_object_versionlist(self, path):
+ self.assert_container()
+ r = self.object_get(path, format='json', version='list')
+ return r.json['versions']
+++ /dev/null
-# Copyright 2011-2012 GRNET S.A. All rights reserved.
-#
-# Redistribution and use in source and binary forms, with or
-# without modification, are permitted provided that the following
-# conditions are met:
-#
-# 1. Redistributions of source code must retain the above
-# copyright notice, this list of conditions and the following
-# disclaimer.
-#
-# 2. Redistributions in binary form must reproduce the above
-# copyright notice, this list of conditions and the following
-# disclaimer in the documentation and/or other materials
-# provided with the distribution.
-#
-# THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
-# OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
-# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
-# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR
-# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
-# USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
-# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
-# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
-# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
-# POSSIBILITY OF SUCH DAMAGE.
-#
-# The views and conclusions contained in the software and
-# documentation are those of the authors and should not be
-# interpreted as representing official policies, either expressed
-# or implied, of GRNET S.A.
-
-from httplib import HTTPConnection, HTTPSConnection, HTTP
-from sys import stdin
-from xml.dom import minidom
-from StringIO import StringIO
-from urllib import quote, unquote
-from urlparse import urlparse
-
-import json
-import types
-import socket
-import urllib
-import datetime
-
-ERROR_CODES = {304:'Not Modified',
- 400:'Bad Request',
- 401:'Unauthorized',
- 403:'Forbidden',
- 404:'Not Found',
- 409:'Conflict',
- 411:'Length Required',
- 412:'Precondition Failed',
- 413:'Request Entity Too Large',
- 416:'Range Not Satisfiable',
- 422:'Unprocessable Entity',
- 500:'Internal Server Error',
- 501:'Not Implemented'}
-
-class Fault(Exception):
- def __init__(self, data='', status=None):
- if data == '' and status in ERROR_CODES.keys():
- data = ERROR_CODES[status]
- Exception.__init__(self, data)
- self.data = data
- self.status = status
-
-class Client(object):
- def __init__(self, url, token, account, verbose=False, debug=False):
- """`url` can also include a port, e.g '127.0.0.1:8000'."""
-
- self.url = url
- self.account = account
- self.verbose = verbose or debug
- self.debug = debug
- self.token = token
-
- def _req(self, method, path, body=None, headers={}, format='text', params={}):
- p = urlparse(self.url)
- if p.scheme == 'http':
- conn = HTTPConnection(p.netloc)
- elif p.scheme == 'https':
- conn = HTTPSConnection(p.netloc)
- else:
- raise Exception('Unknown URL scheme')
-
- full_path = _prepare_path(p.path + path, format, params)
-
- kwargs = {}
- kwargs['headers'] = _prepare_headers(headers)
- kwargs['headers']['X-Auth-Token'] = self.token
- if body:
- kwargs['body'] = body
- kwargs['headers'].setdefault('content-type', 'application/octet-stream')
- kwargs['headers'].setdefault('content-length', len(body) if body else 0)
-
- #print '#', method, full_path, kwargs
- #t1 = datetime.datetime.utcnow()
- conn.request(method, full_path, **kwargs)
-
- resp = conn.getresponse()
- #t2 = datetime.datetime.utcnow()
- #print 'response time:', str(t2-t1)
- return _handle_response(resp, self.verbose, self.debug)
-
- def _chunked_transfer(self, path, method='PUT', f=stdin, headers=None,
- blocksize=1024, params={}):
- """perfomrs a chunked request"""
- p = urlparse(self.url)
- if p.scheme == 'http':
- conn = HTTPConnection(p.netloc)
- elif p.scheme == 'https':
- conn = HTTPSConnection(p.netloc)
- else:
- raise Exception('Unknown URL scheme')
-
- full_path = _prepare_path(p.path + path, params=params)
-
- headers.setdefault('content-type', 'application/octet-stream')
-
- conn.putrequest(method, full_path)
- conn.putheader('x-auth-token', self.token)
- conn.putheader('transfer-encoding', 'chunked')
- for k,v in _prepare_headers(headers).items():
- conn.putheader(k, v)
- conn.endheaders()
-
- # write body
- data = ''
- while True:
- if f.closed:
- break
- block = f.read(blocksize)
- if block == '':
- break
- data = '%x\r\n%s\r\n' % (len(block), block)
- try:
- conn.send(data)
- except:
- #retry
- conn.send(data)
- data = '0\r\n\r\n'
- try:
- conn.send(data)
- except:
- #retry
- conn.send(data)
-
- resp = conn.getresponse()
- return _handle_response(resp, self.verbose, self.debug)
-
- def delete(self, path, format='text', params={}):
- return self._req('DELETE', path, format=format, params=params)
-
- def get(self, path, format='text', headers={}, params={}):
- return self._req('GET', path, headers=headers, format=format,
- params=params)
-
- def head(self, path, format='text', params={}):
- return self._req('HEAD', path, format=format, params=params)
-
- def post(self, path, body=None, format='text', headers=None, params={}):
- return self._req('POST', path, body, headers=headers, format=format,
- params=params)
-
- def put(self, path, body=None, format='text', headers=None, params={}):
- return self._req('PUT', path, body, headers=headers, format=format,
- params=params)
-
- def _list(self, path, format='text', params={}, **headers):
- status, headers, data = self.get(path, format=format, headers=headers,
- params=params)
- if format == 'json':
- data = json.loads(data) if data else ''
- elif format == 'xml':
- data = minidom.parseString(data)
- else:
- data = data.split('\n')[:-1] if data else ''
- return data
-
- def _get_metadata(self, path, prefix=None, params={}):
- status, headers, data = self.head(path, params=params)
- prefixlen = len(prefix) if prefix else 0
- meta = {}
- for key, val in headers.items():
- if prefix and not key.startswith(prefix):
- continue
- elif prefix and key.startswith(prefix):
- key = key[prefixlen:]
- meta[key] = val
- return meta
-
- def _filter(self, l, d):
- """
- filter out from l elements having the metadata values provided
- """
- ll = l
- for elem in l:
- if type(elem) == types.DictionaryType:
- for key in d.keys():
- k = 'x_object_meta_%s' % key
- if k in elem.keys() and elem[k] == d[key]:
- ll.remove(elem)
- break
- return ll
-
-class OOS_Client(Client):
- """Openstack Object Storage Client"""
-
- def _update_metadata(self, path, entity, **meta):
- """adds new and updates the values of previously set metadata"""
- ex_meta = self.retrieve_account_metadata(restricted=True)
- ex_meta.update(meta)
- headers = {}
- prefix = 'x-%s-meta-' % entity
- for k,v in ex_meta.items():
- k = '%s%s' % (prefix, k)
- headers[k] = v
- return self.post(path, headers=headers)
-
- def _reset_metadata(self, path, entity, **meta):
- """
- overwrites all user defined metadata
- """
- headers = {}
- prefix = 'x-%s-meta-' % entity
- for k,v in meta.items():
- k = '%s%s' % (prefix, k)
- headers[k] = v
- return self.post(path, headers=headers)
-
- def _delete_metadata(self, path, entity, meta=[]):
- """delete previously set metadata"""
- ex_meta = self.retrieve_account_metadata(restricted=True)
- headers = {}
- prefix = 'x-%s-meta-' % entity
- for k in ex_meta.keys():
- if k in meta:
- headers['%s%s' % (prefix, k)] = ex_meta[k]
- return self.post(path, headers=headers)
-
- # Storage Account Services
-
- def list_containers(self, format='text', limit=None,
- marker=None, params={}, account=None, **headers):
- """lists containers"""
- account = account or self.account
- path = '/%s' % account
- params.update({'limit':limit, 'marker':marker})
- return self._list(path, format, params, **headers)
-
- def retrieve_account_metadata(self, restricted=False, account=None, **params):
- """returns the account metadata"""
- account = account or self.account
- path = '/%s' % account
- prefix = 'x-account-meta-' if restricted else None
- return self._get_metadata(path, prefix, params)
-
- def update_account_metadata(self, account=None, **meta):
- """updates the account metadata"""
- account = account or self.account
- path = '/%s' % account
- return self._update_metadata(path, 'account', **meta)
-
- def delete_account_metadata(self, meta=[], account=None):
- """deletes the account metadata"""
- account = account or self.account
- path = '/%s' % account
- return self._delete_metadata(path, 'account', meta)
-
- def reset_account_metadata(self, account=None, **meta):
- """resets account metadata"""
- account = account or self.account
- path = '/%s' % account
- return self._reset_metadata(path, 'account', **meta)
-
- # Storage Container Services
-
- def _filter_trashed(self, l):
- return self._filter(l, {'trash':'true'})
-
- def list_objects(self, container, format='text',
- limit=None, marker=None, prefix=None, delimiter=None,
- path=None, include_trashed=False, params={}, account=None,
- **headers):
- """returns a list with the container objects"""
- account = account or self.account
- params.update({'limit':limit, 'marker':marker, 'prefix':prefix,
- 'delimiter':delimiter, 'path':path})
- l = self._list('/%s/%s' % (account, container), format, params,
- **headers)
- #TODO support filter trashed with xml also
- if format != 'xml' and not include_trashed:
- l = self._filter_trashed(l)
- return l
-
- def create_container(self, container, account=None, meta={}, **headers):
- """creates a container"""
- account = account or self.account
- if not headers:
- headers = {}
- for k,v in meta.items():
- headers['x-container-meta-%s' %k.strip().upper()] = v.strip()
- status, header, data = self.put('/%s/%s' % (account, container),
- headers=headers)
- if status == 202:
- return False
- elif status != 201:
- raise Fault(data, int(status))
- return True
-
- def delete_container(self, container, params={}, account=None):
- """deletes a container"""
- account = account or self.account
- return self.delete('/%s/%s' % (account, container), params=params)
-
- def retrieve_container_metadata(self, container, restricted=False,
- account=None, **params):
- """returns the container metadata"""
- account = account or self.account
- prefix = 'x-container-meta-' if restricted else None
- return self._get_metadata('/%s/%s' % (account, container), prefix,
- params)
-
- def update_container_metadata(self, container, account=None, **meta):
- """unpdates the container metadata"""
- account = account or self.account
- return self._update_metadata('/%s/%s' % (account, container),
- 'container', **meta)
-
- def delete_container_metadata(self, container, meta=[], account=None):
- """deletes the container metadata"""
- account = account or self.account
- path = '/%s/%s' % (account, container)
- return self._delete_metadata(path, 'container', meta)
-
- # Storage Object Services
-
- def request_object(self, container, object, format='text', params={},
- account=None, **headers):
- """returns tuple containing the status, headers and data response for an object request"""
- account = account or self.account
- path = '/%s/%s/%s' % (account, container, object)
- status, headers, data = self.get(path, format, headers, params)
- return status, headers, data
-
- def retrieve_object(self, container, object, format='text', params={},
- account=None, **headers):
- """returns an object's data"""
- account = account or self.account
- t = self.request_object(container, object, format, params, account,
- **headers)
- data = t[2]
- if format == 'json':
- data = json.loads(data) if data else ''
- elif format == 'xml':
- data = minidom.parseString(data)
- return data
-
- def retrieve_object_hashmap(self, container, object, format='json', params={},
- account=None, **headers):
- """returns the hashmap representing object's data"""
- if not params:
- params = {}
- params.update({'hashmap':None})
- return self.retrieve_object(container, object, params, format, account, **headers)
-
- def create_directory_marker(self, container, object, account=None):
- """creates a dierectory marker"""
- account = account or self.account
- if not object:
- raise Fault('Directory markers have to be nested in a container')
- h = {'content_type':'application/directory'}
- return self.create_zero_length_object(container, object, account=account,
- **h)
-
- def create_object(self, container, object, f=stdin, format='text', meta={},
- params={}, etag=None, content_type=None, content_encoding=None,
- content_disposition=None, account=None, **headers):
- """creates a zero-length object"""
- account = account or self.account
- path = '/%s/%s/%s' % (account, container, object)
- for k, v in headers.items():
- if v == None:
- headers.pop(k)
-
- l = ['etag', 'content_encoding', 'content_disposition', 'content_type']
- l = [elem for elem in l if eval(elem)]
- for elem in l:
- headers.update({elem:eval(elem)})
- headers.setdefault('content-type', 'application/octet-stream')
-
- for k,v in meta.items():
- headers['x-object-meta-%s' %k.strip()] = v.strip()
- data = f.read() if f else None
- return self.put(path, data, format, headers=headers, params=params)
-
- def create_zero_length_object(self, container, object, meta={}, etag=None,
- content_type=None, content_encoding=None,
- content_disposition=None, account=None,
- **headers):
- account = account or self.account
- args = locals().copy()
- for elem in ['self', 'container', 'headers', 'account']:
- args.pop(elem)
- args.update(headers)
- return self.create_object(container, account=account, f=None, **args)
-
- def update_object(self, container, object, f=stdin,
- offset=None, meta={}, params={}, content_length=None,
- content_type=None, content_encoding=None,
- content_disposition=None, account=None, **headers):
- account = account or self.account
- path = '/%s/%s/%s' % (account, container, object)
- for k, v in headers.items():
- if v == None:
- headers.pop(k)
-
- l = ['content_encoding', 'content_disposition', 'content_type',
- 'content_length']
- l = [elem for elem in l if eval(elem)]
- for elem in l:
- headers.update({elem:eval(elem)})
-
- if 'content_range' not in headers.keys():
- if offset != None:
- headers['content_range'] = 'bytes %s-/*' % offset
- else:
- headers['content_range'] = 'bytes */*'
-
- for k,v in meta.items():
- headers['x-object-meta-%s' %k.strip()] = v.strip()
- data = f.read() if f else None
- return self.post(path, data, headers=headers, params=params)
-
- def update_object_using_chunks(self, container, object, f=stdin,
- blocksize=1024, offset=None, meta={},
- params={}, content_type=None, content_encoding=None,
- content_disposition=None, account=None, **headers):
- """updates an object (incremental upload)"""
- account = account or self.account
- path = '/%s/%s/%s' % (account, container, object)
- headers = headers if not headers else {}
- l = ['content_type', 'content_encoding', 'content_disposition']
- l = [elem for elem in l if eval(elem)]
- for elem in l:
- headers.update({elem:eval(elem)})
-
- if offset != None:
- headers['content_range'] = 'bytes %s-/*' % offset
- else:
- headers['content_range'] = 'bytes */*'
-
- for k,v in meta.items():
- v = v.strip()
- headers['x-object-meta-%s' %k.strip()] = v
- return self._chunked_transfer(path, 'POST', f, headers=headers,
- blocksize=blocksize, params=params)
-
- def _change_obj_location(self, src_container, src_object, dst_container,
- dst_object, remove=False, meta={}, account=None,
- content_type=None, delimiter=None, **headers):
- account = account or self.account
- path = '/%s/%s/%s' % (account, dst_container, dst_object)
- headers = {} if not headers else headers
- params = {}
- for k, v in meta.items():
- headers['x-object-meta-%s' % k] = v
- if remove:
- headers['x-move-from'] = '/%s/%s' % (src_container, src_object)
- else:
- headers['x-copy-from'] = '/%s/%s' % (src_container, src_object)
- headers['content_length'] = 0
- if content_type:
- headers['content_type'] = content_type
- else:
- params['ignore_content_type'] = ''
- if delimiter:
- params['delimiter'] = delimiter
- return self.put(path, headers=headers, params=params)
-
- def copy_object(self, src_container, src_object, dst_container, dst_object,
- meta={}, account=None, content_type=None, delimiter=None, **headers):
- """copies an object"""
- account = account or self.account
- return self._change_obj_location(src_container, src_object,
- dst_container, dst_object, account=account,
- remove=False, meta=meta,
- content_type=content_type, delimiter=delimiter, **headers)
-
- def move_object(self, src_container, src_object, dst_container,
- dst_object, meta={}, account=None,
- content_type=None, **headers):
- """moves an object"""
- account = account or self.account
- return self._change_obj_location(src_container, src_object,
- dst_container, dst_object,
- account=account, remove=True,
- meta=meta, content_type=content_type,
- **headers)
-
- def delete_object(self, container, object, params={}, account=None):
- """deletes an object"""
- account = account or self.account
- return self.delete('/%s/%s/%s' % (account, container, object),
- params=params)
-
- def retrieve_object_metadata(self, container, object, restricted=False,
- version=None, account=None):
- """
- set restricted to True to get only user defined metadata
- """
- account = account or self.account
- path = '/%s/%s/%s' % (account, container, object)
- prefix = 'x-object-meta-' if restricted else None
- params = {'version':version} if version else {}
- return self._get_metadata(path, prefix, params=params)
-
- def update_object_metadata(self, container, object, account=None,
- **meta):
- """
- updates object's metadata
- """
- account = account or self.account
- path = '/%s/%s/%s' % (account, container, object)
- return self._update_metadata(path, 'object', **meta)
-
- def delete_object_metadata(self, container, object, meta=[], account=None):
- """
- deletes object's metadata
- """
- account = account or self.account
- path = '/%s/%s' % (account, container, object)
- return self._delete_metadata(path, 'object', meta)
-
-class Pithos_Client(OOS_Client):
- """Pithos Storage Client. Extends OOS_Client"""
-
- def _update_metadata(self, path, entity, **meta):
- """
- adds new and updates the values of previously set metadata
- """
- params = {'update':None}
- headers = {}
- prefix = 'x-%s-meta-' % entity
- for k,v in meta.items():
- k = '%s%s' % (prefix, k)
- headers[k] = v
- return self.post(path, headers=headers, params=params)
-
- def _delete_metadata(self, path, entity, meta=[]):
- """
- delete previously set metadata
- """
- params = {'update':None}
- headers = {}
- prefix = 'x-%s-meta-' % entity
- for m in meta:
- headers['%s%s' % (prefix, m)] = ''
- return self.post(path, headers=headers, params=params)
-
- # Storage Account Services
-
- def list_containers(self, format='text', if_modified_since=None,
- if_unmodified_since=None, limit=None, marker=None,
- shared=False, until=None, account=None, public=False):
- """returns a list with the account containers"""
- account = account or self.account
- params = {'until':until} if until else {}
- if shared:
- params['shared'] = None
- if public:
- params['public'] = None
- headers = {'if-modified-since':if_modified_since,
- 'if-unmodified-since':if_unmodified_since}
- return OOS_Client.list_containers(self, account=account, format=format,
- limit=limit, marker=marker,
- params=params, **headers)
-
- def retrieve_account_metadata(self, restricted=False, until=None,
- account=None):
- """returns the account metadata"""
- account = account or self.account
- params = {'until':until} if until else {}
- return OOS_Client.retrieve_account_metadata(self, account=account,
- restricted=restricted,
- **params)
-
- def set_account_groups(self, account=None, **groups):
- """create account groups"""
- account = account or self.account
- path = '/%s' % account
- headers = {}
- for k, v in groups.items():
- headers['x-account-group-%s' % k] = v
- params = {'update':None}
- return self.post(path, headers=headers, params=params)
-
- def retrieve_account_groups(self, account=None):
- """returns the account groups"""
- account = account or self.account
- meta = self.retrieve_account_metadata(account=account)
- prefix = 'x-account-group-'
- prefixlen = len(prefix)
- groups = {}
- for key, val in meta.items():
- if prefix and not key.startswith(prefix):
- continue
- elif prefix and key.startswith(prefix):
- key = key[prefixlen:]
- groups[key] = val
- return groups
-
- def unset_account_groups(self, groups=[], account=None):
- """delete account groups"""
- account = account or self.account
- path = '/%s' % account
- headers = {}
- for elem in groups:
- headers['x-account-group-%s' % elem] = ''
- params = {'update':None}
- return self.post(path, headers=headers, params=params)
-
- def reset_account_groups(self, account=None, **groups):
- """overrides account groups"""
- account = account or self.account
- path = '/%s' % account
- headers = {}
- for k, v in groups.items():
- v = v.strip()
- headers['x-account-group-%s' % k] = v
- meta = self.retrieve_account_metadata(restricted=True)
- prefix = 'x-account-meta-'
- for k,v in meta.items():
- k = '%s%s' % (prefix, k)
- headers[k] = v
- return self.post(path, headers=headers)
-
- # Storage Container Services
- def create_container(self, container, account=None, meta={}, policies={}):
- """creates a container"""
- args = {}
- for k, v in policies.items():
- args['X-Container-Policy-%s' % k.capitalize()] = v
- return OOS_Client.create_container(self, container, account, meta, **args)
-
- def list_objects(self, container, format='text',
- limit=None, marker=None, prefix=None, delimiter=None,
- path=None, shared=False, include_trashed=False, params={},
- if_modified_since=None, if_unmodified_since=None, meta='',
- until=None, account=None, public=False):
- """returns a list with the container objects"""
- account = account or self.account
- params = {'until':until, 'meta':meta}
- if shared:
- params['shared'] = None
- if public:
- params['public'] = None
- args = locals().copy()
- for elem in ['self', 'container', 'params', 'until', 'meta']:
- args.pop(elem)
- return OOS_Client.list_objects(self, container, params=params, **args)
-
- def retrieve_container_metadata(self, container, restricted=False,
- until=None, account=None):
- """returns container's metadata"""
- account = account or self.account
- params = {'until':until} if until else {}
- return OOS_Client.retrieve_container_metadata(self, container,
- account=account,
- restricted=restricted,
- **params)
-
- def set_container_policies(self, container, account=None,
- **policies):
- """sets containers policies"""
- account = account or self.account
- path = '/%s/%s' % (account, container)
- headers = {}
- for key, val in policies.items():
- headers['x-container-policy-%s' % key] = val
- return self.post(path, headers=headers)
-
- def update_container_data(self, container, f=stdin):
- """adds blocks of data to the container"""
- account = self.account
- path = '/%s/%s' % (account, container)
- params = {'update': None}
- headers = {'content_type': 'application/octet-stream'}
- data = f.read() if f else None
- headers['content_length'] = len(data)
- return self.post(path, data, headers=headers, params=params)
-
- def delete_container(self, container, until=None, account=None, delimiter=None):
- """deletes a container or the container history until the date provided"""
- account = account or self.account
- params = {'until':until} if until else {}
- if delimiter:
- params['delimiter'] = delimiter
- return OOS_Client.delete_container(self, container, account=account,
- params=params)
-
- # Storage Object Services
-
- def retrieve_object(self, container, object, params={}, format='text',
- range=None, if_range=None,
- if_match=None, if_none_match=None,
- if_modified_since=None, if_unmodified_since=None,
- account=None, **headers):
- """returns an object"""
- account = account or self.account
- headers={}
- l = ['range', 'if_range', 'if_match', 'if_none_match',
- 'if_modified_since', 'if_unmodified_since']
- l = [elem for elem in l if eval(elem)]
- for elem in l:
- headers.update({elem:eval(elem)})
- if format != 'text':
- params['hashmap'] = None
- return OOS_Client.retrieve_object(self, container, object,
- account=account, format=format,
- params=params, **headers)
-
- def retrieve_object_version(self, container, object, version,
- format='text', range=None, if_range=None,
- if_match=None, if_none_match=None,
- if_modified_since=None, if_unmodified_since=None,
- account=None):
- """returns a specific object version"""
- account = account or self.account
- args = locals().copy()
- l = ['self', 'container', 'object']
- for elem in l:
- args.pop(elem)
- params = {'version':version}
- return self.retrieve_object(container, object, params=params, **args)
-
- def retrieve_object_versionlist(self, container, object, range=None,
- if_range=None, if_match=None,
- if_none_match=None, if_modified_since=None,
- if_unmodified_since=None, account=None):
- """returns the object version list"""
- account = account or self.account
- args = locals().copy()
- l = ['self', 'container', 'object']
- for elem in l:
- args.pop(elem)
-
- return self.retrieve_object_version(container, object, version='list',
- format='json', **args)
-
- def create_zero_length_object(self, container, object,
- meta={}, etag=None, content_type=None,
- content_encoding=None,
- content_disposition=None,
- x_object_manifest=None, x_object_sharing=None,
- x_object_public=None, account=None):
- """createas a zero length object"""
- account = account or self.account
- args = locals().copy()
- for elem in ['self', 'container', 'object']:
- args.pop(elem)
- return OOS_Client.create_zero_length_object(self, container, object,
- **args)
-
- def create_folder(self, container, name,
- meta={}, etag=None,
- content_encoding=None,
- content_disposition=None,
- x_object_manifest=None, x_object_sharing=None,
- x_object_public=None, account=None):
- args = locals().copy()
- for elem in ['self', 'container', 'name']:
- args.pop(elem)
- args['content_type'] = 'application/directory'
- return self.create_zero_length_object(container, name, **args)
-
- def create_object(self, container, object, f=stdin, format='text',
- meta={}, params={}, etag=None, content_type=None,
- content_encoding=None, content_disposition=None,
- x_object_manifest=None, x_object_sharing=None,
- x_object_public=None, account=None):
- """creates an object"""
- account = account or self.account
- args = locals().copy()
- for elem in ['self', 'container', 'object']:
- args.pop(elem)
- if format != 'text':
- params.update({'hashmap':None})
- return OOS_Client.create_object(self, container, object, **args)
-
- def create_object_using_chunks(self, container, object,
- f=stdin, blocksize=1024, meta={}, etag=None,
- content_type=None, content_encoding=None,
- content_disposition=None,
- x_object_sharing=None, x_object_manifest=None,
- x_object_public=None, account=None):
- """creates an object (incremental upload)"""
- account = account or self.account
- path = '/%s/%s/%s' % (account, container, object)
- headers = {}
- l = ['etag', 'content_type', 'content_encoding', 'content_disposition',
- 'x_object_sharing', 'x_object_manifest', 'x_object_public']
- l = [elem for elem in l if eval(elem)]
- for elem in l:
- headers.update({elem:eval(elem)})
- headers.setdefault('content-type', 'application/octet-stream')
-
- for k,v in meta.items():
- v = v.strip()
- headers['x-object-meta-%s' %k.strip()] = v
-
- return self._chunked_transfer(path, 'PUT', f, headers=headers,
- blocksize=blocksize)
-
- def create_object_by_hashmap(self, container, object, hashmap={},
- meta={}, etag=None, content_encoding=None,
- content_disposition=None, content_type=None,
- x_object_sharing=None, x_object_manifest=None,
- x_object_public = None, account=None):
- """creates an object by uploading hashes representing data instead of data"""
- account = account or self.account
- args = locals().copy()
- for elem in ['self', 'container', 'object', 'hashmap']:
- args.pop(elem)
-
- try:
- data = json.dumps(hashmap)
- except SyntaxError:
- raise Fault('Invalid formatting')
- args['params'] = {'hashmap':None}
- args['format'] = 'json'
-
- return self.create_object(container, object, f=StringIO(data), **args)
-
- def create_manifestation(self, container, object, manifest, account=None):
- """creates a manifestation"""
- account = account or self.account
- headers={'x_object_manifest':manifest}
- return self.create_object(container, object, f=None, account=account,
- **headers)
-
- def update_object(self, container, object, f=stdin,
- offset=None, meta={}, replace=False, content_length=None,
- content_type=None, content_range=None,
- content_encoding=None, content_disposition=None,
- x_object_bytes=None, x_object_manifest=None,
- x_object_sharing=None, x_object_public=None,
- x_source_object=None, account=None):
- """updates an object"""
- account = account or self.account
- args = locals().copy()
- for elem in ['self', 'container', 'object', 'replace']:
- args.pop(elem)
- if not replace:
- args['params'] = {'update':None}
- return OOS_Client.update_object(self, container, object, **args)
-
- def update_object_using_chunks(self, container, object, f=stdin,
- blocksize=1024, offset=None, meta={},
- replace=False, content_type=None, content_encoding=None,
- content_disposition=None, x_object_bytes=None,
- x_object_manifest=None, x_object_sharing=None,
- x_object_public=None, account=None):
- """updates an object (incremental upload)"""
- account = account or self.account
- args = locals().copy()
- for elem in ['self', 'container', 'object', 'replace']:
- args.pop(elem)
- if not replace:
- args['params'] = {'update':None}
- return OOS_Client.update_object_using_chunks(self, container, object, **args)
-
- def update_from_other_source(self, container, object, source,
- offset=None, meta={}, content_range=None,
- content_encoding=None, content_disposition=None,
- x_object_bytes=None, x_object_manifest=None,
- x_object_sharing=None, x_object_public=None, account=None):
- """updates an object"""
- account = account or self.account
- args = locals().copy()
- for elem in ['self', 'container', 'object', 'source']:
- args.pop(elem)
-
- args['x_source_object'] = source
- return self.update_object(container, object, f=None, **args)
-
- def delete_object(self, container, object, until=None, account=None, delimiter=None):
- """deletes an object or the object history until the date provided"""
- account = account or self.account
- params = {'until':until} if until else {}
- if delimiter:
- params['delimiter'] = delimiter
- return OOS_Client.delete_object(self, container, object, params, account)
-
- def trash_object(self, container, object):
- """trashes an object"""
- account = account or self.account
- path = '/%s/%s' % (container, object)
- meta = {'trash':'true'}
- return self._update_metadata(path, 'object', **meta)
-
- def restore_object(self, container, object, account=None):
- """restores a trashed object"""
- account = account or self.account
- return self.delete_object_metadata(container, object, account, ['trash'])
-
- def publish_object(self, container, object, account=None):
- """sets a previously created object publicly accessible"""
- account = account or self.account
- path = '/%s/%s/%s' % (account, container, object)
- headers = {}
- headers['x_object_public'] = True
- params = {'update':None}
- return self.post(path, headers=headers, params=params)
-
- def unpublish_object(self, container, object, account=None):
- """unpublish an object"""
- account = account or self.account
- path = '/%s/%s/%s' % (account, container, object)
- headers = {}
- headers['x_object_public'] = False
- params = {'update':None}
- return self.post(path, headers=headers, params=params)
-
- def copy_object(self, src_container, src_object, dst_container, dst_object,
- meta={}, public=False, version=None, account=None,
- content_type=None, delimiter=None):
- """copies an object"""
- account = account or self.account
- headers = {}
- headers['x_object_public'] = public
- if version:
- headers['x_source_version'] = version
- return OOS_Client.copy_object(self, src_container, src_object,
- dst_container, dst_object, meta=meta,
- account=account, content_type=content_type,
- delimiter=delimiter,
- **headers)
-
- def move_object(self, src_container, src_object, dst_container,
- dst_object, meta={}, public=False,
- account=None, content_type=None, delimiter=None):
- """moves an object"""
- headers = {}
- headers['x_object_public'] = public
- return OOS_Client.move_object(self, src_container, src_object,
- dst_container, dst_object, meta=meta,
- account=account, content_type=content_type,
- delimiter=delimiter,
- **headers)
-
- def list_shared_by_others(self, limit=None, marker=None, format='text'):
- """lists other accounts that share objects to the user"""
- l = ['limit', 'marker']
- params = {}
- for elem in [elem for elem in l if eval(elem)]:
- params[elem] = eval(elem)
- return self._list('', format, params)
-
- def share_object(self, container, object, l, read=True):
- """gives access(read by default) to an object to a user/group list"""
- action = 'read' if read else 'write'
- sharing = '%s=%s' % (action, ','.join(l))
- self.update_object(container, object, f=None, x_object_sharing=sharing)
-
-def _prepare_path(path, format='text', params={}):
- full_path = '%s?format=%s' % (quote(path), format)
-
- for k,v in params.items():
- value = quote(str(v)) if v else ''
- full_path = '%s&%s=%s' %(full_path, quote(k), value)
- return full_path
-
-def _prepare_headers(headers):
- for k,v in headers.items():
- headers.pop(k)
- k = k.replace('_', '-')
- headers[quote(k)] = quote(v, safe='/=,:@ *"') if type(v) == types.StringType else v
- return headers
-
-def _handle_response(response, verbose=False, debug=False):
- headers = response.getheaders()
- headers = dict((unquote(h), unquote(v)) for h,v in headers)
-
- if verbose:
- print '%d %s' % (response.status, response.reason)
- for key, val in headers.items():
- print '%s: %s' % (key.capitalize(), val)
- print
-
- length = response.getheader('content-length', None)
- data = response.read(length)
- if debug:
- print data
- print
-
- if int(response.status) in ERROR_CODES.keys():
- raise Fault(data, int(response.status))
-
- #print '**', response.status, headers, data, '\n'
- return response.status, headers, data
+++ /dev/null
-# Copyright 2011-2012 GRNET S.A. All rights reserved.
-#
-# Redistribution and use in source and binary forms, with or
-# without modification, are permitted provided that the following
-# conditions are met:
-#
-# 1. Redistributions of source code must retain the above
-# copyright notice, this list of conditions and the following
-# disclaimer.
-#
-# 2. Redistributions in binary form must reproduce the above
-# copyright notice, this list of conditions and the following
-# disclaimer in the documentation and/or other materials
-# provided with the distribution.
-#
-# THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
-# OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
-# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
-# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR
-# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
-# USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
-# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
-# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
-# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
-# POSSIBILITY OF SUCH DAMAGE.
-#
-# The views and conclusions contained in the software and
-# documentation are those of the authors and should not be
-# interpreted as representing official policies, either expressed
-# or implied, of GRNET S.A.
-
-import hashlib
-import os
-
-from binascii import hexlify
-
-from progress.bar import IncrementalBar
-
-def file_read_iterator(fp, size=1024):
- while True:
- data = fp.read(size)
- if not data:
- break
- yield data
-
-class HashMap(list):
-
- def __init__(self, blocksize, blockhash):
- super(HashMap, self).__init__()
- self.blocksize = blocksize
- self.blockhash = blockhash
-
- def _hash_raw(self, v):
- h = hashlib.new(self.blockhash)
- h.update(v)
- return h.digest()
-
- def _hash_block(self, v):
- return self._hash_raw(v.rstrip('\x00'))
-
- def hash(self):
- if len(self) == 0:
- return self._hash_raw('')
- if len(self) == 1:
- return self.__getitem__(0)
-
- h = list(self)
- s = 2
- while s < len(h):
- s = s * 2
- h += [('\x00' * len(h[0]))] * (s - len(h))
- while len(h) > 1:
- h = [self._hash_raw(h[x] + h[x + 1]) for x in range(0, len(h), 2)]
- return h[0]
-
- def load(self, fp, with_progress_bar=True):
- self.size = 0
- file_size = os.fstat(fp.fileno()).st_size
- nblocks = 1 + (file_size - 1) // self.blocksize
- if with_progress_bar:
- bar = IncrementalBar('Computing', max=nblocks)
- bar.suffix = '%(percent).1f%% - %(eta)ds'
- for block in bar.iter(file_read_iterator(fp, self.blocksize)):
- self.append(self._hash_block(block))
- self.size += len(block)
- else:
- for block in file_read_iterator(fp, self.blocksize):
- self.append(self._hash_block(block))
- self.size += len(block)
-
- def get_hash(self, fp, start, size):
- fp.seek(start)
- block = fp.read(size)
- h = hashlib.new(self.blockhash)
- h.update(block.strip('\x00'))
- return h.digest()
- #r = self._hash_raw(block.strip('\x00'))
- #return r
-
-
-def merkle(path, blocksize=4194304, blockhash='sha256'):
- hashes = HashMap(blocksize, blockhash)
- hashes.load(open(path))
- return hexlify(hashes.hash())
+++ /dev/null
-#!/usr/bin/env python
-
-# Copyright 2011-2012 GRNET S.A. All rights reserved.
-#
-# Redistribution and use in source and binary forms, with or
-# without modification, are permitted provided that the following
-# conditions are met:
-#
-# 1. Redistributions of source code must retain the above
-# copyright notice, this list of conditions and the following
-# disclaimer.
-#
-# 2. Redistributions in binary form must reproduce the above
-# copyright notice, this list of conditions and the following
-# disclaimer in the documentation and/or other materials
-# provided with the distribution.
-#
-# THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
-# OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
-# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
-# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR
-# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
-# USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
-# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
-# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
-# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
-# POSSIBILITY OF SUCH DAMAGE.
-#
-# The views and conclusions contained in the software and
-# documentation are those of the authors and should not be
-# interpreted as representing official policies, either expressed
-# or implied, of GRNET S.A.
-
-from getpass import getuser
-from optparse import OptionParser
-from os import environ
-from sys import argv, exit, stdin, stdout
-from datetime import datetime
-
-from kamaki.pithos_sh.client import Pithos_Client, Fault
-from kamaki.pithos_sh.util import get_user, get_auth, get_url
-from kamaki.pithos_sh.transfer import upload, download
-
-import json
-import logging
-import types
-import re
-import time as _time
-import os
-
-_cli_commands = {}
-
-def cli_command(*args):
- def decorator(cls):
- cls.commands = args
- for name in args:
- _cli_commands[name] = cls
- return cls
- return decorator
-
-def class_for_cli_command(name):
- return _cli_commands[name]
-
-class Command(object):
- syntax = ''
-
- def __init__(self, name, argv):
- parser = OptionParser('%%prog %s [options] %s' % (name, self.syntax))
- parser.add_option('--url', dest='url', metavar='URL',
- default=get_url(), help='server URL (currently: %s)' % get_url())
- parser.add_option('--user', dest='user', metavar='USER',
- default=get_user(),
- help='account USER (currently: %s)' % get_user())
- parser.add_option('--token', dest='token', metavar='TOKEN',
- default=get_auth(),
- help='account TOKEN (currently: %s)' % get_auth())
- parser.add_option('-v', action='store_true', dest='verbose',
- default=False, help='verbose output')
- parser.add_option('-d', action='store_true', dest='debug',
- default=False, help='debug output')
- self.add_options(parser)
- options, args = parser.parse_args(argv)
-
- # Add options to self
- for opt in parser.option_list:
- key = opt.dest
- if key:
- val = getattr(options, key)
- setattr(self, key, val)
-
- self.client = Pithos_Client(self.url, self.token, self.user, self.verbose,
- self.debug)
-
- self.parser = parser
- self.args = args
-
- def _build_args(self, attrs):
- args = {}
- for a in [a for a in attrs if getattr(self, a)]:
- args[a] = getattr(self, a)
- return args
-
- def add_options(self, parser):
- pass
-
- def execute(self, *args):
- pass
-
-@cli_command('list', 'ls')
-class List(Command):
- syntax = '[<container>[/<object>]]'
- description = 'list containers or objects'
-
- def add_options(self, parser):
- parser.add_option('-l', action='store_true', dest='detail',
- default=False, help='show detailed output')
- parser.add_option('-n', action='store', type='int', dest='limit',
- default=10000, help='show limited output')
- parser.add_option('--marker', action='store', type='str',
- dest='marker', default=None,
- help='show output greater then marker')
- parser.add_option('--prefix', action='store', type='str',
- dest='prefix', default=None,
- help='show output starting with prefix')
- parser.add_option('--delimiter', action='store', type='str',
- dest='delimiter', default=None,
- help='show output up to the delimiter')
- parser.add_option('--path', action='store', type='str',
- dest='path', default=None,
- help='show output starting with prefix up to /')
- parser.add_option('--meta', action='store', type='str',
- dest='meta', default=None,
- help='show output having the specified meta keys')
- parser.add_option('--if-modified-since', action='store', type='str',
- dest='if_modified_since', default=None,
- help='show output if modified since then')
- parser.add_option('--if-unmodified-since', action='store', type='str',
- dest='if_unmodified_since', default=None,
- help='show output if not modified since then')
- parser.add_option('--until', action='store', dest='until',
- default=None, help='show metadata until that date')
- parser.add_option('--format', action='store', dest='format',
- default='%d/%m/%Y %H:%M:%S', help='format to parse until date (default: %d/%m/%Y %H:%M:%S)')
- parser.add_option('--shared', action='store_true', dest='shared',
- default=False, help='show only shared')
- parser.add_option('--public', action='store_true', dest='public',
- default=False, help='show only public')
-
-
- def execute(self, container=None):
- if container:
- self.list_objects(container)
- else:
- self.list_containers()
-
- def list_containers(self):
- attrs = ['limit', 'marker', 'if_modified_since',
- 'if_unmodified_since', 'shared', 'public']
- args = self._build_args(attrs)
- args['format'] = 'json' if self.detail else 'text'
-
- if getattr(self, 'until'):
- t = _time.strptime(self.until, self.format)
- args['until'] = int(_time.mktime(t))
-
- l = self.client.list_containers(**args)
- print_list(l)
-
- def list_objects(self, container):
- #prepate params
- params = {}
- attrs = ['limit', 'marker', 'prefix', 'delimiter', 'path',
- 'meta', 'if_modified_since', 'if_unmodified_since',
- 'shared', 'public']
- args = self._build_args(attrs)
- args['format'] = 'json' if self.detail else 'text'
-
- if self.until:
- t = _time.strptime(self.until, self.format)
- args['until'] = int(_time.mktime(t))
-
- container, sep, object = container.partition('/')
- if object:
- return
-
- detail = 'json'
- #if request with meta quering disable trash filtering
- show_trashed = True if self.meta else False
- l = self.client.list_objects(container, **args)
- print_list(l, detail=self.detail)
-
-@cli_command('meta')
-class Meta(Command):
- syntax = '[<container>[/<object>]]'
- description = 'get account/container/object metadata'
-
- def add_options(self, parser):
- parser.add_option('-r', action='store_true', dest='restricted',
- default=False, help='show only user defined metadata')
- parser.add_option('--until', action='store', dest='until',
- default=None, help='show metadata until that date')
- parser.add_option('--format', action='store', dest='format',
- default='%d/%m/%Y %H:%M:%S', help='format to parse until date (default: %d/%m/%Y %H:%M:%S)')
- parser.add_option('--version', action='store', dest='version',
- default=None, help='show specific version \
- (applies only for objects)')
-
- def execute(self, path=''):
- container, sep, object = path.partition('/')
- args = {'restricted': self.restricted}
- if getattr(self, 'until'):
- t = _time.strptime(self.until, self.format)
- args['until'] = int(_time.mktime(t))
-
- if object:
- meta = self.client.retrieve_object_metadata(container, object,
- self.restricted,
- self.version)
- elif container:
- meta = self.client.retrieve_container_metadata(container, **args)
- else:
- meta = self.client.retrieve_account_metadata(**args)
- if meta == None:
- print 'Entity does not exist'
- else:
- print_dict(meta, header=None)
-
-@cli_command('create')
-class CreateContainer(Command):
- syntax = '<container> [key=val] [...]'
- description = 'create a container'
-
- def add_options(self, parser):
- parser.add_option('--versioning', action='store', dest='versioning',
- default=None, help='set container versioning (auto/none)')
- parser.add_option('--quota', action='store', dest='quota',
- default=None, help='set default container quota')
-
- def execute(self, container, *args):
- meta = {}
- for arg in args:
- key, sep, val = arg.partition('=')
- meta[key] = val
- policy = {}
- if getattr(self, 'versioning'):
- policy['versioning'] = self.versioning
- if getattr(self, 'quota'):
- policy['quota'] = self.quota
- ret = self.client.create_container(container, meta=meta, policies=policy)
- if not ret:
- print 'Container already exists'
-
-@cli_command('delete', 'rm')
-class Delete(Command):
- syntax = '<container>[/<object>]'
- description = 'delete a container or an object'
-
- def add_options(self, parser):
- parser.add_option('--until', action='store', dest='until',
- default=None, help='remove history until that date')
- parser.add_option('--format', action='store', dest='format',
- default='%d/%m/%Y %H:%M:%S', help='format to parse until date (default: %d/%m/%Y %H:%M:%S)')
- parser.add_option('--delimiter', action='store', type='str',
- dest='delimiter', default=None,
- help='mass delete objects with path staring with <src object> + delimiter')
- parser.add_option('-r', action='store_true',
- dest='recursive', default=False,
- help='mass delimiter objects with delimiter /')
-
- def execute(self, path):
- container, sep, object = path.partition('/')
- until = None
- if getattr(self, 'until'):
- t = _time.strptime(self.until, self.format)
- until = int(_time.mktime(t))
-
- kwargs = {}
- if self.delimiter:
- kwargs['delimiter'] = self.delimiter
- elif self.recursive:
- kwargs['delimiter'] = '/'
-
- if object:
- self.client.delete_object(container, object, until, **kwargs)
- else:
- self.client.delete_container(container, until, **kwargs)
-
-@cli_command('get')
-class GetObject(Command):
- syntax = '<container>/<object>'
- description = 'get the data of an object'
-
- def add_options(self, parser):
- parser.add_option('-l', action='store_true', dest='detail',
- default=False, help='show detailed output')
- parser.add_option('--range', action='store', dest='range',
- default=None, help='show range of data')
- parser.add_option('--if-range', action='store', dest='if_range',
- default=None, help='show range of data')
- parser.add_option('--if-match', action='store', dest='if_match',
- default=None, help='show output if ETags match')
- parser.add_option('--if-none-match', action='store',
- dest='if_none_match', default=None,
- help='show output if ETags don\'t match')
- parser.add_option('--if-modified-since', action='store', type='str',
- dest='if_modified_since', default=None,
- help='show output if modified since then')
- parser.add_option('--if-unmodified-since', action='store', type='str',
- dest='if_unmodified_since', default=None,
- help='show output if not modified since then')
- parser.add_option('-o', action='store', type='str',
- dest='file', default=None,
- help='save output in file')
- parser.add_option('--version', action='store', type='str',
- dest='version', default=None,
- help='get the specific \
- version')
- parser.add_option('--versionlist', action='store_true',
- dest='versionlist', default=False,
- help='get the full object version list')
- parser.add_option('--hashmap', action='store_true',
- dest='hashmap', default=False,
- help='get the object hashmap instead')
-
- def execute(self, path):
- attrs = ['if_match', 'if_none_match', 'if_modified_since',
- 'if_unmodified_since', 'hashmap']
- args = self._build_args(attrs)
- args['format'] = 'json' if self.detail else 'text'
- if self.range:
- args['range'] = 'bytes=%s' % self.range
- if getattr(self, 'if_range'):
- args['if-range'] = 'If-Range:%s' % getattr(self, 'if_range')
-
- container, sep, object = path.partition('/')
- data = None
- if self.versionlist:
- if 'detail' in args.keys():
- args.pop('detail')
- args.pop('format')
- self.detail = True
- data = self.client.retrieve_object_versionlist(container, object, **args)
- elif self.version:
- data = self.client.retrieve_object_version(container, object,
- self.version, **args)
- elif self.hashmap:
- if 'detail' in args.keys():
- args.pop('detail')
- args.pop('format')
- self.detail = True
- data = self.client.retrieve_object_hashmap(container, object, **args)
- else:
- data = self.client.retrieve_object(container, object, **args)
-
- f = open(self.file, 'w') if self.file else stdout
- if self.detail or type(data) == types.DictionaryType:
- if self.versionlist:
- print_versions(data, f=f)
- else:
- print_dict(data, f=f)
- else:
- f.write(data)
- f.close()
-
-@cli_command('mkdir')
-class PutMarker(Command):
- syntax = '<container>/<directory marker>'
- description = 'create a directory marker'
-
- def execute(self, path):
- container, sep, object = path.partition('/')
- self.client.create_directory_marker(container, object)
-
-@cli_command('put')
-class PutObject(Command):
- syntax = '<container>/<object> [key=val] [...]'
- description = 'create/override object'
-
- def add_options(self, parser):
- parser.add_option('--use_hashes', action='store_true', dest='use_hashes',
- default=False, help='provide hashmap instead of data')
- parser.add_option('--chunked', action='store_true', dest='chunked',
- default=False, help='set chunked transfer mode')
- parser.add_option('--etag', action='store', dest='etag',
- default=None, help='check written data')
- parser.add_option('--content-encoding', action='store',
- dest='content_encoding', default=None,
- help='provide the object MIME content type')
- parser.add_option('--content-disposition', action='store', type='str',
- dest='content_disposition', default=None,
- help='provide the presentation style of the object')
- #parser.add_option('-S', action='store',
- # dest='segment_size', default=False,
- # help='use for large file support')
- parser.add_option('--manifest', action='store',
- dest='x_object_manifest', default=None,
- help='provide object parts prefix in <container>/<object> form')
- parser.add_option('--content-type', action='store',
- dest='content_type', default=None,
- help='create object with specific content type')
- parser.add_option('--sharing', action='store',
- dest='x_object_sharing', default=None,
- help='define sharing object policy')
- parser.add_option('-f', action='store',
- dest='srcpath', default=None,
- help='file descriptor to read from (pass - for standard input)')
- parser.add_option('--public', action='store_true',
- dest='x_object_public', default=False,
- help='make object publicly accessible')
-
- def execute(self, path, *args):
- if path.find('=') != -1:
- raise Fault('Missing path argument')
-
- #prepare user defined meta
- meta = {}
- for arg in args:
- key, sep, val = arg.partition('=')
- meta[key] = val
-
- attrs = ['etag', 'content_encoding', 'content_disposition',
- 'content_type', 'x_object_sharing', 'x_object_public']
- args = self._build_args(attrs)
-
- container, sep, object = path.partition('/')
-
- f = None
- if self.srcpath:
- f = open(self.srcpath) if self.srcpath != '-' else stdin
-
- if self.use_hashes and not f:
- raise Fault('Illegal option combination')
-
- if self.chunked:
- self.client.create_object_using_chunks(container, object, f,
- meta=meta, **args)
- elif self.use_hashes:
- data = f.read()
- hashmap = json.loads(data)
- self.client.create_object_by_hashmap(container, object, hashmap,
- meta=meta, **args)
- elif self.x_object_manifest:
- self.client.create_manifestation(container, object, self.x_object_manifest)
- elif not f:
- self.client.create_zero_length_object(container, object, meta=meta, **args)
- else:
- self.client.create_object(container, object, f, meta=meta, **args)
- if f:
- f.close()
-
-@cli_command('copy', 'cp')
-class CopyObject(Command):
- syntax = '<src container>/<src object> [<dst container>/]<dst object> [key=val] [...]'
- description = 'copy an object to a different location'
-
- def add_options(self, parser):
- parser.add_option('--version', action='store',
- dest='version', default=False,
- help='copy specific version')
- parser.add_option('--public', action='store_true',
- dest='public', default=False,
- help='make object publicly accessible')
- parser.add_option('--content-type', action='store',
- dest='content_type', default=None,
- help='change object\'s content type')
- parser.add_option('--delimiter', action='store', type='str',
- dest='delimiter', default=None,
- help='mass copy objects with path staring with <src object> + delimiter')
- parser.add_option('-r', action='store_true',
- dest='recursive', default=False,
- help='mass copy with delimiter /')
-
- def execute(self, src, dst, *args):
- src_container, sep, src_object = src.partition('/')
- dst_container, sep, dst_object = dst.partition('/')
-
- #prepare user defined meta
- meta = {}
- for arg in args:
- key, sep, val = arg.partition('=')
- meta[key] = val
-
- if not sep:
- dst_container = src_container
- dst_object = dst
-
- args = {'content_type':self.content_type} if self.content_type else {}
- if self.delimiter:
- args['delimiter'] = self.delimiter
- elif self.recursive:
- args['delimiter'] = '/'
- self.client.copy_object(src_container, src_object, dst_container,
- dst_object, meta, self.public, self.version,
- **args)
-
-@cli_command('set')
-class SetMeta(Command):
- syntax = '[<container>[/<object>]] key=val [key=val] [...]'
- description = 'set account/container/object metadata'
-
- def execute(self, path, *args):
- #in case of account fix the args
- if path.find('=') != -1:
- args = list(args)
- args.append(path)
- args = tuple(args)
- path = ''
- meta = {}
- for arg in args:
- key, sep, val = arg.partition('=')
- meta[key.strip()] = val.strip()
- container, sep, object = path.partition('/')
- if object:
- self.client.update_object_metadata(container, object, **meta)
- elif container:
- self.client.update_container_metadata(container, **meta)
- else:
- self.client.update_account_metadata(**meta)
-
-@cli_command('update')
-class UpdateObject(Command):
- syntax = '<container>/<object> path [key=val] [...]'
- description = 'update object metadata/data (default mode: append)'
-
- def add_options(self, parser):
- parser.add_option('-a', action='store_true', dest='append',
- default=True, help='append data')
- parser.add_option('--offset', action='store',
- dest='offset',
- default=None, help='starting offest to be updated')
- parser.add_option('--range', action='store', dest='content_range',
- default=None, help='range of data to be updated')
- parser.add_option('--chunked', action='store_true', dest='chunked',
- default=False, help='set chunked transfer mode')
- parser.add_option('--content-encoding', action='store',
- dest='content_encoding', default=None,
- help='provide the object MIME content type')
- parser.add_option('--content-disposition', action='store', type='str',
- dest='content_disposition', default=None,
- help='provide the presentation style of the object')
- parser.add_option('--manifest', action='store', type='str',
- dest='x_object_manifest', default=None,
- help='use for large file support')
- parser.add_option('--sharing', action='store',
- dest='x_object_sharing', default=None,
- help='define sharing object policy')
- parser.add_option('--nosharing', action='store_true',
- dest='no_sharing', default=None,
- help='clear object sharing policy')
- parser.add_option('-f', action='store',
- dest='srcpath', default=None,
- help='file descriptor to read from: pass - for standard input')
- parser.add_option('--public', action='store_true',
- dest='x_object_public', default=False,
- help='make object publicly accessible')
- parser.add_option('--replace', action='store_true',
- dest='replace', default=False,
- help='override metadata')
-
- def execute(self, path, *args):
- if path.find('=') != -1:
- raise Fault('Missing path argument')
-
- #prepare user defined meta
- meta = {}
- for arg in args:
- key, sep, val = arg.partition('=')
- meta[key] = val
-
-
- attrs = ['content_encoding', 'content_disposition', 'x_object_sharing',
- 'x_object_public', 'x_object_manifest', 'replace', 'offset',
- 'content_range']
- args = self._build_args(attrs)
-
- if self.no_sharing:
- args['x_object_sharing'] = ''
-
- container, sep, object = path.partition('/')
-
- f = None
- if self.srcpath:
- f = open(self.srcpath) if self.srcpath != '-' else stdin
-
- if self.chunked:
- self.client.update_object_using_chunks(container, object, f,
- meta=meta, **args)
- else:
- self.client.update_object(container, object, f, meta=meta, **args)
- if f:
- f.close()
-
-@cli_command('move', 'mv')
-class MoveObject(Command):
- syntax = '<src container>/<src object> [<dst container>/]<dst object>'
- description = 'move an object to a different location'
-
- def add_options(self, parser):
- parser.add_option('--public', action='store_true',
- dest='public', default=False,
- help='make object publicly accessible')
- parser.add_option('--content-type', action='store',
- dest='content_type', default=None,
- help='change object\'s content type')
- parser.add_option('--delimiter', action='store', type='str',
- dest='delimiter', default=None,
- help='mass move objects with path staring with <src object> + delimiter')
- parser.add_option('-r', action='store_true',
- dest='recursive', default=False,
- help='mass move objects with delimiter /')
-
- def execute(self, src, dst, *args):
- src_container, sep, src_object = src.partition('/')
- dst_container, sep, dst_object = dst.partition('/')
- if not sep:
- dst_container = src_container
- dst_object = dst
-
- #prepare user defined meta
- meta = {}
- for arg in args:
- key, sep, val = arg.partition('=')
- meta[key] = val
-
- args = {'content_type':self.content_type} if self.content_type else {}
- if self.delimiter:
- args['delimiter'] = self.delimiter
- elif self.recursive:
- args['delimiter'] = '/'
- self.client.move_object(src_container, src_object, dst_container,
- dst_object, meta, self.public, **args)
-
-@cli_command('unset')
-class UnsetObject(Command):
- syntax = '<container>/[<object>] key [key] [...]'
- description = 'delete metadata info'
-
- def execute(self, path, *args):
- #in case of account fix the args
- if len(args) == 0:
- args = list(args)
- args.append(path)
- args = tuple(args)
- path = ''
- meta = []
- for key in args:
- meta.append(key)
- container, sep, object = path.partition('/')
- if object:
- self.client.delete_object_metadata(container, object, meta)
- elif container:
- self.client.delete_container_metadata(container, meta)
- else:
- self.client.delete_account_metadata(meta)
-
-@cli_command('group')
-class CreateGroup(Command):
- syntax = 'key=val [key=val] [...]'
- description = 'create account groups'
-
- def execute(self, *args):
- groups = {}
- for arg in args:
- key, sep, val = arg.partition('=')
- groups[key] = val
- self.client.set_account_groups(**groups)
-
-@cli_command('ungroup')
-class DeleteGroup(Command):
- syntax = 'key [key] [...]'
- description = 'delete account groups'
-
- def execute(self, *args):
- groups = []
- for arg in args:
- groups.append(arg)
- self.client.unset_account_groups(groups)
-
-@cli_command('policy')
-class SetPolicy(Command):
- syntax = 'container key=val [key=val] [...]'
- description = 'set container policies'
-
- def execute(self, path, *args):
- if path.find('=') != -1:
- raise Fault('Missing container argument')
-
- container, sep, object = path.partition('/')
-
- if object:
- raise Fault('Only containers have policies')
-
- policies = {}
- for arg in args:
- key, sep, val = arg.partition('=')
- policies[key] = val
-
- self.client.set_container_policies(container, **policies)
-
-@cli_command('publish')
-class PublishObject(Command):
- syntax = '<container>/<object>'
- description = 'publish an object'
-
- def execute(self, src):
- src_container, sep, src_object = src.partition('/')
-
- self.client.publish_object(src_container, src_object)
-
-@cli_command('unpublish')
-class UnpublishObject(Command):
- syntax = '<container>/<object>'
- description = 'unpublish an object'
-
- def execute(self, src):
- src_container, sep, src_object = src.partition('/')
-
- self.client.unpublish_object(src_container, src_object)
-
-@cli_command('sharing')
-class SharingObject(Command):
- syntax = 'list users sharing objects with the user'
- description = 'list user accounts sharing objects with the user'
-
- def add_options(self, parser):
- parser.add_option('-l', action='store_true', dest='detail',
- default=False, help='show detailed output')
- parser.add_option('-n', action='store', type='int', dest='limit',
- default=10000, help='show limited output')
- parser.add_option('--marker', action='store', type='str',
- dest='marker', default=None,
- help='show output greater then marker')
-
-
- def execute(self):
- attrs = ['limit', 'marker']
- args = self._build_args(attrs)
- args['format'] = 'json' if self.detail else 'text'
-
- print_list(self.client.list_shared_by_others(**args))
-
-@cli_command('send')
-class Send(Command):
- syntax = '<file> <container>[/<prefix>]'
- description = 'upload file to container (using prefix)'
-
- def execute(self, file, path):
- container, sep, prefix = path.partition('/')
- upload(self.client, file, container, prefix)
-
-@cli_command('receive')
-class Receive(Command):
- syntax = '<container>/<object> <file>'
- description = 'download object to file'
-
- def execute(self, path, file):
- container, sep, object = path.partition('/')
- download(self.client, container, object, file)
-
-def print_usage():
- cmd = Command('', [])
- parser = cmd.parser
- parser.usage = '%prog <command> [options]'
- parser.print_help()
-
- commands = []
- for cls in set(_cli_commands.values()):
- name = ', '.join(cls.commands)
- description = getattr(cls, 'description', '')
- commands.append(' %s %s' % (name.ljust(12), description))
- print '\nCommands:\n' + '\n'.join(sorted(commands))
-
-def print_dict(d, header='name', f=stdout, detail=True):
- header = header if header in d else 'subdir'
- if header and header in d:
- f.write('%s\n' %d.pop(header).encode('utf8'))
- if detail:
- patterns = ['^x_(account|container|object)_meta_(\w+)$']
- patterns.append(patterns[0].replace('_', '-'))
- for key, val in sorted(d.items()):
- f.write('%s: %s\n' % (key.rjust(30), val))
-
-def print_list(l, verbose=False, f=stdout, detail=True):
- for elem in l:
- #if it's empty string continue
- if not elem:
- continue
- if type(elem) == types.DictionaryType:
- print_dict(elem, f=f, detail=detail)
- elif type(elem) == types.StringType:
- if not verbose:
- elem = elem.split('Traceback')[0]
- f.write('%s\n' % elem)
- else:
- f.write('%s\n' % elem)
-
-def print_versions(data, f=stdout):
- if 'versions' not in data:
- f.write('%s\n' %data)
- return
- f.write('versions:\n')
- for id, t in data['versions']:
- f.write('%s @ %s\n' % (str(id).rjust(30), datetime.fromtimestamp(float(t))))
-
-
-def main():
- try:
- name = argv[1]
- cls = class_for_cli_command(name)
- except (IndexError, KeyError):
- print_usage()
- exit(1)
-
- cmd = cls(name, argv[2:])
-
- try:
- cmd.execute(*cmd.args)
- except TypeError, e:
- cmd.parser.print_help()
- exit(1)
- except Fault, f:
- status = '%s ' % f.status if f.status else ''
- print '%s%s' % (status, f.data)
-
-
-if __name__ == '__main__':
- main()
+++ /dev/null
-# Copyright 2011-2012 GRNET S.A. All rights reserved.
-#
-# Redistribution and use in source and binary forms, with or
-# without modification, are permitted provided that the following
-# conditions are met:
-#
-# 1. Redistributions of source code must retain the above
-# copyright notice, this list of conditions and the following
-# disclaimer.
-#
-# 2. Redistributions in binary form must reproduce the above
-# copyright notice, this list of conditions and the following
-# disclaimer in the documentation and/or other materials
-# provided with the distribution.
-#
-# THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
-# OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
-# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
-# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR
-# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
-# USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
-# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
-# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
-# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
-# POSSIBILITY OF SUCH DAMAGE.
-#
-# The views and conclusions contained in the software and
-# documentation are those of the authors and should not be
-# interpreted as representing official policies, either expressed
-# or implied, of GRNET S.A.
-
-import os
-import types
-import json
-
-from hashmap import HashMap
-from binascii import hexlify, unhexlify
-from cStringIO import StringIO
-from client import Fault
-
-from progress.bar import IncrementalBar
-
-def upload(client, path, container, prefix, name=None, mimetype=None):
-
- meta = client.retrieve_container_metadata(container)
- blocksize = int(meta['x-container-block-size'])
- blockhash = meta['x-container-block-hash']
-
- size = os.path.getsize(path)
- hashes = HashMap(blocksize, blockhash)
- hashes.load(open(path))
- map = {'bytes': size, 'hashes': [hexlify(x) for x in hashes]}
-
- objectname = name if name else os.path.split(path)[-1]
- object = prefix + objectname
- kwargs = {'mimetype':mimetype} if mimetype else {}
- v = None
- try:
- v = client.create_object_by_hashmap(container, object, map, **kwargs)
- except Fault, fault:
- if fault.status != 409:
- raise
- else:
- return v
-
- if type(fault.data) == types.StringType:
- missing = json.loads(fault.data)
- elif type(fault.data) == types.ListType:
- missing = fault.data
-
- if '' in missing:
- del missing[missing.index(''):]
-
- bar = IncrementalBar('Uploading', max=len(missing))
- bar.suffix = '%(percent).1f%% - %(eta)ds'
- with open(path) as fp:
- for hash in missing:
- offset = hashes.index(unhexlify(hash)) * blocksize
- fp.seek(offset)
- block = fp.read(blocksize)
- client.update_container_data(container, StringIO(block))
- bar.next()
- bar.finish()
-
- return client.create_object_by_hashmap(container, object, map, **kwargs)
-
-def download(client, container, object, path):
-
- res = client.retrieve_object_hashmap(container, object)
- blocksize = int(res['block_size'])
- blockhash = res['block_hash']
- bytes = res['bytes']
- map = res['hashes']
-
- if os.path.exists(path):
- h = HashMap(blocksize, blockhash)
- h.load(open(path))
- hashes = [hexlify(x) for x in h]
- else:
- open(path, 'w').close() # Create an empty file
- hashes = []
-
- with open(path, 'a+') as fp:
- if bytes != 0:
- for i, h in enumerate(map):
- if i < len(hashes) and h == hashes[i]:
- continue
- start = i * blocksize
- end = '' if i == len(map) - 1 else ((i + 1) * blocksize) - 1
- data = client.retrieve_object(container, object, range='bytes=%s-%s' % (start, end))
- if i != len(map) - 1:
- data += (blocksize - len(data)) * '\x00'
- fp.seek(start)
- fp.write(data)
- fp.truncate(bytes)
-
-def cat(client, container, object):
- res = client.retrieve_object_hashmap(container, object)
- blocksize = int(res['block_size'])
- blockhash = res['block_hash']
- bytes = res['bytes']
- map = res['hashes']
-
- hashes = []
- for i, h in enumerate(map):
- if i < len(hashes) and h == hashes[i]:
- continue
- start = i * blocksize
- end = '' if i == len(map) - 1 else ((i + 1) * blocksize) - 1
- data = client.retrieve_object(container, object, range='bytes=%s-%s' % (start, end))
- if i != len(map) - 1:
- data += (blocksize - len(data)) * '\x00'
- print(unicode(data))
+++ /dev/null
-# Copyright 2011-2012 GRNET S.A. All rights reserved.
-#
-# Redistribution and use in source and binary forms, with or
-# without modification, are permitted provided that the following
-# conditions are met:
-#
-# 1. Redistributions of source code must retain the above
-# copyright notice, this list of conditions and the following
-# disclaimer.
-#
-# 2. Redistributions in binary form must reproduce the above
-# copyright notice, this list of conditions and the following
-# disclaimer in the documentation and/or other materials
-# provided with the distribution.
-#
-# THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
-# OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
-# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
-# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR
-# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
-# USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
-# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
-# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
-# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
-# POSSIBILITY OF SUCH DAMAGE.
-#
-# The views and conclusions contained in the software and
-# documentation are those of the authors and should not be
-# interpreted as representing official policies, either expressed
-# or implied, of GRNET S.A.
-
-import os
-
-DEFAULT_URL = 'https://pithos.okeanos.grnet.gr/v1'
-DEFAULT_USER = 'test'
-DEFAULT_TOKEN = '0000'
-
-def get_user():
- try:
- return os.environ['PITHOS_USER']
- except KeyError:
- return DEFAULT_USER
-
-def get_auth():
- try:
- return os.environ['PITHOS_TOKEN']
- except KeyError:
- return DEFAULT_TOKEN
-
-def get_url():
- try:
- return os.environ['PITHOS_URL'].rstrip('/')
- except KeyError:
- return DEFAULT_URL