Statistics
| Branch: | Tag: | Revision:

root / kamaki / clients / pithos / __init__.py @ edc1182f

History | View | Annotate | Download (49 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 4f228300 Stavros Sachtouris
from kamaki.clients.utils import path4url, filter_in, readall
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 6c068db6 Stavros Sachtouris
            container=None, sizelimit=None, versioning=None, metadata=None,
109 6c068db6 Stavros Sachtouris
            **kwargs):
110 94bedc5b Stavros Sachtouris
        """
111 94bedc5b Stavros Sachtouris
        :param container: (str) if not given, self.container is used instead
112 94bedc5b Stavros Sachtouris

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

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

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

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

156 4375e020 Stavros Sachtouris
        :param f: open file descriptor
157 4375e020 Stavros Sachtouris

158 4375e020 Stavros Sachtouris
        :param withHashFile: (bool)
159 4375e020 Stavros Sachtouris

160 4375e020 Stavros Sachtouris
        :param size: (int) size of data to upload
161 4375e020 Stavros Sachtouris

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

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

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

168 4375e020 Stavros Sachtouris
        :param content_type: (str)
169 4375e020 Stavros Sachtouris

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

173 4375e020 Stavros Sachtouris
        :param public: (bool)
174 3c216009 Stavros Sachtouris

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

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

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

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

221 4375e020 Stavros Sachtouris
        :param content_type: (str)
222 4375e020 Stavros Sachtouris

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

226 4375e020 Stavros Sachtouris
        :param public: (bool)
227 3c216009 Stavros Sachtouris

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

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

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

388 4375e020 Stavros Sachtouris
        :param obj: (str) remote object path
389 4375e020 Stavros Sachtouris

390 4375e020 Stavros Sachtouris
        :param f: open file descriptor (rb)
391 4375e020 Stavros Sachtouris

392 4375e020 Stavros Sachtouris
        :param hash_cb: optional progress.bar object for calculating hashes
393 4375e020 Stavros Sachtouris

394 4375e020 Stavros Sachtouris
        :param upload_cb: optional progress.bar object for uploading
395 4375e020 Stavros Sachtouris

396 4375e020 Stavros Sachtouris
        :param etag: (str)
397 4375e020 Stavros Sachtouris

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

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

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

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

410 4375e020 Stavros Sachtouris
        :param content_type: (str)
411 4375e020 Stavros Sachtouris

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

415 4375e020 Stavros Sachtouris
        :param public: (bool)
416 76ebf97c Stavros Sachtouris

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

522 9d502497 Stavros Sachtouris
        :param obj: (str) remote object path
523 9d502497 Stavros Sachtouris

524 9d502497 Stavros Sachtouris
        :param input_str: (str) upload content
525 9d502497 Stavros Sachtouris

526 9d502497 Stavros Sachtouris
        :param hash_cb: optional progress.bar object for calculating hashes
527 9d502497 Stavros Sachtouris

528 9d502497 Stavros Sachtouris
        :param upload_cb: optional progress.bar object for uploading
529 9d502497 Stavros Sachtouris

530 9d502497 Stavros Sachtouris
        :param etag: (str)
531 9d502497 Stavros Sachtouris

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

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

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

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

544 9d502497 Stavros Sachtouris
        :param content_type: (str)
545 9d502497 Stavros Sachtouris

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

549 9d502497 Stavros Sachtouris
        :param public: (bool)
550 9d502497 Stavros Sachtouris

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

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

762 4375e020 Stavros Sachtouris
        :param obj: (str) remote object path
763 4375e020 Stavros Sachtouris

764 4375e020 Stavros Sachtouris
        :param dst: open file descriptor (wb+)
765 4375e020 Stavros Sachtouris

766 4375e020 Stavros Sachtouris
        :param download_cb: optional progress.bar object for downloading
767 4375e020 Stavros Sachtouris

768 4375e020 Stavros Sachtouris
        :param version: (str) file version
769 4375e020 Stavros Sachtouris

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

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

774 4375e020 Stavros Sachtouris
        :param if_match: (str)
775 4375e020 Stavros Sachtouris

776 4375e020 Stavros Sachtouris
        :param if_none_match: (str)
777 4375e020 Stavros Sachtouris

778 4375e020 Stavros Sachtouris
        :param if_modified_since: (str) formated date
779 4375e020 Stavros Sachtouris

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

838 49cc29b2 Stavros Sachtouris
        :param obj: (str) remote object path
839 49cc29b2 Stavros Sachtouris

840 49cc29b2 Stavros Sachtouris
        :param download_cb: optional progress.bar object for downloading
841 49cc29b2 Stavros Sachtouris

842 49cc29b2 Stavros Sachtouris
        :param version: (str) file version
843 49cc29b2 Stavros Sachtouris

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

846 49cc29b2 Stavros Sachtouris
        :param if_match: (str)
847 49cc29b2 Stavros Sachtouris

848 49cc29b2 Stavros Sachtouris
        :param if_none_match: (str)
849 49cc29b2 Stavros Sachtouris

850 49cc29b2 Stavros Sachtouris
        :param if_modified_since: (str) formated date
851 49cc29b2 Stavros Sachtouris

852 49cc29b2 Stavros Sachtouris
        :param if_unmodified_since: (str) formated date
853 49cc29b2 Stavros Sachtouris

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

932 4375e020 Stavros Sachtouris
        :param if_match: (str)
933 4375e020 Stavros Sachtouris

934 4375e020 Stavros Sachtouris
        :param if_none_match: (str)
935 4375e020 Stavros Sachtouris

936 4375e020 Stavros Sachtouris
        :param if_modified_since: (str) formated date
937 4375e020 Stavros Sachtouris

938 4375e020 Stavros Sachtouris
        :param if_unmodified_since: (str) formated date
939 4375e020 Stavros Sachtouris

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

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

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

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

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

1056 4375e020 Stavros Sachtouris
        :raises ClientError: 404 Container does not exist
1057 4375e020 Stavros Sachtouris

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

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

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

1109 4375e020 Stavros Sachtouris
        :returns: (dict)
1110 f91bc6b1 Stavros Sachtouris

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

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

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

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

1175 4375e020 Stavros Sachtouris
        :param until: (str) formated date
1176 4375e020 Stavros Sachtouris

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

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

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

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

1226 bc223d91 Stavros Sachtouris
        :param version: (str)
1227 bc223d91 Stavros Sachtouris

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

1242 bc223d91 Stavros Sachtouris
        :param version: (str)
1243 bc223d91 Stavros Sachtouris

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

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

1277 bc223d91 Stavros Sachtouris
        :param obj: (str) remote object path
1278 bc223d91 Stavros Sachtouris

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

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

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

1304 bc223d91 Stavros Sachtouris
        :param source_file: open file descriptor
1305 f49084df Stavros Sachtouris

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

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

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

1377 bc223d91 Stavros Sachtouris
        :param obj: (str) remote object path
1378 bc223d91 Stavros Sachtouris

1379 bc223d91 Stavros Sachtouris
        :param start: (int) position in bytes to start overwriting from
1380 bc223d91 Stavros Sachtouris

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

1383 bc223d91 Stavros Sachtouris
        :param source_file: open file descriptor
1384 bc223d91 Stavros Sachtouris

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

1438 bc223d91 Stavros Sachtouris
        :param src_object: (str) source object path
1439 bc223d91 Stavros Sachtouris

1440 bc223d91 Stavros Sachtouris
        :param dst_container: (str) destination container
1441 bc223d91 Stavros Sachtouris

1442 bc223d91 Stavros Sachtouris
        :param dst_object: (str) destination object path
1443 bc223d91 Stavros Sachtouris

1444 bc223d91 Stavros Sachtouris
        :param source_version: (str) source object version
1445 bc223d91 Stavros Sachtouris

1446 3a066af4 Stavros Sachtouris
        :param source_account: (str) account to copy from
1447 3a066af4 Stavros Sachtouris

1448 bc223d91 Stavros Sachtouris
        :param public: (bool)
1449 bc223d91 Stavros Sachtouris

1450 bc223d91 Stavros Sachtouris
        :param content_type: (str)
1451 bc223d91 Stavros Sachtouris

1452 bc223d91 Stavros Sachtouris
        :param delimiter: (str)
1453 55c75058 Stavros Sachtouris

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

1482 bc223d91 Stavros Sachtouris
        :param src_object: (str) source object path
1483 bc223d91 Stavros Sachtouris

1484 bc223d91 Stavros Sachtouris
        :param dst_container: (str) destination container
1485 bc223d91 Stavros Sachtouris

1486 bc223d91 Stavros Sachtouris
        :param dst_object: (str) destination object path
1487 bc223d91 Stavros Sachtouris

1488 4f266635 Stavros Sachtouris
        :param source_account: (str) account to move from
1489 4f266635 Stavros Sachtouris

1490 bc223d91 Stavros Sachtouris
        :param source_version: (str) source object version
1491 bc223d91 Stavros Sachtouris

1492 bc223d91 Stavros Sachtouris
        :param public: (bool)
1493 bc223d91 Stavros Sachtouris

1494 bc223d91 Stavros Sachtouris
        :param content_type: (str)
1495 bc223d91 Stavros Sachtouris

1496 bc223d91 Stavros Sachtouris
        :param delimiter: (str)
1497 55c75058 Stavros Sachtouris

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

1519 bc223d91 Stavros Sachtouris
        :param limit: (str)
1520 bc223d91 Stavros Sachtouris

1521 bc223d91 Stavros Sachtouris
        :param marker: (str)
1522 bc223d91 Stavros Sachtouris

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

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