Statistics
| Branch: | Tag: | Revision:

root / kamaki / clients / pithos / __init__.py @ 776b275c

History | View | Annotate | Download (48.8 kB)

1 fce31e83 Stavros Sachtouris
# Copyright 2011-2013 GRNET S.A. All rights reserved.
2 d2cea1e2 Giorgos Verigakis
#
3 d2cea1e2 Giorgos Verigakis
# Redistribution and use in source and binary forms, with or
4 d2cea1e2 Giorgos Verigakis
# without modification, are permitted provided that the following
5 d2cea1e2 Giorgos Verigakis
# conditions are met:
6 d2cea1e2 Giorgos Verigakis
#
7 d2cea1e2 Giorgos Verigakis
#   1. Redistributions of source code must retain the above
8 d2cea1e2 Giorgos Verigakis
#      copyright notice, this list of conditions and the following
9 d2cea1e2 Giorgos Verigakis
#      disclaimer.
10 d2cea1e2 Giorgos Verigakis
#
11 d2cea1e2 Giorgos Verigakis
#   2. Redistributions in binary form must reproduce the above
12 d2cea1e2 Giorgos Verigakis
#      copyright notice, this list of conditions and the following
13 d2cea1e2 Giorgos Verigakis
#      disclaimer in the documentation and/or other materials
14 d2cea1e2 Giorgos Verigakis
#      provided with the distribution.
15 d2cea1e2 Giorgos Verigakis
#
16 d2cea1e2 Giorgos Verigakis
# THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
17 d2cea1e2 Giorgos Verigakis
# OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 d2cea1e2 Giorgos Verigakis
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19 d2cea1e2 Giorgos Verigakis
# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR
20 d2cea1e2 Giorgos Verigakis
# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21 d2cea1e2 Giorgos Verigakis
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22 d2cea1e2 Giorgos Verigakis
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
23 d2cea1e2 Giorgos Verigakis
# USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
24 d2cea1e2 Giorgos Verigakis
# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 d2cea1e2 Giorgos Verigakis
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
26 d2cea1e2 Giorgos Verigakis
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27 d2cea1e2 Giorgos Verigakis
# POSSIBILITY OF SUCH DAMAGE.
28 d2cea1e2 Giorgos Verigakis
#
29 d2cea1e2 Giorgos Verigakis
# The views and conclusions contained in the software and
30 d2cea1e2 Giorgos Verigakis
# documentation are those of the authors and should not be
31 d2cea1e2 Giorgos Verigakis
# interpreted as representing official policies, either expressed
32 d2cea1e2 Giorgos Verigakis
# or implied, of GRNET S.A.
33 d2cea1e2 Giorgos Verigakis
34 f27ed9a0 Stavros Sachtouris
from threading import enumerate as activethreads
35 435008b6 Stavros Sachtouris
36 3dabe5d2 Stavros Sachtouris
from os import fstat
37 64ab4c13 Stavros Sachtouris
from hashlib import new as newhashlib
38 e02728f9 Stavros Sachtouris
from time import time
39 49cc29b2 Stavros Sachtouris
from StringIO import StringIO
40 a91e0293 Giorgos Verigakis
41 64ab4c13 Stavros Sachtouris
from binascii import hexlify
42 6a0b1658 Giorgos Verigakis
43 6069b53b Stavros Sachtouris
from kamaki.clients import SilentEvent, sendlog
44 55faa0bc Stavros Sachtouris
from kamaki.clients.pithos.rest_api import PithosRestClient
45 c270fe96 Stavros Sachtouris
from kamaki.clients.storage import ClientError
46 c270fe96 Stavros Sachtouris
from kamaki.clients.utils import path4url, filter_in
47 6a0b1658 Giorgos Verigakis
48 3dabe5d2 Stavros Sachtouris
49 4375e020 Stavros Sachtouris
def _pithos_hash(block, blockhash):
50 64ab4c13 Stavros Sachtouris
    h = newhashlib(blockhash)
51 6a0b1658 Giorgos Verigakis
    h.update(block.rstrip('\x00'))
52 6a0b1658 Giorgos Verigakis
    return h.hexdigest()
53 6a0b1658 Giorgos Verigakis
54 3dabe5d2 Stavros Sachtouris
55 776b275c Stavros Sachtouris
def _range_up(start, end, max_value, a_range):
56 776b275c Stavros Sachtouris
    """
57 776b275c Stavros Sachtouris
    :param start: (int) the window bottom
58 776b275c Stavros Sachtouris

59 776b275c Stavros Sachtouris
    :param end: (int) the window top
60 776b275c Stavros Sachtouris

61 776b275c Stavros Sachtouris
    :param max_value: (int) maximum accepted value
62 776b275c Stavros Sachtouris

63 776b275c Stavros Sachtouris
    :param a_range: (str) a range string in the form X[,X'[,X''[...]]]
64 776b275c Stavros Sachtouris
        where X: x|x-y|-x where x < y and x, y natural numbers
65 776b275c Stavros Sachtouris

66 776b275c Stavros Sachtouris
    :returns: (str) a range string cut-off for the start-end range
67 776b275c Stavros Sachtouris
        an empty response means this window is out of range
68 776b275c Stavros Sachtouris
    """
69 776b275c Stavros Sachtouris
    assert start >= 0, '_range_up was called with start < 0'
70 776b275c Stavros Sachtouris
    assert end >= start, '_range_up was called with end < start'
71 776b275c Stavros Sachtouris
    assert end <= max_value, '_range_up was called with max_value < end'
72 776b275c Stavros Sachtouris
    if not a_range:
73 776b275c Stavros Sachtouris
        return '%s-%s' % (start, end)
74 776b275c Stavros Sachtouris
    selected = []
75 776b275c Stavros Sachtouris
    for some_range in a_range.split(','):
76 776b275c Stavros Sachtouris
        v0, sep, v1 = some_range.partition('-')
77 776b275c Stavros Sachtouris
        if v0:
78 776b275c Stavros Sachtouris
            v0 = int(v0)
79 6e50fed4 Stavros Sachtouris
            if sep:
80 776b275c Stavros Sachtouris
                v1 = int(v1)
81 776b275c Stavros Sachtouris
                if v1 < start or v0 > end or v1 < v0:
82 776b275c Stavros Sachtouris
                    continue
83 776b275c Stavros Sachtouris
                v0 = v0 if v0 > start else start
84 776b275c Stavros Sachtouris
                v1 = v1 if v1 < end else end
85 776b275c Stavros Sachtouris
                selected.append('%s-%s' % (v0, v1))
86 776b275c Stavros Sachtouris
            elif v0 < start:
87 776b275c Stavros Sachtouris
                continue
88 6e50fed4 Stavros Sachtouris
            else:
89 776b275c Stavros Sachtouris
                v1 = v0 if v0 <= end else end
90 776b275c Stavros Sachtouris
                selected.append('%s-%s' % (start, v1))
91 6e50fed4 Stavros Sachtouris
        else:
92 776b275c Stavros Sachtouris
            v1 = int(v1)
93 776b275c Stavros Sachtouris
            if max_value - v1 > end:
94 776b275c Stavros Sachtouris
                continue
95 776b275c Stavros Sachtouris
            v0 = (max_value - v1) if max_value - v1 > start else start
96 776b275c Stavros Sachtouris
            selected.append('%s-%s' % (v0, end))
97 776b275c Stavros Sachtouris
    return ','.join(selected)
98 642f1bbd Stavros Sachtouris
99 3dabe5d2 Stavros Sachtouris
100 55faa0bc Stavros Sachtouris
class PithosClient(PithosRestClient):
101 76e7661e Stavros Sachtouris
    """Synnefo Pithos+ API client"""
102 a91e0293 Giorgos Verigakis
103 3dabe5d2 Stavros Sachtouris
    def __init__(self, base_url, token, account=None, container=None):
104 3dabe5d2 Stavros Sachtouris
        super(PithosClient, self).__init__(base_url, token, account, container)
105 435008b6 Stavros Sachtouris
106 94bedc5b Stavros Sachtouris
    def create_container(
107 94bedc5b Stavros Sachtouris
            self,
108 94bedc5b Stavros Sachtouris
            container=None, sizelimit=None, versioning=None, metadata=None):
109 94bedc5b Stavros Sachtouris
        """
110 94bedc5b Stavros Sachtouris
        :param container: (str) if not given, self.container is used instead
111 94bedc5b Stavros Sachtouris

112 94bedc5b Stavros Sachtouris
        :param sizelimit: (int) container total size limit in bytes
113 94bedc5b Stavros Sachtouris

114 94bedc5b Stavros Sachtouris
        :param versioning: (str) can be auto or whatever supported by server
115 94bedc5b Stavros Sachtouris

116 94bedc5b Stavros Sachtouris
        :param metadata: (dict) Custom user-defined metadata of the form
117 94bedc5b Stavros Sachtouris
            { 'name1': 'value1', 'name2': 'value2', ... }
118 94bedc5b Stavros Sachtouris

119 94bedc5b Stavros Sachtouris
        :returns: (dict) response headers
120 94bedc5b Stavros Sachtouris
        """
121 94bedc5b Stavros Sachtouris
        cnt_back_up = self.container
122 94bedc5b Stavros Sachtouris
        try:
123 94bedc5b Stavros Sachtouris
            self.container = container or cnt_back_up
124 94bedc5b Stavros Sachtouris
            r = self.container_put(
125 94bedc5b Stavros Sachtouris
                quota=sizelimit, versioning=versioning, metadata=metadata)
126 94bedc5b Stavros Sachtouris
            return r.headers
127 94bedc5b Stavros Sachtouris
        finally:
128 94bedc5b Stavros Sachtouris
            self.container = cnt_back_up
129 94bedc5b Stavros Sachtouris
130 2a7292f1 Stavros Sachtouris
    def purge_container(self, container=None):
131 4375e020 Stavros Sachtouris
        """Delete an empty container and destroy associated blocks
132 4375e020 Stavros Sachtouris
        """
133 2a7292f1 Stavros Sachtouris
        cnt_back_up = self.container
134 2a7292f1 Stavros Sachtouris
        try:
135 2a7292f1 Stavros Sachtouris
            self.container = container or cnt_back_up
136 5655d560 Stavros Sachtouris
            r = self.container_delete(until=unicode(time()))
137 2a7292f1 Stavros Sachtouris
        finally:
138 2a7292f1 Stavros Sachtouris
            self.container = cnt_back_up
139 5655d560 Stavros Sachtouris
        return r.headers
140 3dabe5d2 Stavros Sachtouris
141 24ff0a35 Stavros Sachtouris
    def upload_object_unchunked(
142 24ff0a35 Stavros Sachtouris
            self, obj, f,
143 24ff0a35 Stavros Sachtouris
            withHashFile=False,
144 24ff0a35 Stavros Sachtouris
            size=None,
145 24ff0a35 Stavros Sachtouris
            etag=None,
146 24ff0a35 Stavros Sachtouris
            content_encoding=None,
147 24ff0a35 Stavros Sachtouris
            content_disposition=None,
148 24ff0a35 Stavros Sachtouris
            content_type=None,
149 24ff0a35 Stavros Sachtouris
            sharing=None,
150 24ff0a35 Stavros Sachtouris
            public=None):
151 4375e020 Stavros Sachtouris
        """
152 4375e020 Stavros Sachtouris
        :param obj: (str) remote object path
153 4375e020 Stavros Sachtouris

154 4375e020 Stavros Sachtouris
        :param f: open file descriptor
155 4375e020 Stavros Sachtouris

156 4375e020 Stavros Sachtouris
        :param withHashFile: (bool)
157 4375e020 Stavros Sachtouris

158 4375e020 Stavros Sachtouris
        :param size: (int) size of data to upload
159 4375e020 Stavros Sachtouris

160 4375e020 Stavros Sachtouris
        :param etag: (str)
161 4375e020 Stavros Sachtouris

162 4375e020 Stavros Sachtouris
        :param content_encoding: (str)
163 4375e020 Stavros Sachtouris

164 4375e020 Stavros Sachtouris
        :param content_disposition: (str)
165 4375e020 Stavros Sachtouris

166 4375e020 Stavros Sachtouris
        :param content_type: (str)
167 4375e020 Stavros Sachtouris

168 4375e020 Stavros Sachtouris
        :param sharing: {'read':[user and/or grp names],
169 4375e020 Stavros Sachtouris
            'write':[usr and/or grp names]}
170 4375e020 Stavros Sachtouris

171 4375e020 Stavros Sachtouris
        :param public: (bool)
172 3c216009 Stavros Sachtouris

173 3c216009 Stavros Sachtouris
        :returns: (dict) created object metadata
174 4375e020 Stavros Sachtouris
        """
175 277ca4ed Dionysis Zindros
        self._assert_container()
176 65a45524 Stavros Sachtouris
177 65a45524 Stavros Sachtouris
        if withHashFile:
178 65a45524 Stavros Sachtouris
            data = f.read()
179 65a45524 Stavros Sachtouris
            try:
180 65a45524 Stavros Sachtouris
                import json
181 65a45524 Stavros Sachtouris
                data = json.dumps(json.loads(data))
182 65a45524 Stavros Sachtouris
            except ValueError:
183 24ff0a35 Stavros Sachtouris
                raise ClientError('"%s" is not json-formated' % f.name, 1)
184 65a45524 Stavros Sachtouris
            except SyntaxError:
185 24ff0a35 Stavros Sachtouris
                msg = '"%s" is not a valid hashmap file' % f.name
186 24ff0a35 Stavros Sachtouris
                raise ClientError(msg, 1)
187 65a45524 Stavros Sachtouris
            f = StringIO(data)
188 2a7292f1 Stavros Sachtouris
        else:
189 975270f2 Stavros Sachtouris
            data = f.read(size) if size else f.read()
190 3c216009 Stavros Sachtouris
        r = self.object_put(
191 24ff0a35 Stavros Sachtouris
            obj,
192 3dabe5d2 Stavros Sachtouris
            data=data,
193 3dabe5d2 Stavros Sachtouris
            etag=etag,
194 3dabe5d2 Stavros Sachtouris
            content_encoding=content_encoding,
195 3dabe5d2 Stavros Sachtouris
            content_disposition=content_disposition,
196 3dabe5d2 Stavros Sachtouris
            content_type=content_type,
197 3dabe5d2 Stavros Sachtouris
            permissions=sharing,
198 3dabe5d2 Stavros Sachtouris
            public=public,
199 3dabe5d2 Stavros Sachtouris
            success=201)
200 3c216009 Stavros Sachtouris
        return r.headers
201 3dabe5d2 Stavros Sachtouris
202 24ff0a35 Stavros Sachtouris
    def create_object_by_manifestation(
203 24ff0a35 Stavros Sachtouris
            self, obj,
204 24ff0a35 Stavros Sachtouris
            etag=None,
205 24ff0a35 Stavros Sachtouris
            content_encoding=None,
206 24ff0a35 Stavros Sachtouris
            content_disposition=None,
207 24ff0a35 Stavros Sachtouris
            content_type=None,
208 24ff0a35 Stavros Sachtouris
            sharing=None,
209 24ff0a35 Stavros Sachtouris
            public=None):
210 4375e020 Stavros Sachtouris
        """
211 4375e020 Stavros Sachtouris
        :param obj: (str) remote object path
212 4375e020 Stavros Sachtouris

213 4375e020 Stavros Sachtouris
        :param etag: (str)
214 4375e020 Stavros Sachtouris

215 4375e020 Stavros Sachtouris
        :param content_encoding: (str)
216 4375e020 Stavros Sachtouris

217 4375e020 Stavros Sachtouris
        :param content_disposition: (str)
218 4375e020 Stavros Sachtouris

219 4375e020 Stavros Sachtouris
        :param content_type: (str)
220 4375e020 Stavros Sachtouris

221 4375e020 Stavros Sachtouris
        :param sharing: {'read':[user and/or grp names],
222 4375e020 Stavros Sachtouris
            'write':[usr and/or grp names]}
223 4375e020 Stavros Sachtouris

224 4375e020 Stavros Sachtouris
        :param public: (bool)
225 3c216009 Stavros Sachtouris

226 3c216009 Stavros Sachtouris
        :returns: (dict) created object metadata
227 4375e020 Stavros Sachtouris
        """
228 277ca4ed Dionysis Zindros
        self._assert_container()
229 3c216009 Stavros Sachtouris
        r = self.object_put(
230 24ff0a35 Stavros Sachtouris
            obj,
231 3dabe5d2 Stavros Sachtouris
            content_length=0,
232 3dabe5d2 Stavros Sachtouris
            etag=etag,
233 3dabe5d2 Stavros Sachtouris
            content_encoding=content_encoding,
234 3dabe5d2 Stavros Sachtouris
            content_disposition=content_disposition,
235 3dabe5d2 Stavros Sachtouris
            content_type=content_type,
236 3dabe5d2 Stavros Sachtouris
            permissions=sharing,
237 3dabe5d2 Stavros Sachtouris
            public=public,
238 3dabe5d2 Stavros Sachtouris
            manifest='%s/%s' % (self.container, obj))
239 3c216009 Stavros Sachtouris
        return r.headers
240 3dabe5d2 Stavros Sachtouris
241 4375e020 Stavros Sachtouris
    # upload_* auxiliary methods
242 9d502497 Stavros Sachtouris
    def _put_block_async(self, data, hash):
243 4375e020 Stavros Sachtouris
        event = SilentEvent(method=self._put_block, data=data, hash=hash)
244 4375e020 Stavros Sachtouris
        event.start()
245 4375e020 Stavros Sachtouris
        return event
246 4375e020 Stavros Sachtouris
247 4375e020 Stavros Sachtouris
    def _put_block(self, data, hash):
248 24ff0a35 Stavros Sachtouris
        r = self.container_post(
249 24ff0a35 Stavros Sachtouris
            update=True,
250 4375e020 Stavros Sachtouris
            content_type='application/octet-stream',
251 4375e020 Stavros Sachtouris
            content_length=len(data),
252 4375e020 Stavros Sachtouris
            data=data,
253 4375e020 Stavros Sachtouris
            format='json')
254 4375e020 Stavros Sachtouris
        assert r.json[0] == hash, 'Local hash does not match server'
255 4375e020 Stavros Sachtouris
256 76ebf97c Stavros Sachtouris
    def _get_file_block_info(self, fileobj, size=None, cache=None):
257 76ebf97c Stavros Sachtouris
        """
258 76ebf97c Stavros Sachtouris
        :param fileobj: (file descriptor) source
259 76ebf97c Stavros Sachtouris

260 76ebf97c Stavros Sachtouris
        :param size: (int) size of data to upload from source
261 76ebf97c Stavros Sachtouris

262 76ebf97c Stavros Sachtouris
        :param cache: (dict) if provided, cache container info response to
263 76ebf97c Stavros Sachtouris
        avoid redundant calls
264 76ebf97c Stavros Sachtouris
        """
265 76ebf97c Stavros Sachtouris
        if isinstance(cache, dict):
266 76ebf97c Stavros Sachtouris
            try:
267 76ebf97c Stavros Sachtouris
                meta = cache[self.container]
268 76ebf97c Stavros Sachtouris
            except KeyError:
269 76ebf97c Stavros Sachtouris
                meta = self.get_container_info()
270 76ebf97c Stavros Sachtouris
                cache[self.container] = meta
271 76ebf97c Stavros Sachtouris
        else:
272 76ebf97c Stavros Sachtouris
            meta = self.get_container_info()
273 435008b6 Stavros Sachtouris
        blocksize = int(meta['x-container-block-size'])
274 435008b6 Stavros Sachtouris
        blockhash = meta['x-container-block-hash']
275 64ab4c13 Stavros Sachtouris
        size = size if size is not None else fstat(fileobj.fileno()).st_size
276 435008b6 Stavros Sachtouris
        nblocks = 1 + (size - 1) // blocksize
277 64ab4c13 Stavros Sachtouris
        return (blocksize, blockhash, size, nblocks)
278 64ab4c13 Stavros Sachtouris
279 9d502497 Stavros Sachtouris
    def _create_object_or_get_missing_hashes(
280 24ff0a35 Stavros Sachtouris
            self, obj, json,
281 24ff0a35 Stavros Sachtouris
            size=None,
282 24ff0a35 Stavros Sachtouris
            format='json',
283 24ff0a35 Stavros Sachtouris
            hashmap=True,
284 24ff0a35 Stavros Sachtouris
            content_type=None,
285 b349b84b Stavros Sachtouris
            if_etag_match=None,
286 b349b84b Stavros Sachtouris
            if_etag_not_match=None,
287 24ff0a35 Stavros Sachtouris
            content_encoding=None,
288 24ff0a35 Stavros Sachtouris
            content_disposition=None,
289 24ff0a35 Stavros Sachtouris
            permissions=None,
290 24ff0a35 Stavros Sachtouris
            public=None,
291 24ff0a35 Stavros Sachtouris
            success=(201, 409)):
292 24ff0a35 Stavros Sachtouris
        r = self.object_put(
293 24ff0a35 Stavros Sachtouris
            obj,
294 3dabe5d2 Stavros Sachtouris
            format='json',
295 3dabe5d2 Stavros Sachtouris
            hashmap=True,
296 3dabe5d2 Stavros Sachtouris
            content_type=content_type,
297 3dabe5d2 Stavros Sachtouris
            json=json,
298 b349b84b Stavros Sachtouris
            if_etag_match=if_etag_match,
299 b349b84b Stavros Sachtouris
            if_etag_not_match=if_etag_not_match,
300 3dabe5d2 Stavros Sachtouris
            content_encoding=content_encoding,
301 3dabe5d2 Stavros Sachtouris
            content_disposition=content_disposition,
302 3dabe5d2 Stavros Sachtouris
            permissions=permissions,
303 3dabe5d2 Stavros Sachtouris
            public=public,
304 64ab4c13 Stavros Sachtouris
            success=success)
305 3c216009 Stavros Sachtouris
        return (None if r.status_code == 201 else r.json), r.headers
306 435008b6 Stavros Sachtouris
307 b349b84b Stavros Sachtouris
    def _calculate_blocks_for_upload(
308 2005b18e Stavros Sachtouris
            self, blocksize, blockhash, size, nblocks, hashes, hmap, fileobj,
309 2005b18e Stavros Sachtouris
            hash_cb=None):
310 3dabe5d2 Stavros Sachtouris
        offset = 0
311 435008b6 Stavros Sachtouris
        if hash_cb:
312 435008b6 Stavros Sachtouris
            hash_gen = hash_cb(nblocks)
313 435008b6 Stavros Sachtouris
            hash_gen.next()
314 435008b6 Stavros Sachtouris
315 435008b6 Stavros Sachtouris
        for i in range(nblocks):
316 64ab4c13 Stavros Sachtouris
            block = fileobj.read(min(blocksize, size - offset))
317 435008b6 Stavros Sachtouris
            bytes = len(block)
318 4375e020 Stavros Sachtouris
            hash = _pithos_hash(block, blockhash)
319 435008b6 Stavros Sachtouris
            hashes.append(hash)
320 5b263ba2 Stavros Sachtouris
            hmap[hash] = (offset, bytes)
321 435008b6 Stavros Sachtouris
            offset += bytes
322 435008b6 Stavros Sachtouris
            if hash_cb:
323 435008b6 Stavros Sachtouris
                hash_gen.next()
324 fce31e83 Stavros Sachtouris
        msg = 'Failed to calculate uploaded blocks:'
325 bcbf3cf0 Stavros Sachtouris
        ' Offset and object size do not match'
326 fce31e83 Stavros Sachtouris
        assert offset == size, msg
327 435008b6 Stavros Sachtouris
328 3e7d1e0e Stavros Sachtouris
    def _upload_missing_blocks(self, missing, hmap, fileobj, upload_gen=None):
329 3e7d1e0e Stavros Sachtouris
        """upload missing blocks asynchronously"""
330 435008b6 Stavros Sachtouris
331 cad39033 Stavros Sachtouris
        self._init_thread_limit()
332 e9abe82b Stavros Sachtouris
333 435008b6 Stavros Sachtouris
        flying = []
334 7644c38e Stavros Sachtouris
        failures = []
335 435008b6 Stavros Sachtouris
        for hash in missing:
336 5b263ba2 Stavros Sachtouris
            offset, bytes = hmap[hash]
337 64ab4c13 Stavros Sachtouris
            fileobj.seek(offset)
338 64ab4c13 Stavros Sachtouris
            data = fileobj.read(bytes)
339 9d502497 Stavros Sachtouris
            r = self._put_block_async(data, hash)
340 435008b6 Stavros Sachtouris
            flying.append(r)
341 745d938b Stavros Sachtouris
            unfinished = self._watch_thread_limit(flying)
342 745d938b Stavros Sachtouris
            for thread in set(flying).difference(unfinished):
343 7644c38e Stavros Sachtouris
                if thread.exception:
344 7644c38e Stavros Sachtouris
                    failures.append(thread)
345 24ff0a35 Stavros Sachtouris
                    if isinstance(
346 2005b18e Stavros Sachtouris
                            thread.exception,
347 2005b18e Stavros Sachtouris
                            ClientError) and thread.exception.status == 502:
348 2005b18e Stavros Sachtouris
                        self.POOLSIZE = self._thread_limit
349 7644c38e Stavros Sachtouris
                elif thread.isAlive():
350 745d938b Stavros Sachtouris
                    flying.append(thread)
351 3e7d1e0e Stavros Sachtouris
                elif upload_gen:
352 3e7d1e0e Stavros Sachtouris
                    try:
353 3e7d1e0e Stavros Sachtouris
                        upload_gen.next()
354 3e7d1e0e Stavros Sachtouris
                    except:
355 3e7d1e0e Stavros Sachtouris
                        pass
356 e02728f9 Stavros Sachtouris
            flying = unfinished
357 e02728f9 Stavros Sachtouris
358 e02728f9 Stavros Sachtouris
        for thread in flying:
359 e02728f9 Stavros Sachtouris
            thread.join()
360 7644c38e Stavros Sachtouris
            if thread.exception:
361 7644c38e Stavros Sachtouris
                failures.append(thread)
362 3e7d1e0e Stavros Sachtouris
            elif upload_gen:
363 3e7d1e0e Stavros Sachtouris
                try:
364 3e7d1e0e Stavros Sachtouris
                    upload_gen.next()
365 3e7d1e0e Stavros Sachtouris
                except:
366 3e7d1e0e Stavros Sachtouris
                    pass
367 56f0908a Stavros Sachtouris
368 7644c38e Stavros Sachtouris
        return [failure.kwargs['hash'] for failure in failures]
369 3dabe5d2 Stavros Sachtouris
370 24ff0a35 Stavros Sachtouris
    def upload_object(
371 24ff0a35 Stavros Sachtouris
            self, obj, f,
372 24ff0a35 Stavros Sachtouris
            size=None,
373 24ff0a35 Stavros Sachtouris
            hash_cb=None,
374 24ff0a35 Stavros Sachtouris
            upload_cb=None,
375 24ff0a35 Stavros Sachtouris
            etag=None,
376 e9ac514e Stavros Sachtouris
            if_etag_match=None,
377 524d9cdd Stavros Sachtouris
            if_not_exist=None,
378 24ff0a35 Stavros Sachtouris
            content_encoding=None,
379 24ff0a35 Stavros Sachtouris
            content_disposition=None,
380 24ff0a35 Stavros Sachtouris
            content_type=None,
381 24ff0a35 Stavros Sachtouris
            sharing=None,
382 76ebf97c Stavros Sachtouris
            public=None,
383 76ebf97c Stavros Sachtouris
            container_info_cache=None):
384 4375e020 Stavros Sachtouris
        """Upload an object using multiple connections (threads)
385 4375e020 Stavros Sachtouris

386 4375e020 Stavros Sachtouris
        :param obj: (str) remote object path
387 4375e020 Stavros Sachtouris

388 4375e020 Stavros Sachtouris
        :param f: open file descriptor (rb)
389 4375e020 Stavros Sachtouris

390 4375e020 Stavros Sachtouris
        :param hash_cb: optional progress.bar object for calculating hashes
391 4375e020 Stavros Sachtouris

392 4375e020 Stavros Sachtouris
        :param upload_cb: optional progress.bar object for uploading
393 4375e020 Stavros Sachtouris

394 4375e020 Stavros Sachtouris
        :param etag: (str)
395 4375e020 Stavros Sachtouris

396 e9ac514e Stavros Sachtouris
        :param if_etag_match: (str) Push that value to if-match header at file
397 e9ac514e Stavros Sachtouris
            creation
398 e9ac514e Stavros Sachtouris

399 524d9cdd Stavros Sachtouris
        :param if_not_exist: (bool) If true, the file will be uploaded ONLY if
400 524d9cdd Stavros Sachtouris
            it does not exist remotely, otherwise the operation will fail.
401 524d9cdd Stavros Sachtouris
            Involves the case of an object with the same path is created while
402 524d9cdd Stavros Sachtouris
            the object is being uploaded.
403 524d9cdd Stavros Sachtouris

404 4375e020 Stavros Sachtouris
        :param content_encoding: (str)
405 4375e020 Stavros Sachtouris

406 4375e020 Stavros Sachtouris
        :param content_disposition: (str)
407 4375e020 Stavros Sachtouris

408 4375e020 Stavros Sachtouris
        :param content_type: (str)
409 4375e020 Stavros Sachtouris

410 4375e020 Stavros Sachtouris
        :param sharing: {'read':[user and/or grp names],
411 4375e020 Stavros Sachtouris
            'write':[usr and/or grp names]}
412 4375e020 Stavros Sachtouris

413 4375e020 Stavros Sachtouris
        :param public: (bool)
414 76ebf97c Stavros Sachtouris

415 76ebf97c Stavros Sachtouris
        :param container_info_cache: (dict) if given, avoid redundant calls to
416 9dc6159f Stavros Sachtouris
            server for container info (block size and hash information)
417 4375e020 Stavros Sachtouris
        """
418 277ca4ed Dionysis Zindros
        self._assert_container()
419 56f0908a Stavros Sachtouris
420 14c72dbd Stavros Sachtouris
        block_info = (
421 14c72dbd Stavros Sachtouris
            blocksize, blockhash, size, nblocks) = self._get_file_block_info(
422 14c72dbd Stavros Sachtouris
                f, size, container_info_cache)
423 64ab4c13 Stavros Sachtouris
        (hashes, hmap, offset) = ([], {}, 0)
424 bcbf3cf0 Stavros Sachtouris
        if not content_type:
425 3dabe5d2 Stavros Sachtouris
            content_type = 'application/octet-stream'
426 64ab4c13 Stavros Sachtouris
427 b349b84b Stavros Sachtouris
        self._calculate_blocks_for_upload(
428 24ff0a35 Stavros Sachtouris
            *block_info,
429 3dabe5d2 Stavros Sachtouris
            hashes=hashes,
430 3dabe5d2 Stavros Sachtouris
            hmap=hmap,
431 3dabe5d2 Stavros Sachtouris
            fileobj=f,
432 64ab4c13 Stavros Sachtouris
            hash_cb=hash_cb)
433 64ab4c13 Stavros Sachtouris
434 64ab4c13 Stavros Sachtouris
        hashmap = dict(bytes=size, hashes=hashes)
435 9d502497 Stavros Sachtouris
        missing, obj_headers = self._create_object_or_get_missing_hashes(
436 24ff0a35 Stavros Sachtouris
            obj, hashmap,
437 3dabe5d2 Stavros Sachtouris
            content_type=content_type,
438 3dabe5d2 Stavros Sachtouris
            size=size,
439 b349b84b Stavros Sachtouris
            if_etag_match=if_etag_match,
440 b349b84b Stavros Sachtouris
            if_etag_not_match='*' if if_not_exist else None,
441 3dabe5d2 Stavros Sachtouris
            content_encoding=content_encoding,
442 3dabe5d2 Stavros Sachtouris
            content_disposition=content_disposition,
443 3dabe5d2 Stavros Sachtouris
            permissions=sharing,
444 3dabe5d2 Stavros Sachtouris
            public=public)
445 64ab4c13 Stavros Sachtouris
446 64ab4c13 Stavros Sachtouris
        if missing is None:
447 3c216009 Stavros Sachtouris
            return obj_headers
448 1d329d27 Stavros Sachtouris
449 3e7d1e0e Stavros Sachtouris
        if upload_cb:
450 3e7d1e0e Stavros Sachtouris
            upload_gen = upload_cb(len(missing))
451 745d938b Stavros Sachtouris
            for i in range(len(missing), len(hashmap['hashes']) + 1):
452 745d938b Stavros Sachtouris
                try:
453 745d938b Stavros Sachtouris
                    upload_gen.next()
454 745d938b Stavros Sachtouris
                except:
455 745d938b Stavros Sachtouris
                    upload_gen = None
456 3e7d1e0e Stavros Sachtouris
        else:
457 3e7d1e0e Stavros Sachtouris
            upload_gen = None
458 3e7d1e0e Stavros Sachtouris
459 7eda693f Stavros Sachtouris
        retries = 7
460 f27ed9a0 Stavros Sachtouris
        try:
461 7644c38e Stavros Sachtouris
            while retries:
462 7eda693f Stavros Sachtouris
                sendlog.info('%s blocks missing' % len(missing))
463 7644c38e Stavros Sachtouris
                num_of_blocks = len(missing)
464 7644c38e Stavros Sachtouris
                missing = self._upload_missing_blocks(
465 7644c38e Stavros Sachtouris
                    missing,
466 7644c38e Stavros Sachtouris
                    hmap,
467 7644c38e Stavros Sachtouris
                    f,
468 3e7d1e0e Stavros Sachtouris
                    upload_gen)
469 16c895db Stavros Sachtouris
                if missing:
470 16c895db Stavros Sachtouris
                    if num_of_blocks == len(missing):
471 16c895db Stavros Sachtouris
                        retries -= 1
472 16c895db Stavros Sachtouris
                    else:
473 16c895db Stavros Sachtouris
                        num_of_blocks = len(missing)
474 7644c38e Stavros Sachtouris
                else:
475 7644c38e Stavros Sachtouris
                    break
476 7644c38e Stavros Sachtouris
            if missing:
477 ecf0fc97 Stavros Sachtouris
                try:
478 ecf0fc97 Stavros Sachtouris
                    details = ['%s' % thread.exception for thread in missing]
479 ecf0fc97 Stavros Sachtouris
                except Exception:
480 5c2058e7 Stavros Sachtouris
                    details = ['Also, failed to read thread exceptions']
481 6fa30b1b Stavros Sachtouris
                raise ClientError(
482 6fa30b1b Stavros Sachtouris
                    '%s blocks failed to upload' % len(missing),
483 ecf0fc97 Stavros Sachtouris
                    details=details)
484 9d502497 Stavros Sachtouris
        except KeyboardInterrupt:
485 9d502497 Stavros Sachtouris
            sendlog.info('- - - wait for threads to finish')
486 9d502497 Stavros Sachtouris
            for thread in activethreads():
487 9d502497 Stavros Sachtouris
                thread.join()
488 9d502497 Stavros Sachtouris
            raise
489 9d502497 Stavros Sachtouris
490 9d502497 Stavros Sachtouris
        r = self.object_put(
491 9d502497 Stavros Sachtouris
            obj,
492 9d502497 Stavros Sachtouris
            format='json',
493 9d502497 Stavros Sachtouris
            hashmap=True,
494 9d502497 Stavros Sachtouris
            content_type=content_type,
495 f8426b5c Stavros Sachtouris
            content_encoding=content_encoding,
496 9d502497 Stavros Sachtouris
            if_etag_match=if_etag_match,
497 9d502497 Stavros Sachtouris
            if_etag_not_match='*' if if_not_exist else None,
498 9d502497 Stavros Sachtouris
            etag=etag,
499 9d502497 Stavros Sachtouris
            json=hashmap,
500 9d502497 Stavros Sachtouris
            permissions=sharing,
501 9d502497 Stavros Sachtouris
            public=public,
502 9d502497 Stavros Sachtouris
            success=201)
503 9d502497 Stavros Sachtouris
        return r.headers
504 9d502497 Stavros Sachtouris
505 9d502497 Stavros Sachtouris
    def upload_from_string(
506 9d502497 Stavros Sachtouris
            self, obj, input_str,
507 9d502497 Stavros Sachtouris
            hash_cb=None,
508 9d502497 Stavros Sachtouris
            upload_cb=None,
509 9d502497 Stavros Sachtouris
            etag=None,
510 9d502497 Stavros Sachtouris
            if_etag_match=None,
511 9d502497 Stavros Sachtouris
            if_not_exist=None,
512 9d502497 Stavros Sachtouris
            content_encoding=None,
513 9d502497 Stavros Sachtouris
            content_disposition=None,
514 9d502497 Stavros Sachtouris
            content_type=None,
515 9d502497 Stavros Sachtouris
            sharing=None,
516 9d502497 Stavros Sachtouris
            public=None,
517 9d502497 Stavros Sachtouris
            container_info_cache=None):
518 9d502497 Stavros Sachtouris
        """Upload an object using multiple connections (threads)
519 9d502497 Stavros Sachtouris

520 9d502497 Stavros Sachtouris
        :param obj: (str) remote object path
521 9d502497 Stavros Sachtouris

522 9d502497 Stavros Sachtouris
        :param input_str: (str) upload content
523 9d502497 Stavros Sachtouris

524 9d502497 Stavros Sachtouris
        :param hash_cb: optional progress.bar object for calculating hashes
525 9d502497 Stavros Sachtouris

526 9d502497 Stavros Sachtouris
        :param upload_cb: optional progress.bar object for uploading
527 9d502497 Stavros Sachtouris

528 9d502497 Stavros Sachtouris
        :param etag: (str)
529 9d502497 Stavros Sachtouris

530 9d502497 Stavros Sachtouris
        :param if_etag_match: (str) Push that value to if-match header at file
531 9d502497 Stavros Sachtouris
            creation
532 9d502497 Stavros Sachtouris

533 9d502497 Stavros Sachtouris
        :param if_not_exist: (bool) If true, the file will be uploaded ONLY if
534 9d502497 Stavros Sachtouris
            it does not exist remotely, otherwise the operation will fail.
535 9d502497 Stavros Sachtouris
            Involves the case of an object with the same path is created while
536 9d502497 Stavros Sachtouris
            the object is being uploaded.
537 9d502497 Stavros Sachtouris

538 9d502497 Stavros Sachtouris
        :param content_encoding: (str)
539 9d502497 Stavros Sachtouris

540 9d502497 Stavros Sachtouris
        :param content_disposition: (str)
541 9d502497 Stavros Sachtouris

542 9d502497 Stavros Sachtouris
        :param content_type: (str)
543 9d502497 Stavros Sachtouris

544 9d502497 Stavros Sachtouris
        :param sharing: {'read':[user and/or grp names],
545 9d502497 Stavros Sachtouris
            'write':[usr and/or grp names]}
546 9d502497 Stavros Sachtouris

547 9d502497 Stavros Sachtouris
        :param public: (bool)
548 9d502497 Stavros Sachtouris

549 9d502497 Stavros Sachtouris
        :param container_info_cache: (dict) if given, avoid redundant calls to
550 9dc6159f Stavros Sachtouris
            server for container info (block size and hash information)
551 9d502497 Stavros Sachtouris
        """
552 9d502497 Stavros Sachtouris
        self._assert_container()
553 9d502497 Stavros Sachtouris
554 9d502497 Stavros Sachtouris
        blocksize, blockhash, size, nblocks = self._get_file_block_info(
555 9d502497 Stavros Sachtouris
                fileobj=None, size=len(input_str), cache=container_info_cache)
556 9d502497 Stavros Sachtouris
        (hashes, hmap, offset) = ([], {}, 0)
557 9d502497 Stavros Sachtouris
        if not content_type:
558 9d502497 Stavros Sachtouris
            content_type = 'application/octet-stream'
559 9d502497 Stavros Sachtouris
560 6fa30b1b Stavros Sachtouris
        hashes = []
561 9d502497 Stavros Sachtouris
        hmap = {}
562 f17121cd Stavros Sachtouris
        for blockid in range(nblocks):
563 9d502497 Stavros Sachtouris
            start = blockid * blocksize
564 9d502497 Stavros Sachtouris
            block = input_str[start: (start + blocksize)]
565 6fa30b1b Stavros Sachtouris
            hashes.append(_pithos_hash(block, blockhash))
566 9d502497 Stavros Sachtouris
            hmap[hashes[blockid]] = (start, block)
567 9d502497 Stavros Sachtouris
568 9d502497 Stavros Sachtouris
        hashmap = dict(bytes=size, hashes=hashes)
569 9d502497 Stavros Sachtouris
        missing, obj_headers = self._create_object_or_get_missing_hashes(
570 9d502497 Stavros Sachtouris
            obj, hashmap,
571 9d502497 Stavros Sachtouris
            content_type=content_type,
572 9d502497 Stavros Sachtouris
            size=size,
573 9d502497 Stavros Sachtouris
            if_etag_match=if_etag_match,
574 9d502497 Stavros Sachtouris
            if_etag_not_match='*' if if_not_exist else None,
575 9d502497 Stavros Sachtouris
            content_encoding=content_encoding,
576 9d502497 Stavros Sachtouris
            content_disposition=content_disposition,
577 9d502497 Stavros Sachtouris
            permissions=sharing,
578 9d502497 Stavros Sachtouris
            public=public)
579 9d502497 Stavros Sachtouris
        if missing is None:
580 9d502497 Stavros Sachtouris
            return obj_headers
581 9d502497 Stavros Sachtouris
        num_of_missing = len(missing)
582 9d502497 Stavros Sachtouris
583 9d502497 Stavros Sachtouris
        if upload_cb:
584 f17121cd Stavros Sachtouris
            self.progress_bar_gen = upload_cb(nblocks)
585 f17121cd Stavros Sachtouris
            for i in range(nblocks + 1 - num_of_missing):
586 9d502497 Stavros Sachtouris
                self._cb_next()
587 9d502497 Stavros Sachtouris
588 6fa30b1b Stavros Sachtouris
        tries = 7
589 6fa30b1b Stavros Sachtouris
        old_failures = 0
590 9d502497 Stavros Sachtouris
        try:
591 6fa30b1b Stavros Sachtouris
            while tries and missing:
592 6fa30b1b Stavros Sachtouris
                flying = []
593 6fa30b1b Stavros Sachtouris
                failures = []
594 6fa30b1b Stavros Sachtouris
                for hash in missing:
595 6fa30b1b Stavros Sachtouris
                    offset, block = hmap[hash]
596 6fa30b1b Stavros Sachtouris
                    bird = self._put_block_async(block, hash)
597 6fa30b1b Stavros Sachtouris
                    flying.append(bird)
598 6fa30b1b Stavros Sachtouris
                    unfinished = self._watch_thread_limit(flying)
599 6fa30b1b Stavros Sachtouris
                    for thread in set(flying).difference(unfinished):
600 6fa30b1b Stavros Sachtouris
                        if thread.exception:
601 6fa30b1b Stavros Sachtouris
                            failures.append(thread.kwargs['hash'])
602 6fa30b1b Stavros Sachtouris
                        if thread.isAlive():
603 6fa30b1b Stavros Sachtouris
                            flying.append(thread)
604 6fa30b1b Stavros Sachtouris
                        else:
605 6fa30b1b Stavros Sachtouris
                            self._cb_next()
606 6fa30b1b Stavros Sachtouris
                    flying = unfinished
607 6fa30b1b Stavros Sachtouris
                for thread in flying:
608 6fa30b1b Stavros Sachtouris
                    thread.join()
609 9d502497 Stavros Sachtouris
                    if thread.exception:
610 6fa30b1b Stavros Sachtouris
                        failures.append(thread.kwargs['hash'])
611 6fa30b1b Stavros Sachtouris
                    self._cb_next()
612 6fa30b1b Stavros Sachtouris
                missing = failures
613 6fa30b1b Stavros Sachtouris
                if missing and len(missing) == old_failures:
614 6fa30b1b Stavros Sachtouris
                    tries -= 1
615 6fa30b1b Stavros Sachtouris
                old_failures = len(missing)
616 6fa30b1b Stavros Sachtouris
            if missing:
617 6fa30b1b Stavros Sachtouris
                raise ClientError(
618 6fa30b1b Stavros Sachtouris
                    '%s blocks failed to upload' % len(missing),
619 6fa30b1b Stavros Sachtouris
                    details=['%s' % thread.exception for thread in missing])
620 f27ed9a0 Stavros Sachtouris
        except KeyboardInterrupt:
621 6069b53b Stavros Sachtouris
            sendlog.info('- - - wait for threads to finish')
622 f27ed9a0 Stavros Sachtouris
            for thread in activethreads():
623 f27ed9a0 Stavros Sachtouris
                thread.join()
624 f27ed9a0 Stavros Sachtouris
            raise
625 f27ed9a0 Stavros Sachtouris
626 3c216009 Stavros Sachtouris
        r = self.object_put(
627 f27ed9a0 Stavros Sachtouris
            obj,
628 3dabe5d2 Stavros Sachtouris
            format='json',
629 3dabe5d2 Stavros Sachtouris
            hashmap=True,
630 3dabe5d2 Stavros Sachtouris
            content_type=content_type,
631 5c2058e7 Stavros Sachtouris
            content_encoding=content_encoding,
632 e9ac514e Stavros Sachtouris
            if_etag_match=if_etag_match,
633 524d9cdd Stavros Sachtouris
            if_etag_not_match='*' if if_not_exist else None,
634 524d9cdd Stavros Sachtouris
            etag=etag,
635 3dabe5d2 Stavros Sachtouris
            json=hashmap,
636 50165863 Stavros Sachtouris
            permissions=sharing,
637 50165863 Stavros Sachtouris
            public=public,
638 3dabe5d2 Stavros Sachtouris
            success=201)
639 3c216009 Stavros Sachtouris
        return r.headers
640 3dabe5d2 Stavros Sachtouris
641 f27ed9a0 Stavros Sachtouris
    # download_* auxiliary methods
642 fbfee225 Stavros Sachtouris
    def _get_remote_blocks_info(self, obj, **restargs):
643 56f0908a Stavros Sachtouris
        #retrieve object hashmap
644 3dabe5d2 Stavros Sachtouris
        myrange = restargs.pop('data_range', None)
645 319be41b Stavros Sachtouris
        hashmap = self.get_object_hashmap(obj, **restargs)
646 642f1bbd Stavros Sachtouris
        restargs['data_range'] = myrange
647 56f0908a Stavros Sachtouris
        blocksize = int(hashmap['block_size'])
648 56f0908a Stavros Sachtouris
        blockhash = hashmap['block_hash']
649 56f0908a Stavros Sachtouris
        total_size = hashmap['bytes']
650 fbfee225 Stavros Sachtouris
        #assert total_size/blocksize + 1 == len(hashmap['hashes'])
651 56f0908a Stavros Sachtouris
        map_dict = {}
652 fbfee225 Stavros Sachtouris
        for i, h in enumerate(hashmap['hashes']):
653 ca7f78c0 Stavros Sachtouris
            #  map_dict[h] = i   CHAGE
654 ca7f78c0 Stavros Sachtouris
            if h in map_dict:
655 ca7f78c0 Stavros Sachtouris
                map_dict[h].append(i)
656 ca7f78c0 Stavros Sachtouris
            else:
657 ca7f78c0 Stavros Sachtouris
                map_dict[h] = [i]
658 fbfee225 Stavros Sachtouris
        return (blocksize, blockhash, total_size, hashmap['hashes'], map_dict)
659 64ab4c13 Stavros Sachtouris
660 24ff0a35 Stavros Sachtouris
    def _dump_blocks_sync(
661 7806f19d Stavros Sachtouris
            self, obj, remote_hashes, blocksize, total_size, dst, crange,
662 24ff0a35 Stavros Sachtouris
            **args):
663 fbfee225 Stavros Sachtouris
        for blockid, blockhash in enumerate(remote_hashes):
664 24ff0a35 Stavros Sachtouris
            if blockhash:
665 24ff0a35 Stavros Sachtouris
                start = blocksize * blockid
666 24ff0a35 Stavros Sachtouris
                is_last = start + blocksize > total_size
667 24ff0a35 Stavros Sachtouris
                end = (total_size - 1) if is_last else (start + blocksize - 1)
668 776b275c Stavros Sachtouris
                data_range = _range_up(start, end, total_size, crange)
669 776b275c Stavros Sachtouris
                if not data_range:
670 776b275c Stavros Sachtouris
                    self._cb_next()
671 776b275c Stavros Sachtouris
                    continue
672 776b275c Stavros Sachtouris
                args['data_range'] = 'bytes=%s' % data_range
673 24ff0a35 Stavros Sachtouris
                r = self.object_get(obj, success=(200, 206), **args)
674 24ff0a35 Stavros Sachtouris
                self._cb_next()
675 24ff0a35 Stavros Sachtouris
                dst.write(r.content)
676 24ff0a35 Stavros Sachtouris
                dst.flush()
677 fbfee225 Stavros Sachtouris
678 24ff0a35 Stavros Sachtouris
    def _get_block_async(self, obj, **args):
679 24ff0a35 Stavros Sachtouris
        event = SilentEvent(self.object_get, obj, success=(200, 206), **args)
680 e02728f9 Stavros Sachtouris
        event.start()
681 e02728f9 Stavros Sachtouris
        return event
682 fb0cd49a Stavros Sachtouris
683 48c3782c Stavros Sachtouris
    def _hash_from_file(self, fp, start, size, blockhash):
684 48c3782c Stavros Sachtouris
        fp.seek(start)
685 48c3782c Stavros Sachtouris
        block = fp.read(size)
686 48c3782c Stavros Sachtouris
        h = newhashlib(blockhash)
687 48c3782c Stavros Sachtouris
        h.update(block.strip('\x00'))
688 48c3782c Stavros Sachtouris
        return hexlify(h.digest())
689 48c3782c Stavros Sachtouris
690 28cbc3c2 Stavros Sachtouris
    def _thread2file(self, flying, blockids, local_file, offset=0, **restargs):
691 22fc09fb Stavros Sachtouris
        """write the results of a greenleted rest call to a file
692 f5f2dc53 Stavros Sachtouris

693 f5f2dc53 Stavros Sachtouris
        :param offset: the offset of the file up to blocksize
694 f5f2dc53 Stavros Sachtouris
        - e.g. if the range is 10-100, all blocks will be written to
695 f5f2dc53 Stavros Sachtouris
        normal_position - 10
696 f5f2dc53 Stavros Sachtouris
        """
697 0fbc8a52 Stavros Sachtouris
        for key, g in flying.items():
698 28cbc3c2 Stavros Sachtouris
            if g.isAlive():
699 28cbc3c2 Stavros Sachtouris
                continue
700 28cbc3c2 Stavros Sachtouris
            if g.exception:
701 28cbc3c2 Stavros Sachtouris
                raise g.exception
702 28cbc3c2 Stavros Sachtouris
            block = g.value.content
703 28cbc3c2 Stavros Sachtouris
            for block_start in blockids[key]:
704 28cbc3c2 Stavros Sachtouris
                local_file.seek(block_start + offset)
705 fbfee225 Stavros Sachtouris
                local_file.write(block)
706 fbfee225 Stavros Sachtouris
                self._cb_next()
707 28cbc3c2 Stavros Sachtouris
            flying.pop(key)
708 28cbc3c2 Stavros Sachtouris
            blockids.pop(key)
709 fbfee225 Stavros Sachtouris
        local_file.flush()
710 fbfee225 Stavros Sachtouris
711 24ff0a35 Stavros Sachtouris
    def _dump_blocks_async(
712 24ff0a35 Stavros Sachtouris
            self, obj, remote_hashes, blocksize, total_size, local_file,
713 24ff0a35 Stavros Sachtouris
            blockhash=None, resume=False, filerange=None, **restargs):
714 973fbcad Stavros Sachtouris
        file_size = fstat(local_file.fileno()).st_size if resume else 0
715 28cbc3c2 Stavros Sachtouris
        flying = dict()
716 28cbc3c2 Stavros Sachtouris
        blockid_dict = dict()
717 22fc09fb Stavros Sachtouris
        offset = 0
718 f27ed9a0 Stavros Sachtouris
719 cad39033 Stavros Sachtouris
        self._init_thread_limit()
720 ca7f78c0 Stavros Sachtouris
        for block_hash, blockids in remote_hashes.items():
721 28cbc3c2 Stavros Sachtouris
            blockids = [blk * blocksize for blk in blockids]
722 28cbc3c2 Stavros Sachtouris
            unsaved = [blk for blk in blockids if not (
723 28cbc3c2 Stavros Sachtouris
                blk < file_size and block_hash == self._hash_from_file(
724 28cbc3c2 Stavros Sachtouris
                        local_file, blk, blocksize, blockhash))]
725 28cbc3c2 Stavros Sachtouris
            self._cb_next(len(blockids) - len(unsaved))
726 28cbc3c2 Stavros Sachtouris
            if unsaved:
727 28cbc3c2 Stavros Sachtouris
                key = unsaved[0]
728 ca7f78c0 Stavros Sachtouris
                self._watch_thread_limit(flying.values())
729 28cbc3c2 Stavros Sachtouris
                self._thread2file(
730 28cbc3c2 Stavros Sachtouris
                    flying, blockid_dict, local_file, offset,
731 ca7f78c0 Stavros Sachtouris
                    **restargs)
732 0fbc8a52 Stavros Sachtouris
                end = total_size - 1 if (
733 0fbc8a52 Stavros Sachtouris
                    key + blocksize > total_size) else key + blocksize - 1
734 776b275c Stavros Sachtouris
                data_range = _range_up(key, end, total_size, filerange)
735 776b275c Stavros Sachtouris
                if not data_range:
736 ca7f78c0 Stavros Sachtouris
                    self._cb_next()
737 ca7f78c0 Stavros Sachtouris
                    continue
738 776b275c Stavros Sachtouris
                restargs['async_headers'] = {'Range': 'bytes=%s' % data_range}
739 28cbc3c2 Stavros Sachtouris
                flying[key] = self._get_block_async(obj, **restargs)
740 28cbc3c2 Stavros Sachtouris
                blockid_dict[key] = unsaved
741 fbfee225 Stavros Sachtouris
742 e02728f9 Stavros Sachtouris
        for thread in flying.values():
743 e02728f9 Stavros Sachtouris
            thread.join()
744 28cbc3c2 Stavros Sachtouris
        self._thread2file(flying, blockid_dict, local_file, offset, **restargs)
745 fbfee225 Stavros Sachtouris
746 24ff0a35 Stavros Sachtouris
    def download_object(
747 24ff0a35 Stavros Sachtouris
            self, obj, dst,
748 24ff0a35 Stavros Sachtouris
            download_cb=None,
749 24ff0a35 Stavros Sachtouris
            version=None,
750 24ff0a35 Stavros Sachtouris
            resume=False,
751 24ff0a35 Stavros Sachtouris
            range_str=None,
752 24ff0a35 Stavros Sachtouris
            if_match=None,
753 24ff0a35 Stavros Sachtouris
            if_none_match=None,
754 24ff0a35 Stavros Sachtouris
            if_modified_since=None,
755 24ff0a35 Stavros Sachtouris
            if_unmodified_since=None):
756 f5f2dc53 Stavros Sachtouris
        """Download an object (multiple connections, random blocks)
757 4375e020 Stavros Sachtouris

758 4375e020 Stavros Sachtouris
        :param obj: (str) remote object path
759 4375e020 Stavros Sachtouris

760 4375e020 Stavros Sachtouris
        :param dst: open file descriptor (wb+)
761 4375e020 Stavros Sachtouris

762 4375e020 Stavros Sachtouris
        :param download_cb: optional progress.bar object for downloading
763 4375e020 Stavros Sachtouris

764 4375e020 Stavros Sachtouris
        :param version: (str) file version
765 4375e020 Stavros Sachtouris

766 4375e020 Stavros Sachtouris
        :param resume: (bool) if set, preserve already downloaded file parts
767 4375e020 Stavros Sachtouris

768 f5f2dc53 Stavros Sachtouris
        :param range_str: (str) from, to are file positions (int) in bytes
769 4375e020 Stavros Sachtouris

770 4375e020 Stavros Sachtouris
        :param if_match: (str)
771 4375e020 Stavros Sachtouris

772 4375e020 Stavros Sachtouris
        :param if_none_match: (str)
773 4375e020 Stavros Sachtouris

774 4375e020 Stavros Sachtouris
        :param if_modified_since: (str) formated date
775 4375e020 Stavros Sachtouris

776 f5f2dc53 Stavros Sachtouris
        :param if_unmodified_since: (str) formated date"""
777 24ff0a35 Stavros Sachtouris
        restargs = dict(
778 24ff0a35 Stavros Sachtouris
            version=version,
779 24ff0a35 Stavros Sachtouris
            data_range=None if range_str is None else 'bytes=%s' % range_str,
780 fbfee225 Stavros Sachtouris
            if_match=if_match,
781 fbfee225 Stavros Sachtouris
            if_none_match=if_none_match,
782 fbfee225 Stavros Sachtouris
            if_modified_since=if_modified_since,
783 fbfee225 Stavros Sachtouris
            if_unmodified_since=if_unmodified_since)
784 fbfee225 Stavros Sachtouris
785 24ff0a35 Stavros Sachtouris
        (
786 24ff0a35 Stavros Sachtouris
            blocksize,
787 fbfee225 Stavros Sachtouris
            blockhash,
788 fbfee225 Stavros Sachtouris
            total_size,
789 3dabe5d2 Stavros Sachtouris
            hash_list,
790 fbfee225 Stavros Sachtouris
            remote_hashes) = self._get_remote_blocks_info(obj, **restargs)
791 fbfee225 Stavros Sachtouris
        assert total_size >= 0
792 fbfee225 Stavros Sachtouris
793 fbfee225 Stavros Sachtouris
        if download_cb:
794 ca7f78c0 Stavros Sachtouris
            self.progress_bar_gen = download_cb(len(hash_list))
795 fbfee225 Stavros Sachtouris
            self._cb_next()
796 fbfee225 Stavros Sachtouris
797 fbfee225 Stavros Sachtouris
        if dst.isatty():
798 24ff0a35 Stavros Sachtouris
            self._dump_blocks_sync(
799 24ff0a35 Stavros Sachtouris
                obj,
800 3dabe5d2 Stavros Sachtouris
                hash_list,
801 3dabe5d2 Stavros Sachtouris
                blocksize,
802 3dabe5d2 Stavros Sachtouris
                total_size,
803 3dabe5d2 Stavros Sachtouris
                dst,
804 24ff0a35 Stavros Sachtouris
                range_str,
805 3dabe5d2 Stavros Sachtouris
                **restargs)
806 699d3bb1 Stavros Sachtouris
        else:
807 24ff0a35 Stavros Sachtouris
            self._dump_blocks_async(
808 24ff0a35 Stavros Sachtouris
                obj,
809 3dabe5d2 Stavros Sachtouris
                remote_hashes,
810 3dabe5d2 Stavros Sachtouris
                blocksize,
811 3dabe5d2 Stavros Sachtouris
                total_size,
812 3dabe5d2 Stavros Sachtouris
                dst,
813 3dabe5d2 Stavros Sachtouris
                blockhash,
814 3dabe5d2 Stavros Sachtouris
                resume,
815 24ff0a35 Stavros Sachtouris
                range_str,
816 3dabe5d2 Stavros Sachtouris
                **restargs)
817 24ff0a35 Stavros Sachtouris
            if not range_str:
818 22fc09fb Stavros Sachtouris
                dst.truncate(total_size)
819 5b263ba2 Stavros Sachtouris
820 fbfee225 Stavros Sachtouris
        self._complete_cb()
821 fbfee225 Stavros Sachtouris
822 49cc29b2 Stavros Sachtouris
    def download_to_string(
823 49cc29b2 Stavros Sachtouris
            self, obj,
824 49cc29b2 Stavros Sachtouris
            download_cb=None,
825 49cc29b2 Stavros Sachtouris
            version=None,
826 49cc29b2 Stavros Sachtouris
            range_str=None,
827 49cc29b2 Stavros Sachtouris
            if_match=None,
828 49cc29b2 Stavros Sachtouris
            if_none_match=None,
829 49cc29b2 Stavros Sachtouris
            if_modified_since=None,
830 49cc29b2 Stavros Sachtouris
            if_unmodified_since=None):
831 0fbc8a52 Stavros Sachtouris
        """Download an object to a string (multiple connections). This method
832 0fbc8a52 Stavros Sachtouris
        uses threads for http requests, but stores all content in memory.
833 49cc29b2 Stavros Sachtouris

834 49cc29b2 Stavros Sachtouris
        :param obj: (str) remote object path
835 49cc29b2 Stavros Sachtouris

836 49cc29b2 Stavros Sachtouris
        :param download_cb: optional progress.bar object for downloading
837 49cc29b2 Stavros Sachtouris

838 49cc29b2 Stavros Sachtouris
        :param version: (str) file version
839 49cc29b2 Stavros Sachtouris

840 49cc29b2 Stavros Sachtouris
        :param range_str: (str) from, to are file positions (int) in bytes
841 49cc29b2 Stavros Sachtouris

842 49cc29b2 Stavros Sachtouris
        :param if_match: (str)
843 49cc29b2 Stavros Sachtouris

844 49cc29b2 Stavros Sachtouris
        :param if_none_match: (str)
845 49cc29b2 Stavros Sachtouris

846 49cc29b2 Stavros Sachtouris
        :param if_modified_since: (str) formated date
847 49cc29b2 Stavros Sachtouris

848 49cc29b2 Stavros Sachtouris
        :param if_unmodified_since: (str) formated date
849 49cc29b2 Stavros Sachtouris

850 49cc29b2 Stavros Sachtouris
        :returns: (str) the whole object contents
851 49cc29b2 Stavros Sachtouris
        """
852 49cc29b2 Stavros Sachtouris
        restargs = dict(
853 49cc29b2 Stavros Sachtouris
            version=version,
854 49cc29b2 Stavros Sachtouris
            data_range=None if range_str is None else 'bytes=%s' % range_str,
855 49cc29b2 Stavros Sachtouris
            if_match=if_match,
856 49cc29b2 Stavros Sachtouris
            if_none_match=if_none_match,
857 49cc29b2 Stavros Sachtouris
            if_modified_since=if_modified_since,
858 49cc29b2 Stavros Sachtouris
            if_unmodified_since=if_unmodified_since)
859 49cc29b2 Stavros Sachtouris
860 49cc29b2 Stavros Sachtouris
        (
861 49cc29b2 Stavros Sachtouris
            blocksize,
862 49cc29b2 Stavros Sachtouris
            blockhash,
863 49cc29b2 Stavros Sachtouris
            total_size,
864 49cc29b2 Stavros Sachtouris
            hash_list,
865 49cc29b2 Stavros Sachtouris
            remote_hashes) = self._get_remote_blocks_info(obj, **restargs)
866 49cc29b2 Stavros Sachtouris
        assert total_size >= 0
867 49cc29b2 Stavros Sachtouris
868 49cc29b2 Stavros Sachtouris
        if download_cb:
869 49cc29b2 Stavros Sachtouris
            self.progress_bar_gen = download_cb(len(hash_list))
870 49cc29b2 Stavros Sachtouris
            self._cb_next()
871 49cc29b2 Stavros Sachtouris
872 0fbc8a52 Stavros Sachtouris
        num_of_blocks = len(remote_hashes)
873 0fbc8a52 Stavros Sachtouris
        ret = [''] * num_of_blocks
874 0fbc8a52 Stavros Sachtouris
        self._init_thread_limit()
875 0fbc8a52 Stavros Sachtouris
        flying = dict()
876 5655d560 Stavros Sachtouris
        try:
877 5655d560 Stavros Sachtouris
            for blockid, blockhash in enumerate(remote_hashes):
878 5655d560 Stavros Sachtouris
                start = blocksize * blockid
879 5655d560 Stavros Sachtouris
                is_last = start + blocksize > total_size
880 5655d560 Stavros Sachtouris
                end = (total_size - 1) if is_last else (start + blocksize - 1)
881 776b275c Stavros Sachtouris
                data_range_str = _range_up(start, end, end, range_str)
882 776b275c Stavros Sachtouris
                if data_range_str:
883 5655d560 Stavros Sachtouris
                    self._watch_thread_limit(flying.values())
884 776b275c Stavros Sachtouris
                    restargs['data_range'] = 'bytes=%s' % data_range_str
885 5655d560 Stavros Sachtouris
                    flying[blockid] = self._get_block_async(obj, **restargs)
886 5655d560 Stavros Sachtouris
                for runid, thread in flying.items():
887 5655d560 Stavros Sachtouris
                    if (blockid + 1) == num_of_blocks:
888 5655d560 Stavros Sachtouris
                        thread.join()
889 5655d560 Stavros Sachtouris
                    elif thread.isAlive():
890 5655d560 Stavros Sachtouris
                        continue
891 5655d560 Stavros Sachtouris
                    if thread.exception:
892 5655d560 Stavros Sachtouris
                        raise thread.exception
893 5655d560 Stavros Sachtouris
                    ret[runid] = thread.value.content
894 5655d560 Stavros Sachtouris
                    self._cb_next()
895 5655d560 Stavros Sachtouris
                    flying.pop(runid)
896 5655d560 Stavros Sachtouris
            return ''.join(ret)
897 5655d560 Stavros Sachtouris
        except KeyboardInterrupt:
898 5655d560 Stavros Sachtouris
            sendlog.info('- - - wait for threads to finish')
899 5655d560 Stavros Sachtouris
            for thread in activethreads():
900 5655d560 Stavros Sachtouris
                thread.join()
901 49cc29b2 Stavros Sachtouris
902 fbfee225 Stavros Sachtouris
    #Command Progress Bar method
903 28cbc3c2 Stavros Sachtouris
    def _cb_next(self, step=1):
904 fbfee225 Stavros Sachtouris
        if hasattr(self, 'progress_bar_gen'):
905 fbfee225 Stavros Sachtouris
            try:
906 e5c76b1a Stavros Sachtouris
                for i in xrange(step):
907 e5c76b1a Stavros Sachtouris
                    self.progress_bar_gen.next()
908 fbfee225 Stavros Sachtouris
            except:
909 fbfee225 Stavros Sachtouris
                pass
910 3dabe5d2 Stavros Sachtouris
911 fbfee225 Stavros Sachtouris
    def _complete_cb(self):
912 fbfee225 Stavros Sachtouris
        while True:
913 fbfee225 Stavros Sachtouris
            try:
914 e5c76b1a Stavros Sachtouris
                self.progress_bar_gen.next()
915 fbfee225 Stavros Sachtouris
            except:
916 fbfee225 Stavros Sachtouris
                break
917 b1713259 Stavros Sachtouris
918 24ff0a35 Stavros Sachtouris
    def get_object_hashmap(
919 24ff0a35 Stavros Sachtouris
            self, obj,
920 24ff0a35 Stavros Sachtouris
            version=None,
921 24ff0a35 Stavros Sachtouris
            if_match=None,
922 24ff0a35 Stavros Sachtouris
            if_none_match=None,
923 24ff0a35 Stavros Sachtouris
            if_modified_since=None,
924 776b275c Stavros Sachtouris
            if_unmodified_since=None):
925 4375e020 Stavros Sachtouris
        """
926 4375e020 Stavros Sachtouris
        :param obj: (str) remote object path
927 4375e020 Stavros Sachtouris

928 4375e020 Stavros Sachtouris
        :param if_match: (str)
929 4375e020 Stavros Sachtouris

930 4375e020 Stavros Sachtouris
        :param if_none_match: (str)
931 4375e020 Stavros Sachtouris

932 4375e020 Stavros Sachtouris
        :param if_modified_since: (str) formated date
933 4375e020 Stavros Sachtouris

934 4375e020 Stavros Sachtouris
        :param if_unmodified_since: (str) formated date
935 4375e020 Stavros Sachtouris

936 4375e020 Stavros Sachtouris
        :returns: (list)
937 4375e020 Stavros Sachtouris
        """
938 d804de82 Stavros Sachtouris
        try:
939 24ff0a35 Stavros Sachtouris
            r = self.object_get(
940 24ff0a35 Stavros Sachtouris
                obj,
941 3dabe5d2 Stavros Sachtouris
                hashmap=True,
942 3dabe5d2 Stavros Sachtouris
                version=version,
943 3dabe5d2 Stavros Sachtouris
                if_etag_match=if_match,
944 3dabe5d2 Stavros Sachtouris
                if_etag_not_match=if_none_match,
945 3dabe5d2 Stavros Sachtouris
                if_modified_since=if_modified_since,
946 776b275c Stavros Sachtouris
                if_unmodified_since=if_unmodified_since)
947 d804de82 Stavros Sachtouris
        except ClientError as err:
948 d804de82 Stavros Sachtouris
            if err.status == 304 or err.status == 412:
949 d804de82 Stavros Sachtouris
                return {}
950 d804de82 Stavros Sachtouris
            raise
951 64ab4c13 Stavros Sachtouris
        return r.json
952 56f0908a Stavros Sachtouris
953 3a9e54b0 Stavros Sachtouris
    def set_account_group(self, group, usernames):
954 4375e020 Stavros Sachtouris
        """
955 4375e020 Stavros Sachtouris
        :param group: (str)
956 4375e020 Stavros Sachtouris

957 4375e020 Stavros Sachtouris
        :param usernames: (list)
958 4375e020 Stavros Sachtouris
        """
959 915b99b5 Stavros Sachtouris
        r = self.account_post(update=True, groups={group: usernames})
960 915b99b5 Stavros Sachtouris
        return r
961 eb903ba7 Stavros Sachtouris
962 c2867610 Stavros Sachtouris
    def del_account_group(self, group):
963 4375e020 Stavros Sachtouris
        """
964 4375e020 Stavros Sachtouris
        :param group: (str)
965 4375e020 Stavros Sachtouris
        """
966 c2b5da2f Stavros Sachtouris
        self.account_post(update=True, groups={group: []})
967 c2867610 Stavros Sachtouris
968 8af4cc0b Stavros Sachtouris
    def get_account_info(self, until=None):
969 4375e020 Stavros Sachtouris
        """
970 4375e020 Stavros Sachtouris
        :param until: (str) formated date
971 4375e020 Stavros Sachtouris

972 4375e020 Stavros Sachtouris
        :returns: (dict)
973 4375e020 Stavros Sachtouris
        """
974 8af4cc0b Stavros Sachtouris
        r = self.account_head(until=until)
975 6657ec8c Stavros Sachtouris
        if r.status_code == 401:
976 24851aa5 Stavros Sachtouris
            raise ClientError("No authorization", status=401)
977 64ab4c13 Stavros Sachtouris
        return r.headers
978 6657ec8c Stavros Sachtouris
979 d1856abf Stavros Sachtouris
    def get_account_quota(self):
980 4375e020 Stavros Sachtouris
        """
981 4375e020 Stavros Sachtouris
        :returns: (dict)
982 4375e020 Stavros Sachtouris
        """
983 24ff0a35 Stavros Sachtouris
        return filter_in(
984 24ff0a35 Stavros Sachtouris
            self.get_account_info(),
985 3dabe5d2 Stavros Sachtouris
            'X-Account-Policy-Quota',
986 3dabe5d2 Stavros Sachtouris
            exactMatch=True)
987 d1856abf Stavros Sachtouris
988 df0045d8 Stavros Sachtouris
    #def get_account_versioning(self):
989 df0045d8 Stavros Sachtouris
    #    """
990 df0045d8 Stavros Sachtouris
    #    :returns: (dict)
991 df0045d8 Stavros Sachtouris
    #    """
992 df0045d8 Stavros Sachtouris
    #    return filter_in(
993 df0045d8 Stavros Sachtouris
    #        self.get_account_info(),
994 df0045d8 Stavros Sachtouris
    #        'X-Account-Policy-Versioning',
995 df0045d8 Stavros Sachtouris
    #        exactMatch=True)
996 e6b39366 Stavros Sachtouris
997 8af4cc0b Stavros Sachtouris
    def get_account_meta(self, until=None):
998 4375e020 Stavros Sachtouris
        """
999 8f91e50c Stavros Sachtouris
        :param until: (str) formated date
1000 4375e020 Stavros Sachtouris

1001 4375e020 Stavros Sachtouris
        :returns: (dict)
1002 4375e020 Stavros Sachtouris
        """
1003 3dabe5d2 Stavros Sachtouris
        return filter_in(self.get_account_info(until=until), 'X-Account-Meta-')
1004 af3b2b36 Stavros Sachtouris
1005 c2867610 Stavros Sachtouris
    def get_account_group(self):
1006 4375e020 Stavros Sachtouris
        """
1007 4375e020 Stavros Sachtouris
        :returns: (dict)
1008 4375e020 Stavros Sachtouris
        """
1009 c2867610 Stavros Sachtouris
        return filter_in(self.get_account_info(), 'X-Account-Group-')
1010 c2867610 Stavros Sachtouris
1011 af3b2b36 Stavros Sachtouris
    def set_account_meta(self, metapairs):
1012 4375e020 Stavros Sachtouris
        """
1013 4375e020 Stavros Sachtouris
        :param metapairs: (dict) {key1:val1, key2:val2, ...}
1014 4375e020 Stavros Sachtouris
        """
1015 af3b2b36 Stavros Sachtouris
        assert(type(metapairs) is dict)
1016 915b99b5 Stavros Sachtouris
        r = self.account_post(update=True, metadata=metapairs)
1017 915b99b5 Stavros Sachtouris
        return r.headers
1018 af3b2b36 Stavros Sachtouris
1019 379cd4bb Stavros Sachtouris
    def del_account_meta(self, metakey):
1020 4375e020 Stavros Sachtouris
        """
1021 4375e020 Stavros Sachtouris
        :param metakey: (str) metadatum key
1022 4375e020 Stavros Sachtouris
        """
1023 915b99b5 Stavros Sachtouris
        r = self.account_post(update=True, metadata={metakey: ''})
1024 915b99b5 Stavros Sachtouris
        return r.headers
1025 379cd4bb Stavros Sachtouris
1026 df0045d8 Stavros Sachtouris
    #def set_account_quota(self, quota):
1027 df0045d8 Stavros Sachtouris
    #    """
1028 df0045d8 Stavros Sachtouris
    #    :param quota: (int)
1029 df0045d8 Stavros Sachtouris
    #    """
1030 df0045d8 Stavros Sachtouris
    #    self.account_post(update=True, quota=quota)
1031 df0045d8 Stavros Sachtouris
1032 df0045d8 Stavros Sachtouris
    #def set_account_versioning(self, versioning):
1033 df0045d8 Stavros Sachtouris
    #    """
1034 df0045d8 Stavros Sachtouris
    #    :param versioning: (str)
1035 df0045d8 Stavros Sachtouris
    #    """
1036 df0045d8 Stavros Sachtouris
    #    r = self.account_post(update=True, versioning=versioning)
1037 df0045d8 Stavros Sachtouris
    #    return r.headers
1038 d1856abf Stavros Sachtouris
1039 4fd88feb Stavros Sachtouris
    def list_containers(self):
1040 4375e020 Stavros Sachtouris
        """
1041 4375e020 Stavros Sachtouris
        :returns: (dict)
1042 4375e020 Stavros Sachtouris
        """
1043 4fd88feb Stavros Sachtouris
        r = self.account_get()
1044 64ab4c13 Stavros Sachtouris
        return r.json
1045 b758e547 Stavros Sachtouris
1046 a298f2ab Stavros Sachtouris
    def del_container(self, until=None, delimiter=None):
1047 4375e020 Stavros Sachtouris
        """
1048 4375e020 Stavros Sachtouris
        :param until: (str) formated date
1049 4375e020 Stavros Sachtouris

1050 4c1882ab Stavros Sachtouris
        :param delimiter: (str) with / empty container
1051 4375e020 Stavros Sachtouris

1052 4375e020 Stavros Sachtouris
        :raises ClientError: 404 Container does not exist
1053 4375e020 Stavros Sachtouris

1054 4375e020 Stavros Sachtouris
        :raises ClientError: 409 Container is not empty
1055 4375e020 Stavros Sachtouris
        """
1056 277ca4ed Dionysis Zindros
        self._assert_container()
1057 2005b18e Stavros Sachtouris
        r = self.container_delete(
1058 2005b18e Stavros Sachtouris
            until=until,
1059 3dabe5d2 Stavros Sachtouris
            delimiter=delimiter,
1060 3dabe5d2 Stavros Sachtouris
            success=(204, 404, 409))
1061 a298f2ab Stavros Sachtouris
        if r.status_code == 404:
1062 24ff0a35 Stavros Sachtouris
            raise ClientError(
1063 24ff0a35 Stavros Sachtouris
                'Container "%s" does not exist' % self.container,
1064 3dabe5d2 Stavros Sachtouris
                r.status_code)
1065 a298f2ab Stavros Sachtouris
        elif r.status_code == 409:
1066 24ff0a35 Stavros Sachtouris
            raise ClientError(
1067 24ff0a35 Stavros Sachtouris
                'Container "%s" is not empty' % self.container,
1068 3dabe5d2 Stavros Sachtouris
                r.status_code)
1069 5655d560 Stavros Sachtouris
        return r.headers
1070 a298f2ab Stavros Sachtouris
1071 bae347d3 Stavros Sachtouris
    def get_container_versioning(self, container=None):
1072 4375e020 Stavros Sachtouris
        """
1073 4375e020 Stavros Sachtouris
        :param container: (str)
1074 4375e020 Stavros Sachtouris

1075 4375e020 Stavros Sachtouris
        :returns: (dict)
1076 4375e020 Stavros Sachtouris
        """
1077 bae347d3 Stavros Sachtouris
        cnt_back_up = self.container
1078 bae347d3 Stavros Sachtouris
        try:
1079 bae347d3 Stavros Sachtouris
            self.container = container or cnt_back_up
1080 bae347d3 Stavros Sachtouris
            return filter_in(
1081 bae347d3 Stavros Sachtouris
                self.get_container_info(),
1082 bae347d3 Stavros Sachtouris
                'X-Container-Policy-Versioning')
1083 bae347d3 Stavros Sachtouris
        finally:
1084 bae347d3 Stavros Sachtouris
            self.container = cnt_back_up
1085 d1856abf Stavros Sachtouris
1086 3ed6dbde Stavros Sachtouris
    def get_container_limit(self, container=None):
1087 4375e020 Stavros Sachtouris
        """
1088 4375e020 Stavros Sachtouris
        :param container: (str)
1089 4375e020 Stavros Sachtouris

1090 4375e020 Stavros Sachtouris
        :returns: (dict)
1091 4375e020 Stavros Sachtouris
        """
1092 1879e310 Stavros Sachtouris
        cnt_back_up = self.container
1093 1879e310 Stavros Sachtouris
        try:
1094 1879e310 Stavros Sachtouris
            self.container = container or cnt_back_up
1095 1879e310 Stavros Sachtouris
            return filter_in(
1096 1879e310 Stavros Sachtouris
                self.get_container_info(),
1097 1879e310 Stavros Sachtouris
                'X-Container-Policy-Quota')
1098 1879e310 Stavros Sachtouris
        finally:
1099 1879e310 Stavros Sachtouris
            self.container = cnt_back_up
1100 e6b39366 Stavros Sachtouris
1101 3dabe5d2 Stavros Sachtouris
    def get_container_info(self, until=None):
1102 4375e020 Stavros Sachtouris
        """
1103 4375e020 Stavros Sachtouris
        :param until: (str) formated date
1104 4375e020 Stavros Sachtouris

1105 4375e020 Stavros Sachtouris
        :returns: (dict)
1106 f91bc6b1 Stavros Sachtouris

1107 f91bc6b1 Stavros Sachtouris
        :raises ClientError: 404 Container not found
1108 4375e020 Stavros Sachtouris
        """
1109 f91bc6b1 Stavros Sachtouris
        try:
1110 f91bc6b1 Stavros Sachtouris
            r = self.container_head(until=until)
1111 f91bc6b1 Stavros Sachtouris
        except ClientError as err:
1112 f91bc6b1 Stavros Sachtouris
            err.details.append('for container %s' % self.container)
1113 f91bc6b1 Stavros Sachtouris
            raise err
1114 8af4cc0b Stavros Sachtouris
        return r.headers
1115 8af4cc0b Stavros Sachtouris
1116 3dabe5d2 Stavros Sachtouris
    def get_container_meta(self, until=None):
1117 4375e020 Stavros Sachtouris
        """
1118 4375e020 Stavros Sachtouris
        :param until: (str) formated date
1119 4375e020 Stavros Sachtouris

1120 4375e020 Stavros Sachtouris
        :returns: (dict)
1121 4375e020 Stavros Sachtouris
        """
1122 24ff0a35 Stavros Sachtouris
        return filter_in(
1123 24ff0a35 Stavros Sachtouris
            self.get_container_info(until=until),
1124 3dabe5d2 Stavros Sachtouris
            'X-Container-Meta')
1125 e6b39366 Stavros Sachtouris
1126 3dabe5d2 Stavros Sachtouris
    def get_container_object_meta(self, until=None):
1127 4375e020 Stavros Sachtouris
        """
1128 4375e020 Stavros Sachtouris
        :param until: (str) formated date
1129 4375e020 Stavros Sachtouris

1130 4375e020 Stavros Sachtouris
        :returns: (dict)
1131 4375e020 Stavros Sachtouris
        """
1132 24ff0a35 Stavros Sachtouris
        return filter_in(
1133 24ff0a35 Stavros Sachtouris
            self.get_container_info(until=until),
1134 3dabe5d2 Stavros Sachtouris
            'X-Container-Object-Meta')
1135 e6b39366 Stavros Sachtouris
1136 af3b2b36 Stavros Sachtouris
    def set_container_meta(self, metapairs):
1137 4375e020 Stavros Sachtouris
        """
1138 4375e020 Stavros Sachtouris
        :param metapairs: (dict) {key1:val1, key2:val2, ...}
1139 4375e020 Stavros Sachtouris
        """
1140 af3b2b36 Stavros Sachtouris
        assert(type(metapairs) is dict)
1141 915b99b5 Stavros Sachtouris
        r = self.container_post(update=True, metadata=metapairs)
1142 915b99b5 Stavros Sachtouris
        return r.headers
1143 3dabe5d2 Stavros Sachtouris
1144 3e544e5b Stavros Sachtouris
    def del_container_meta(self, metakey):
1145 4375e020 Stavros Sachtouris
        """
1146 4375e020 Stavros Sachtouris
        :param metakey: (str) metadatum key
1147 545c6c29 Stavros Sachtouris

1148 545c6c29 Stavros Sachtouris
        :returns: (dict) response headers
1149 4375e020 Stavros Sachtouris
        """
1150 915b99b5 Stavros Sachtouris
        r = self.container_post(update=True, metadata={metakey: ''})
1151 915b99b5 Stavros Sachtouris
        return r.headers
1152 e6b39366 Stavros Sachtouris
1153 326a79b9 Stavros Sachtouris
    def set_container_limit(self, limit):
1154 4375e020 Stavros Sachtouris
        """
1155 326a79b9 Stavros Sachtouris
        :param limit: (int)
1156 4375e020 Stavros Sachtouris
        """
1157 915b99b5 Stavros Sachtouris
        r = self.container_post(update=True, quota=limit)
1158 915b99b5 Stavros Sachtouris
        return r.headers
1159 d1856abf Stavros Sachtouris
1160 d1856abf Stavros Sachtouris
    def set_container_versioning(self, versioning):
1161 4375e020 Stavros Sachtouris
        """
1162 4375e020 Stavros Sachtouris
        :param versioning: (str)
1163 4375e020 Stavros Sachtouris
        """
1164 915b99b5 Stavros Sachtouris
        r = self.container_post(update=True, versioning=versioning)
1165 915b99b5 Stavros Sachtouris
        return r.headers
1166 d1856abf Stavros Sachtouris
1167 a298f2ab Stavros Sachtouris
    def del_object(self, obj, until=None, delimiter=None):
1168 4375e020 Stavros Sachtouris
        """
1169 4375e020 Stavros Sachtouris
        :param obj: (str) remote object path
1170 4375e020 Stavros Sachtouris

1171 4375e020 Stavros Sachtouris
        :param until: (str) formated date
1172 4375e020 Stavros Sachtouris

1173 4375e020 Stavros Sachtouris
        :param delimiter: (str)
1174 4375e020 Stavros Sachtouris
        """
1175 277ca4ed Dionysis Zindros
        self._assert_container()
1176 5655d560 Stavros Sachtouris
        r = self.object_delete(obj, until=until, delimiter=delimiter)
1177 5655d560 Stavros Sachtouris
        return r.headers
1178 a298f2ab Stavros Sachtouris
1179 4375e020 Stavros Sachtouris
    def set_object_meta(self, obj, metapairs):
1180 4375e020 Stavros Sachtouris
        """
1181 4375e020 Stavros Sachtouris
        :param obj: (str) remote object path
1182 4375e020 Stavros Sachtouris

1183 4375e020 Stavros Sachtouris
        :param metapairs: (dict) {key1:val1, key2:val2, ...}
1184 4375e020 Stavros Sachtouris
        """
1185 af3b2b36 Stavros Sachtouris
        assert(type(metapairs) is dict)
1186 915b99b5 Stavros Sachtouris
        r = self.object_post(obj, update=True, metadata=metapairs)
1187 915b99b5 Stavros Sachtouris
        return r.headers
1188 87688514 Stavros Sachtouris
1189 bc223d91 Stavros Sachtouris
    def del_object_meta(self, obj, metakey):
1190 4375e020 Stavros Sachtouris
        """
1191 4375e020 Stavros Sachtouris
        :param obj: (str) remote object path
1192 bc223d91 Stavros Sachtouris

1193 bc223d91 Stavros Sachtouris
        :param metakey: (str) metadatum key
1194 4375e020 Stavros Sachtouris
        """
1195 915b99b5 Stavros Sachtouris
        r = self.object_post(obj, update=True, metadata={metakey: ''})
1196 915b99b5 Stavros Sachtouris
        return r.headers
1197 6de1f262 Stavros Sachtouris
1198 bc223d91 Stavros Sachtouris
    def publish_object(self, obj):
1199 bc223d91 Stavros Sachtouris
        """
1200 bc223d91 Stavros Sachtouris
        :param obj: (str) remote object path
1201 5260a313 Stavros Sachtouris

1202 5260a313 Stavros Sachtouris
        :returns: (str) access url
1203 bc223d91 Stavros Sachtouris
        """
1204 c2b5da2f Stavros Sachtouris
        self.object_post(obj, update=True, public=True)
1205 5260a313 Stavros Sachtouris
        info = self.get_object_info(obj)
1206 fa382f9e Stavros Sachtouris
        return info['x-object-public']
1207 93542587 Stavros Sachtouris
        pref, sep, rest = self.base_url.partition('//')
1208 93542587 Stavros Sachtouris
        base = rest.split('/')[0]
1209 f847a0cc Stavros Sachtouris
        return '%s%s%s/%s' % (pref, sep, base, info['x-object-public'])
1210 87688514 Stavros Sachtouris
1211 bc223d91 Stavros Sachtouris
    def unpublish_object(self, obj):
1212 bc223d91 Stavros Sachtouris
        """
1213 bc223d91 Stavros Sachtouris
        :param obj: (str) remote object path
1214 bc223d91 Stavros Sachtouris
        """
1215 5655d560 Stavros Sachtouris
        r = self.object_post(obj, update=True, public=False)
1216 5655d560 Stavros Sachtouris
        return r.headers
1217 28470086 Stavros Sachtouris
1218 8af4cc0b Stavros Sachtouris
    def get_object_info(self, obj, version=None):
1219 bc223d91 Stavros Sachtouris
        """
1220 bc223d91 Stavros Sachtouris
        :param obj: (str) remote object path
1221 bc223d91 Stavros Sachtouris

1222 bc223d91 Stavros Sachtouris
        :param version: (str)
1223 bc223d91 Stavros Sachtouris

1224 bc223d91 Stavros Sachtouris
        :returns: (dict)
1225 bc223d91 Stavros Sachtouris
        """
1226 ca092af4 Stavros Sachtouris
        try:
1227 ca092af4 Stavros Sachtouris
            r = self.object_head(obj, version=version)
1228 ca092af4 Stavros Sachtouris
            return r.headers
1229 ca092af4 Stavros Sachtouris
        except ClientError as ce:
1230 ca092af4 Stavros Sachtouris
            if ce.status == 404:
1231 723e9d47 Stavros Sachtouris
                raise ClientError('Object %s not found' % obj, status=404)
1232 ca092af4 Stavros Sachtouris
            raise
1233 8af4cc0b Stavros Sachtouris
1234 8af4cc0b Stavros Sachtouris
    def get_object_meta(self, obj, version=None):
1235 bc223d91 Stavros Sachtouris
        """
1236 bc223d91 Stavros Sachtouris
        :param obj: (str) remote object path
1237 bc223d91 Stavros Sachtouris

1238 bc223d91 Stavros Sachtouris
        :param version: (str)
1239 bc223d91 Stavros Sachtouris

1240 bc223d91 Stavros Sachtouris
        :returns: (dict)
1241 bc223d91 Stavros Sachtouris
        """
1242 24ff0a35 Stavros Sachtouris
        return filter_in(
1243 24ff0a35 Stavros Sachtouris
            self.get_object_info(obj, version=version),
1244 3dabe5d2 Stavros Sachtouris
            'X-Object-Meta')
1245 8af4cc0b Stavros Sachtouris
1246 bc223d91 Stavros Sachtouris
    def get_object_sharing(self, obj):
1247 bc223d91 Stavros Sachtouris
        """
1248 bc223d91 Stavros Sachtouris
        :param obj: (str) remote object path
1249 bc223d91 Stavros Sachtouris

1250 bc223d91 Stavros Sachtouris
        :returns: (dict)
1251 bc223d91 Stavros Sachtouris
        """
1252 24ff0a35 Stavros Sachtouris
        r = filter_in(
1253 24ff0a35 Stavros Sachtouris
            self.get_object_info(obj),
1254 3dabe5d2 Stavros Sachtouris
            'X-Object-Sharing',
1255 3dabe5d2 Stavros Sachtouris
            exactMatch=True)
1256 f70616fc Stavros Sachtouris
        reply = {}
1257 f70616fc Stavros Sachtouris
        if len(r) > 0:
1258 f70616fc Stavros Sachtouris
            perms = r['x-object-sharing'].split(';')
1259 f70616fc Stavros Sachtouris
            for perm in perms:
1260 f70616fc Stavros Sachtouris
                try:
1261 f70616fc Stavros Sachtouris
                    perm.index('=')
1262 f70616fc Stavros Sachtouris
                except ValueError:
1263 f70616fc Stavros Sachtouris
                    raise ClientError('Incorrect reply format')
1264 f70616fc Stavros Sachtouris
                (key, val) = perm.strip().split('=')
1265 f70616fc Stavros Sachtouris
                reply[key] = val
1266 f70616fc Stavros Sachtouris
        return reply
1267 f49084df Stavros Sachtouris
1268 24ff0a35 Stavros Sachtouris
    def set_object_sharing(
1269 2005b18e Stavros Sachtouris
            self, obj,
1270 00336c85 Stavros Sachtouris
            read_permission=False, write_permission=False):
1271 28470086 Stavros Sachtouris
        """Give read/write permisions to an object.
1272 bc223d91 Stavros Sachtouris

1273 bc223d91 Stavros Sachtouris
        :param obj: (str) remote object path
1274 bc223d91 Stavros Sachtouris

1275 00336c85 Stavros Sachtouris
        :param read_permission: (list - bool) users and user groups that get
1276 00336c85 Stavros Sachtouris
            read permission for this object - False means all previous read
1277 bc223d91 Stavros Sachtouris
            permissions will be removed
1278 bc223d91 Stavros Sachtouris

1279 00336c85 Stavros Sachtouris
        :param write_permission: (list - bool) of users and user groups to get
1280 00336c85 Stavros Sachtouris
           write permission for this object - False means all previous write
1281 4067cdaf Stavros Sachtouris
           permissions will be removed
1282 915b99b5 Stavros Sachtouris

1283 915b99b5 Stavros Sachtouris
        :returns: (dict) response headers
1284 28470086 Stavros Sachtouris
        """
1285 4067cdaf Stavros Sachtouris
1286 00336c85 Stavros Sachtouris
        perms = dict(read=read_permission or '', write=write_permission or '')
1287 915b99b5 Stavros Sachtouris
        r = self.object_post(obj, update=True, permissions=perms)
1288 915b99b5 Stavros Sachtouris
        return r.headers
1289 28470086 Stavros Sachtouris
1290 bc223d91 Stavros Sachtouris
    def del_object_sharing(self, obj):
1291 bc223d91 Stavros Sachtouris
        """
1292 bc223d91 Stavros Sachtouris
        :param obj: (str) remote object path
1293 bc223d91 Stavros Sachtouris
        """
1294 915b99b5 Stavros Sachtouris
        return self.set_object_sharing(obj)
1295 bc223d91 Stavros Sachtouris
1296 bc223d91 Stavros Sachtouris
    def append_object(self, obj, source_file, upload_cb=None):
1297 bc223d91 Stavros Sachtouris
        """
1298 bc223d91 Stavros Sachtouris
        :param obj: (str) remote object path
1299 bc223d91 Stavros Sachtouris

1300 bc223d91 Stavros Sachtouris
        :param source_file: open file descriptor
1301 f49084df Stavros Sachtouris

1302 bc223d91 Stavros Sachtouris
        :param upload_db: progress.bar for uploading
1303 bcabbc35 Stavros Sachtouris
        """
1304 277ca4ed Dionysis Zindros
        self._assert_container()
1305 2f749e6e Stavros Sachtouris
        meta = self.get_container_info()
1306 ab474306 Stavros Sachtouris
        blocksize = int(meta['x-container-block-size'])
1307 64ab4c13 Stavros Sachtouris
        filesize = fstat(source_file.fileno()).st_size
1308 3dabe5d2 Stavros Sachtouris
        nblocks = 1 + (filesize - 1) // blocksize
1309 ab474306 Stavros Sachtouris
        offset = 0
1310 5655d560 Stavros Sachtouris
        headers = {}
1311 ca092af4 Stavros Sachtouris
        if upload_cb:
1312 5655d560 Stavros Sachtouris
            self.progress_bar_gen = upload_cb(nblocks)
1313 5655d560 Stavros Sachtouris
            self._cb_next()
1314 5655d560 Stavros Sachtouris
        flying = {}
1315 5655d560 Stavros Sachtouris
        self._init_thread_limit()
1316 5655d560 Stavros Sachtouris
        try:
1317 5655d560 Stavros Sachtouris
            for i in range(nblocks):
1318 5655d560 Stavros Sachtouris
                block = source_file.read(min(blocksize, filesize - offset))
1319 5655d560 Stavros Sachtouris
                offset += len(block)
1320 5655d560 Stavros Sachtouris
1321 5655d560 Stavros Sachtouris
                self._watch_thread_limit(flying.values())
1322 5655d560 Stavros Sachtouris
                unfinished = {}
1323 5655d560 Stavros Sachtouris
                flying[i] = SilentEvent(
1324 5655d560 Stavros Sachtouris
                    method=self.object_post,
1325 5655d560 Stavros Sachtouris
                    obj=obj,
1326 5655d560 Stavros Sachtouris
                    update=True,
1327 5655d560 Stavros Sachtouris
                    content_range='bytes */*',
1328 5655d560 Stavros Sachtouris
                    content_type='application/octet-stream',
1329 5655d560 Stavros Sachtouris
                    content_length=len(block),
1330 5655d560 Stavros Sachtouris
                    data=block)
1331 5655d560 Stavros Sachtouris
                flying[i].start()
1332 5655d560 Stavros Sachtouris
1333 5655d560 Stavros Sachtouris
                for key, thread in flying.items():
1334 5655d560 Stavros Sachtouris
                    if thread.isAlive():
1335 5655d560 Stavros Sachtouris
                        if i < nblocks:
1336 5655d560 Stavros Sachtouris
                            unfinished[key] = thread
1337 5655d560 Stavros Sachtouris
                            continue
1338 5655d560 Stavros Sachtouris
                        thread.join()
1339 5655d560 Stavros Sachtouris
                    if thread.exception:
1340 5655d560 Stavros Sachtouris
                        raise thread.exception
1341 5655d560 Stavros Sachtouris
                    headers[key] = thread.value.headers
1342 5655d560 Stavros Sachtouris
                    self._cb_next()
1343 5655d560 Stavros Sachtouris
                flying = unfinished
1344 5655d560 Stavros Sachtouris
        except KeyboardInterrupt:
1345 5655d560 Stavros Sachtouris
            sendlog.info('- - - wait for threads to finish')
1346 5655d560 Stavros Sachtouris
            for thread in activethreads():
1347 5655d560 Stavros Sachtouris
                thread.join()
1348 3ec5c230 Stavros Sachtouris
        finally:
1349 3ec5c230 Stavros Sachtouris
            from time import sleep
1350 4e424eaa Stavros Sachtouris
            sleep(2 * len(activethreads()))
1351 5655d560 Stavros Sachtouris
        return headers.values()
1352 561116a6 Stavros Sachtouris
1353 bc223d91 Stavros Sachtouris
    def truncate_object(self, obj, upto_bytes):
1354 bc223d91 Stavros Sachtouris
        """
1355 bc223d91 Stavros Sachtouris
        :param obj: (str) remote object path
1356 bc223d91 Stavros Sachtouris

1357 bc223d91 Stavros Sachtouris
        :param upto_bytes: max number of bytes to leave on file
1358 915b99b5 Stavros Sachtouris

1359 915b99b5 Stavros Sachtouris
        :returns: (dict) response headers
1360 bc223d91 Stavros Sachtouris
        """
1361 915b99b5 Stavros Sachtouris
        r = self.object_post(
1362 24ff0a35 Stavros Sachtouris
            obj,
1363 3dabe5d2 Stavros Sachtouris
            update=True,
1364 3dabe5d2 Stavros Sachtouris
            content_range='bytes 0-%s/*' % upto_bytes,
1365 3dabe5d2 Stavros Sachtouris
            content_type='application/octet-stream',
1366 3dabe5d2 Stavros Sachtouris
            object_bytes=upto_bytes,
1367 bc223d91 Stavros Sachtouris
            source_object=path4url(self.container, obj))
1368 915b99b5 Stavros Sachtouris
        return r.headers
1369 ee62607e Stavros Sachtouris
1370 2005b18e Stavros Sachtouris
    def overwrite_object(self, obj, start, end, source_file, upload_cb=None):
1371 bc223d91 Stavros Sachtouris
        """Overwrite a part of an object from local source file
1372 bc223d91 Stavros Sachtouris

1373 bc223d91 Stavros Sachtouris
        :param obj: (str) remote object path
1374 bc223d91 Stavros Sachtouris

1375 bc223d91 Stavros Sachtouris
        :param start: (int) position in bytes to start overwriting from
1376 bc223d91 Stavros Sachtouris

1377 bc223d91 Stavros Sachtouris
        :param end: (int) position in bytes to stop overwriting at
1378 bc223d91 Stavros Sachtouris

1379 bc223d91 Stavros Sachtouris
        :param source_file: open file descriptor
1380 bc223d91 Stavros Sachtouris

1381 bc223d91 Stavros Sachtouris
        :param upload_db: progress.bar for uploading
1382 ee62607e Stavros Sachtouris
        """
1383 4067cdaf Stavros Sachtouris
1384 ca092af4 Stavros Sachtouris
        r = self.get_object_info(obj)
1385 ca092af4 Stavros Sachtouris
        rf_size = int(r['content-length'])
1386 ca092af4 Stavros Sachtouris
        if rf_size < int(start):
1387 ca092af4 Stavros Sachtouris
            raise ClientError(
1388 ca092af4 Stavros Sachtouris
                'Range start exceeds file size',
1389 ca092af4 Stavros Sachtouris
                status=416)
1390 ca092af4 Stavros Sachtouris
        elif rf_size < int(end):
1391 ca092af4 Stavros Sachtouris
            raise ClientError(
1392 ca092af4 Stavros Sachtouris
                'Range end exceeds file size',
1393 ca092af4 Stavros Sachtouris
                status=416)
1394 277ca4ed Dionysis Zindros
        self._assert_container()
1395 2f749e6e Stavros Sachtouris
        meta = self.get_container_info()
1396 ee62607e Stavros Sachtouris
        blocksize = int(meta['x-container-block-size'])
1397 64ab4c13 Stavros Sachtouris
        filesize = fstat(source_file.fileno()).st_size
1398 ee62607e Stavros Sachtouris
        datasize = int(end) - int(start) + 1
1399 3dabe5d2 Stavros Sachtouris
        nblocks = 1 + (datasize - 1) // blocksize
1400 ee62607e Stavros Sachtouris
        offset = 0
1401 ca092af4 Stavros Sachtouris
        if upload_cb:
1402 915b99b5 Stavros Sachtouris
            self.progress_bar_gen = upload_cb(nblocks)
1403 915b99b5 Stavros Sachtouris
            self._cb_next()
1404 915b99b5 Stavros Sachtouris
        headers = []
1405 ee62607e Stavros Sachtouris
        for i in range(nblocks):
1406 24ff0a35 Stavros Sachtouris
            read_size = min(blocksize, filesize - offset, datasize - offset)
1407 24ff0a35 Stavros Sachtouris
            block = source_file.read(read_size)
1408 915b99b5 Stavros Sachtouris
            r = self.object_post(
1409 24ff0a35 Stavros Sachtouris
                obj,
1410 3dabe5d2 Stavros Sachtouris
                update=True,
1411 3dabe5d2 Stavros Sachtouris
                content_type='application/octet-stream',
1412 3dabe5d2 Stavros Sachtouris
                content_length=len(block),
1413 ca092af4 Stavros Sachtouris
                content_range='bytes %s-%s/*' % (
1414 ca092af4 Stavros Sachtouris
                    start + offset,
1415 ca092af4 Stavros Sachtouris
                    start + offset + len(block) - 1),
1416 3dabe5d2 Stavros Sachtouris
                data=block)
1417 915b99b5 Stavros Sachtouris
            headers.append(dict(r.headers))
1418 ca092af4 Stavros Sachtouris
            offset += len(block)
1419 3dabe5d2 Stavros Sachtouris
1420 915b99b5 Stavros Sachtouris
            self._cb_next
1421 915b99b5 Stavros Sachtouris
        return headers
1422 7d420701 Stavros Sachtouris
1423 24ff0a35 Stavros Sachtouris
    def copy_object(
1424 2005b18e Stavros Sachtouris
            self, src_container, src_object, dst_container,
1425 33487500 Stavros Sachtouris
            dst_object=None,
1426 2005b18e Stavros Sachtouris
            source_version=None,
1427 3a066af4 Stavros Sachtouris
            source_account=None,
1428 2005b18e Stavros Sachtouris
            public=False,
1429 2005b18e Stavros Sachtouris
            content_type=None,
1430 2005b18e Stavros Sachtouris
            delimiter=None):
1431 bc223d91 Stavros Sachtouris
        """
1432 bc223d91 Stavros Sachtouris
        :param src_container: (str) source container
1433 bc223d91 Stavros Sachtouris

1434 bc223d91 Stavros Sachtouris
        :param src_object: (str) source object path
1435 bc223d91 Stavros Sachtouris

1436 bc223d91 Stavros Sachtouris
        :param dst_container: (str) destination container
1437 bc223d91 Stavros Sachtouris

1438 bc223d91 Stavros Sachtouris
        :param dst_object: (str) destination object path
1439 bc223d91 Stavros Sachtouris

1440 bc223d91 Stavros Sachtouris
        :param source_version: (str) source object version
1441 bc223d91 Stavros Sachtouris

1442 3a066af4 Stavros Sachtouris
        :param source_account: (str) account to copy from
1443 3a066af4 Stavros Sachtouris

1444 bc223d91 Stavros Sachtouris
        :param public: (bool)
1445 bc223d91 Stavros Sachtouris

1446 bc223d91 Stavros Sachtouris
        :param content_type: (str)
1447 bc223d91 Stavros Sachtouris

1448 bc223d91 Stavros Sachtouris
        :param delimiter: (str)
1449 55c75058 Stavros Sachtouris

1450 55c75058 Stavros Sachtouris
        :returns: (dict) response headers
1451 bc223d91 Stavros Sachtouris
        """
1452 277ca4ed Dionysis Zindros
        self._assert_account()
1453 7d420701 Stavros Sachtouris
        self.container = dst_container
1454 7d420701 Stavros Sachtouris
        src_path = path4url(src_container, src_object)
1455 55c75058 Stavros Sachtouris
        r = self.object_put(
1456 33487500 Stavros Sachtouris
            dst_object or src_object,
1457 3dabe5d2 Stavros Sachtouris
            success=201,
1458 3dabe5d2 Stavros Sachtouris
            copy_from=src_path,
1459 3dabe5d2 Stavros Sachtouris
            content_length=0,
1460 3dabe5d2 Stavros Sachtouris
            source_version=source_version,
1461 3a066af4 Stavros Sachtouris
            source_account=source_account,
1462 3dabe5d2 Stavros Sachtouris
            public=public,
1463 3dabe5d2 Stavros Sachtouris
            content_type=content_type,
1464 7d420701 Stavros Sachtouris
            delimiter=delimiter)
1465 55c75058 Stavros Sachtouris
        return r.headers
1466 a5e0629d Stavros Sachtouris
1467 24ff0a35 Stavros Sachtouris
    def move_object(
1468 24ff0a35 Stavros Sachtouris
            self, src_container, src_object, dst_container,
1469 24ff0a35 Stavros Sachtouris
            dst_object=False,
1470 4f266635 Stavros Sachtouris
            source_account=None,
1471 24ff0a35 Stavros Sachtouris
            source_version=None,
1472 24ff0a35 Stavros Sachtouris
            public=False,
1473 24ff0a35 Stavros Sachtouris
            content_type=None,
1474 24ff0a35 Stavros Sachtouris
            delimiter=None):
1475 bc223d91 Stavros Sachtouris
        """
1476 bc223d91 Stavros Sachtouris
        :param src_container: (str) source container
1477 bc223d91 Stavros Sachtouris

1478 bc223d91 Stavros Sachtouris
        :param src_object: (str) source object path
1479 bc223d91 Stavros Sachtouris

1480 bc223d91 Stavros Sachtouris
        :param dst_container: (str) destination container
1481 bc223d91 Stavros Sachtouris

1482 bc223d91 Stavros Sachtouris
        :param dst_object: (str) destination object path
1483 bc223d91 Stavros Sachtouris

1484 4f266635 Stavros Sachtouris
        :param source_account: (str) account to move from
1485 4f266635 Stavros Sachtouris

1486 bc223d91 Stavros Sachtouris
        :param source_version: (str) source object version
1487 bc223d91 Stavros Sachtouris

1488 bc223d91 Stavros Sachtouris
        :param public: (bool)
1489 bc223d91 Stavros Sachtouris

1490 bc223d91 Stavros Sachtouris
        :param content_type: (str)
1491 bc223d91 Stavros Sachtouris

1492 bc223d91 Stavros Sachtouris
        :param delimiter: (str)
1493 55c75058 Stavros Sachtouris

1494 55c75058 Stavros Sachtouris
        :returns: (dict) response headers
1495 bc223d91 Stavros Sachtouris
        """
1496 277ca4ed Dionysis Zindros
        self._assert_account()
1497 a5e0629d Stavros Sachtouris
        self.container = dst_container
1498 a5e0629d Stavros Sachtouris
        dst_object = dst_object or src_object
1499 a5e0629d Stavros Sachtouris
        src_path = path4url(src_container, src_object)
1500 55c75058 Stavros Sachtouris
        r = self.object_put(
1501 24ff0a35 Stavros Sachtouris
            dst_object,
1502 3dabe5d2 Stavros Sachtouris
            success=201,
1503 3dabe5d2 Stavros Sachtouris
            move_from=src_path,
1504 3dabe5d2 Stavros Sachtouris
            content_length=0,
1505 4f266635 Stavros Sachtouris
            source_account=source_account,
1506 3dabe5d2 Stavros Sachtouris
            source_version=source_version,
1507 3dabe5d2 Stavros Sachtouris
            public=public,
1508 3dabe5d2 Stavros Sachtouris
            content_type=content_type,
1509 a23f6ffe Stavros Sachtouris
            delimiter=delimiter)
1510 55c75058 Stavros Sachtouris
        return r.headers
1511 a23f6ffe Stavros Sachtouris
1512 a23f6ffe Stavros Sachtouris
    def get_sharing_accounts(self, limit=None, marker=None, *args, **kwargs):
1513 bc223d91 Stavros Sachtouris
        """Get accounts that share with self.account
1514 bc223d91 Stavros Sachtouris

1515 bc223d91 Stavros Sachtouris
        :param limit: (str)
1516 bc223d91 Stavros Sachtouris

1517 bc223d91 Stavros Sachtouris
        :param marker: (str)
1518 bc223d91 Stavros Sachtouris

1519 bc223d91 Stavros Sachtouris
        :returns: (dict)
1520 bc223d91 Stavros Sachtouris
        """
1521 277ca4ed Dionysis Zindros
        self._assert_account()
1522 a23f6ffe Stavros Sachtouris
1523 3dabe5d2 Stavros Sachtouris
        self.set_param('format', 'json')
1524 3dabe5d2 Stavros Sachtouris
        self.set_param('limit', limit, iff=limit is not None)
1525 3dabe5d2 Stavros Sachtouris
        self.set_param('marker', marker, iff=marker is not None)
1526 a23f6ffe Stavros Sachtouris
1527 a23f6ffe Stavros Sachtouris
        path = ''
1528 a23f6ffe Stavros Sachtouris
        success = kwargs.pop('success', (200, 204))
1529 3dabe5d2 Stavros Sachtouris
        r = self.get(path, *args, success=success, **kwargs)
1530 38dc5d2f Stavros Sachtouris
        return r.json
1531 38dc5d2f Stavros Sachtouris
1532 bc223d91 Stavros Sachtouris
    def get_object_versionlist(self, obj):
1533 bc223d91 Stavros Sachtouris
        """
1534 bc223d91 Stavros Sachtouris
        :param obj: (str) remote object path
1535 bc223d91 Stavros Sachtouris

1536 bc223d91 Stavros Sachtouris
        :returns: (list)
1537 bc223d91 Stavros Sachtouris
        """
1538 277ca4ed Dionysis Zindros
        self._assert_container()
1539 bc223d91 Stavros Sachtouris
        r = self.object_get(obj, format='json', version='list')
1540 38dc5d2f Stavros Sachtouris
        return r.json['versions']