Statistics
| Branch: | Tag: | Revision:

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

History | View | Annotate | Download (49.5 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 bd02c3ea Stavros Sachtouris
    assert start >= 0, '_range_up called w. start(%s) < 0' % start
70 bd02c3ea Stavros Sachtouris
    assert end >= start, '_range_up called w. end(%s) < start(%s)' % (
71 bd02c3ea Stavros Sachtouris
        end, start)
72 bd02c3ea Stavros Sachtouris
    assert end <= max_value, '_range_up called w. max_value(%s) < end(%s)' % (
73 bd02c3ea Stavros Sachtouris
        max_value, end)
74 776b275c Stavros Sachtouris
    if not a_range:
75 776b275c Stavros Sachtouris
        return '%s-%s' % (start, end)
76 776b275c Stavros Sachtouris
    selected = []
77 776b275c Stavros Sachtouris
    for some_range in a_range.split(','):
78 776b275c Stavros Sachtouris
        v0, sep, v1 = some_range.partition('-')
79 776b275c Stavros Sachtouris
        if v0:
80 776b275c Stavros Sachtouris
            v0 = int(v0)
81 6e50fed4 Stavros Sachtouris
            if sep:
82 776b275c Stavros Sachtouris
                v1 = int(v1)
83 776b275c Stavros Sachtouris
                if v1 < start or v0 > end or v1 < v0:
84 776b275c Stavros Sachtouris
                    continue
85 776b275c Stavros Sachtouris
                v0 = v0 if v0 > start else start
86 776b275c Stavros Sachtouris
                v1 = v1 if v1 < end else end
87 776b275c Stavros Sachtouris
                selected.append('%s-%s' % (v0, v1))
88 776b275c Stavros Sachtouris
            elif v0 < start:
89 776b275c Stavros Sachtouris
                continue
90 6e50fed4 Stavros Sachtouris
            else:
91 776b275c Stavros Sachtouris
                v1 = v0 if v0 <= end else end
92 776b275c Stavros Sachtouris
                selected.append('%s-%s' % (start, v1))
93 6e50fed4 Stavros Sachtouris
        else:
94 776b275c Stavros Sachtouris
            v1 = int(v1)
95 776b275c Stavros Sachtouris
            if max_value - v1 > end:
96 776b275c Stavros Sachtouris
                continue
97 776b275c Stavros Sachtouris
            v0 = (max_value - v1) if max_value - v1 > start else start
98 776b275c Stavros Sachtouris
            selected.append('%s-%s' % (v0, end))
99 776b275c Stavros Sachtouris
    return ','.join(selected)
100 642f1bbd Stavros Sachtouris
101 3dabe5d2 Stavros Sachtouris
102 55faa0bc Stavros Sachtouris
class PithosClient(PithosRestClient):
103 76e7661e Stavros Sachtouris
    """Synnefo Pithos+ API client"""
104 a91e0293 Giorgos Verigakis
105 3dabe5d2 Stavros Sachtouris
    def __init__(self, base_url, token, account=None, container=None):
106 3dabe5d2 Stavros Sachtouris
        super(PithosClient, self).__init__(base_url, token, account, container)
107 435008b6 Stavros Sachtouris
108 94bedc5b Stavros Sachtouris
    def create_container(
109 94bedc5b Stavros Sachtouris
            self,
110 6c068db6 Stavros Sachtouris
            container=None, sizelimit=None, versioning=None, metadata=None,
111 6c068db6 Stavros Sachtouris
            **kwargs):
112 94bedc5b Stavros Sachtouris
        """
113 94bedc5b Stavros Sachtouris
        :param container: (str) if not given, self.container is used instead
114 94bedc5b Stavros Sachtouris

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

391 4375e020 Stavros Sachtouris
        :param obj: (str) remote object path
392 4375e020 Stavros Sachtouris

393 4375e020 Stavros Sachtouris
        :param f: open file descriptor (rb)
394 4375e020 Stavros Sachtouris

395 4375e020 Stavros Sachtouris
        :param hash_cb: optional progress.bar object for calculating hashes
396 4375e020 Stavros Sachtouris

397 4375e020 Stavros Sachtouris
        :param upload_cb: optional progress.bar object for uploading
398 4375e020 Stavros Sachtouris

399 4375e020 Stavros Sachtouris
        :param etag: (str)
400 4375e020 Stavros Sachtouris

401 e9ac514e Stavros Sachtouris
        :param if_etag_match: (str) Push that value to if-match header at file
402 e9ac514e Stavros Sachtouris
            creation
403 e9ac514e Stavros Sachtouris

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

409 4375e020 Stavros Sachtouris
        :param content_encoding: (str)
410 4375e020 Stavros Sachtouris

411 4375e020 Stavros Sachtouris
        :param content_disposition: (str)
412 4375e020 Stavros Sachtouris

413 4375e020 Stavros Sachtouris
        :param content_type: (str)
414 4375e020 Stavros Sachtouris

415 4375e020 Stavros Sachtouris
        :param sharing: {'read':[user and/or grp names],
416 4375e020 Stavros Sachtouris
            'write':[usr and/or grp names]}
417 4375e020 Stavros Sachtouris

418 4375e020 Stavros Sachtouris
        :param public: (bool)
419 76ebf97c Stavros Sachtouris

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

525 9d502497 Stavros Sachtouris
        :param obj: (str) remote object path
526 9d502497 Stavros Sachtouris

527 9d502497 Stavros Sachtouris
        :param input_str: (str) upload content
528 9d502497 Stavros Sachtouris

529 9d502497 Stavros Sachtouris
        :param hash_cb: optional progress.bar object for calculating hashes
530 9d502497 Stavros Sachtouris

531 9d502497 Stavros Sachtouris
        :param upload_cb: optional progress.bar object for uploading
532 9d502497 Stavros Sachtouris

533 9d502497 Stavros Sachtouris
        :param etag: (str)
534 9d502497 Stavros Sachtouris

535 9d502497 Stavros Sachtouris
        :param if_etag_match: (str) Push that value to if-match header at file
536 9d502497 Stavros Sachtouris
            creation
537 9d502497 Stavros Sachtouris

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

543 9d502497 Stavros Sachtouris
        :param content_encoding: (str)
544 9d502497 Stavros Sachtouris

545 9d502497 Stavros Sachtouris
        :param content_disposition: (str)
546 9d502497 Stavros Sachtouris

547 9d502497 Stavros Sachtouris
        :param content_type: (str)
548 9d502497 Stavros Sachtouris

549 9d502497 Stavros Sachtouris
        :param sharing: {'read':[user and/or grp names],
550 9d502497 Stavros Sachtouris
            'write':[usr and/or grp names]}
551 9d502497 Stavros Sachtouris

552 9d502497 Stavros Sachtouris
        :param public: (bool)
553 9d502497 Stavros Sachtouris

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

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

769 4375e020 Stavros Sachtouris
        :param obj: (str) remote object path
770 4375e020 Stavros Sachtouris

771 4375e020 Stavros Sachtouris
        :param dst: open file descriptor (wb+)
772 4375e020 Stavros Sachtouris

773 4375e020 Stavros Sachtouris
        :param download_cb: optional progress.bar object for downloading
774 4375e020 Stavros Sachtouris

775 4375e020 Stavros Sachtouris
        :param version: (str) file version
776 4375e020 Stavros Sachtouris

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

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

781 4375e020 Stavros Sachtouris
        :param if_match: (str)
782 4375e020 Stavros Sachtouris

783 4375e020 Stavros Sachtouris
        :param if_none_match: (str)
784 4375e020 Stavros Sachtouris

785 4375e020 Stavros Sachtouris
        :param if_modified_since: (str) formated date
786 4375e020 Stavros Sachtouris

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

845 49cc29b2 Stavros Sachtouris
        :param obj: (str) remote object path
846 49cc29b2 Stavros Sachtouris

847 49cc29b2 Stavros Sachtouris
        :param download_cb: optional progress.bar object for downloading
848 49cc29b2 Stavros Sachtouris

849 49cc29b2 Stavros Sachtouris
        :param version: (str) file version
850 49cc29b2 Stavros Sachtouris

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

853 49cc29b2 Stavros Sachtouris
        :param if_match: (str)
854 49cc29b2 Stavros Sachtouris

855 49cc29b2 Stavros Sachtouris
        :param if_none_match: (str)
856 49cc29b2 Stavros Sachtouris

857 49cc29b2 Stavros Sachtouris
        :param if_modified_since: (str) formated date
858 49cc29b2 Stavros Sachtouris

859 49cc29b2 Stavros Sachtouris
        :param if_unmodified_since: (str) formated date
860 49cc29b2 Stavros Sachtouris

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

939 4375e020 Stavros Sachtouris
        :param if_match: (str)
940 4375e020 Stavros Sachtouris

941 4375e020 Stavros Sachtouris
        :param if_none_match: (str)
942 4375e020 Stavros Sachtouris

943 4375e020 Stavros Sachtouris
        :param if_modified_since: (str) formated date
944 4375e020 Stavros Sachtouris

945 4375e020 Stavros Sachtouris
        :param if_unmodified_since: (str) formated date
946 4375e020 Stavros Sachtouris

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

968 4375e020 Stavros Sachtouris
        :param usernames: (list)
969 4375e020 Stavros Sachtouris
        """
970 915b99b5 Stavros Sachtouris
        r = self.account_post(update=True, groups={group: usernames})
971 915b99b5 Stavros Sachtouris
        return r
972 eb903ba7 Stavros Sachtouris
973 c2867610 Stavros Sachtouris
    def del_account_group(self, group):
974 4375e020 Stavros Sachtouris
        """
975 4375e020 Stavros Sachtouris
        :param group: (str)
976 4375e020 Stavros Sachtouris
        """
977 c2b5da2f Stavros Sachtouris
        self.account_post(update=True, groups={group: []})
978 c2867610 Stavros Sachtouris
979 8af4cc0b Stavros Sachtouris
    def get_account_info(self, until=None):
980 4375e020 Stavros Sachtouris
        """
981 4375e020 Stavros Sachtouris
        :param until: (str) formated date
982 4375e020 Stavros Sachtouris

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

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

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

1063 4375e020 Stavros Sachtouris
        :raises ClientError: 404 Container does not exist
1064 4375e020 Stavros Sachtouris

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

1086 4375e020 Stavros Sachtouris
        :returns: (dict)
1087 4375e020 Stavros Sachtouris
        """
1088 bae347d3 Stavros Sachtouris
        cnt_back_up = self.container
1089 bae347d3 Stavros Sachtouris
        try:
1090 bae347d3 Stavros Sachtouris
            self.container = container or cnt_back_up
1091 bae347d3 Stavros Sachtouris
            return filter_in(
1092 bae347d3 Stavros Sachtouris
                self.get_container_info(),
1093 bae347d3 Stavros Sachtouris
                'X-Container-Policy-Versioning')
1094 bae347d3 Stavros Sachtouris
        finally:
1095 bae347d3 Stavros Sachtouris
            self.container = cnt_back_up
1096 d1856abf Stavros Sachtouris
1097 3ed6dbde Stavros Sachtouris
    def get_container_limit(self, container=None):
1098 4375e020 Stavros Sachtouris
        """
1099 4375e020 Stavros Sachtouris
        :param container: (str)
1100 4375e020 Stavros Sachtouris

1101 4375e020 Stavros Sachtouris
        :returns: (dict)
1102 4375e020 Stavros Sachtouris
        """
1103 1879e310 Stavros Sachtouris
        cnt_back_up = self.container
1104 1879e310 Stavros Sachtouris
        try:
1105 1879e310 Stavros Sachtouris
            self.container = container or cnt_back_up
1106 1879e310 Stavros Sachtouris
            return filter_in(
1107 1879e310 Stavros Sachtouris
                self.get_container_info(),
1108 1879e310 Stavros Sachtouris
                'X-Container-Policy-Quota')
1109 1879e310 Stavros Sachtouris
        finally:
1110 1879e310 Stavros Sachtouris
            self.container = cnt_back_up
1111 e6b39366 Stavros Sachtouris
1112 482bc0c4 Stavros Sachtouris
    def get_container_info(self, container=None, until=None):
1113 4375e020 Stavros Sachtouris
        """
1114 4375e020 Stavros Sachtouris
        :param until: (str) formated date
1115 4375e020 Stavros Sachtouris

1116 4375e020 Stavros Sachtouris
        :returns: (dict)
1117 f91bc6b1 Stavros Sachtouris

1118 f91bc6b1 Stavros Sachtouris
        :raises ClientError: 404 Container not found
1119 4375e020 Stavros Sachtouris
        """
1120 482bc0c4 Stavros Sachtouris
        bck_cont = self.container
1121 f91bc6b1 Stavros Sachtouris
        try:
1122 482bc0c4 Stavros Sachtouris
            self.container = container or bck_cont
1123 482bc0c4 Stavros Sachtouris
            self._assert_container()
1124 f91bc6b1 Stavros Sachtouris
            r = self.container_head(until=until)
1125 f91bc6b1 Stavros Sachtouris
        except ClientError as err:
1126 f91bc6b1 Stavros Sachtouris
            err.details.append('for container %s' % self.container)
1127 f91bc6b1 Stavros Sachtouris
            raise err
1128 482bc0c4 Stavros Sachtouris
        finally:
1129 482bc0c4 Stavros Sachtouris
            self.container = bck_cont
1130 8af4cc0b Stavros Sachtouris
        return r.headers
1131 8af4cc0b Stavros Sachtouris
1132 3dabe5d2 Stavros Sachtouris
    def get_container_meta(self, until=None):
1133 4375e020 Stavros Sachtouris
        """
1134 4375e020 Stavros Sachtouris
        :param until: (str) formated date
1135 4375e020 Stavros Sachtouris

1136 4375e020 Stavros Sachtouris
        :returns: (dict)
1137 4375e020 Stavros Sachtouris
        """
1138 24ff0a35 Stavros Sachtouris
        return filter_in(
1139 482bc0c4 Stavros Sachtouris
            self.get_container_info(until=until), 'X-Container-Meta')
1140 e6b39366 Stavros Sachtouris
1141 3dabe5d2 Stavros Sachtouris
    def get_container_object_meta(self, until=None):
1142 4375e020 Stavros Sachtouris
        """
1143 4375e020 Stavros Sachtouris
        :param until: (str) formated date
1144 4375e020 Stavros Sachtouris

1145 4375e020 Stavros Sachtouris
        :returns: (dict)
1146 4375e020 Stavros Sachtouris
        """
1147 24ff0a35 Stavros Sachtouris
        return filter_in(
1148 482bc0c4 Stavros Sachtouris
            self.get_container_info(until=until), 'X-Container-Object-Meta')
1149 e6b39366 Stavros Sachtouris
1150 af3b2b36 Stavros Sachtouris
    def set_container_meta(self, metapairs):
1151 4375e020 Stavros Sachtouris
        """
1152 4375e020 Stavros Sachtouris
        :param metapairs: (dict) {key1:val1, key2:val2, ...}
1153 4375e020 Stavros Sachtouris
        """
1154 af3b2b36 Stavros Sachtouris
        assert(type(metapairs) is dict)
1155 915b99b5 Stavros Sachtouris
        r = self.container_post(update=True, metadata=metapairs)
1156 915b99b5 Stavros Sachtouris
        return r.headers
1157 3dabe5d2 Stavros Sachtouris
1158 3e544e5b Stavros Sachtouris
    def del_container_meta(self, metakey):
1159 4375e020 Stavros Sachtouris
        """
1160 4375e020 Stavros Sachtouris
        :param metakey: (str) metadatum key
1161 545c6c29 Stavros Sachtouris

1162 545c6c29 Stavros Sachtouris
        :returns: (dict) response headers
1163 4375e020 Stavros Sachtouris
        """
1164 915b99b5 Stavros Sachtouris
        r = self.container_post(update=True, metadata={metakey: ''})
1165 915b99b5 Stavros Sachtouris
        return r.headers
1166 e6b39366 Stavros Sachtouris
1167 326a79b9 Stavros Sachtouris
    def set_container_limit(self, limit):
1168 4375e020 Stavros Sachtouris
        """
1169 326a79b9 Stavros Sachtouris
        :param limit: (int)
1170 4375e020 Stavros Sachtouris
        """
1171 915b99b5 Stavros Sachtouris
        r = self.container_post(update=True, quota=limit)
1172 915b99b5 Stavros Sachtouris
        return r.headers
1173 d1856abf Stavros Sachtouris
1174 d1856abf Stavros Sachtouris
    def set_container_versioning(self, versioning):
1175 4375e020 Stavros Sachtouris
        """
1176 4375e020 Stavros Sachtouris
        :param versioning: (str)
1177 4375e020 Stavros Sachtouris
        """
1178 915b99b5 Stavros Sachtouris
        r = self.container_post(update=True, versioning=versioning)
1179 915b99b5 Stavros Sachtouris
        return r.headers
1180 d1856abf Stavros Sachtouris
1181 a298f2ab Stavros Sachtouris
    def del_object(self, obj, until=None, delimiter=None):
1182 4375e020 Stavros Sachtouris
        """
1183 4375e020 Stavros Sachtouris
        :param obj: (str) remote object path
1184 4375e020 Stavros Sachtouris

1185 4375e020 Stavros Sachtouris
        :param until: (str) formated date
1186 4375e020 Stavros Sachtouris

1187 4375e020 Stavros Sachtouris
        :param delimiter: (str)
1188 4375e020 Stavros Sachtouris
        """
1189 277ca4ed Dionysis Zindros
        self._assert_container()
1190 5655d560 Stavros Sachtouris
        r = self.object_delete(obj, until=until, delimiter=delimiter)
1191 5655d560 Stavros Sachtouris
        return r.headers
1192 a298f2ab Stavros Sachtouris
1193 4375e020 Stavros Sachtouris
    def set_object_meta(self, obj, metapairs):
1194 4375e020 Stavros Sachtouris
        """
1195 4375e020 Stavros Sachtouris
        :param obj: (str) remote object path
1196 4375e020 Stavros Sachtouris

1197 4375e020 Stavros Sachtouris
        :param metapairs: (dict) {key1:val1, key2:val2, ...}
1198 4375e020 Stavros Sachtouris
        """
1199 af3b2b36 Stavros Sachtouris
        assert(type(metapairs) is dict)
1200 915b99b5 Stavros Sachtouris
        r = self.object_post(obj, update=True, metadata=metapairs)
1201 915b99b5 Stavros Sachtouris
        return r.headers
1202 87688514 Stavros Sachtouris
1203 bc223d91 Stavros Sachtouris
    def del_object_meta(self, obj, metakey):
1204 4375e020 Stavros Sachtouris
        """
1205 4375e020 Stavros Sachtouris
        :param obj: (str) remote object path
1206 bc223d91 Stavros Sachtouris

1207 bc223d91 Stavros Sachtouris
        :param metakey: (str) metadatum key
1208 4375e020 Stavros Sachtouris
        """
1209 915b99b5 Stavros Sachtouris
        r = self.object_post(obj, update=True, metadata={metakey: ''})
1210 915b99b5 Stavros Sachtouris
        return r.headers
1211 6de1f262 Stavros Sachtouris
1212 bc223d91 Stavros Sachtouris
    def publish_object(self, obj):
1213 bc223d91 Stavros Sachtouris
        """
1214 bc223d91 Stavros Sachtouris
        :param obj: (str) remote object path
1215 5260a313 Stavros Sachtouris

1216 5260a313 Stavros Sachtouris
        :returns: (str) access url
1217 bc223d91 Stavros Sachtouris
        """
1218 c2b5da2f Stavros Sachtouris
        self.object_post(obj, update=True, public=True)
1219 5260a313 Stavros Sachtouris
        info = self.get_object_info(obj)
1220 fa382f9e Stavros Sachtouris
        return info['x-object-public']
1221 93542587 Stavros Sachtouris
        pref, sep, rest = self.base_url.partition('//')
1222 93542587 Stavros Sachtouris
        base = rest.split('/')[0]
1223 f847a0cc Stavros Sachtouris
        return '%s%s%s/%s' % (pref, sep, base, info['x-object-public'])
1224 87688514 Stavros Sachtouris
1225 bc223d91 Stavros Sachtouris
    def unpublish_object(self, obj):
1226 bc223d91 Stavros Sachtouris
        """
1227 bc223d91 Stavros Sachtouris
        :param obj: (str) remote object path
1228 bc223d91 Stavros Sachtouris
        """
1229 5655d560 Stavros Sachtouris
        r = self.object_post(obj, update=True, public=False)
1230 5655d560 Stavros Sachtouris
        return r.headers
1231 28470086 Stavros Sachtouris
1232 8af4cc0b Stavros Sachtouris
    def get_object_info(self, obj, version=None):
1233 bc223d91 Stavros Sachtouris
        """
1234 bc223d91 Stavros Sachtouris
        :param obj: (str) remote object path
1235 bc223d91 Stavros Sachtouris

1236 bc223d91 Stavros Sachtouris
        :param version: (str)
1237 bc223d91 Stavros Sachtouris

1238 bc223d91 Stavros Sachtouris
        :returns: (dict)
1239 bc223d91 Stavros Sachtouris
        """
1240 ca092af4 Stavros Sachtouris
        try:
1241 ca092af4 Stavros Sachtouris
            r = self.object_head(obj, version=version)
1242 ca092af4 Stavros Sachtouris
            return r.headers
1243 ca092af4 Stavros Sachtouris
        except ClientError as ce:
1244 ca092af4 Stavros Sachtouris
            if ce.status == 404:
1245 723e9d47 Stavros Sachtouris
                raise ClientError('Object %s not found' % obj, status=404)
1246 ca092af4 Stavros Sachtouris
            raise
1247 8af4cc0b Stavros Sachtouris
1248 8af4cc0b Stavros Sachtouris
    def get_object_meta(self, obj, version=None):
1249 bc223d91 Stavros Sachtouris
        """
1250 bc223d91 Stavros Sachtouris
        :param obj: (str) remote object path
1251 bc223d91 Stavros Sachtouris

1252 bc223d91 Stavros Sachtouris
        :param version: (str)
1253 bc223d91 Stavros Sachtouris

1254 bc223d91 Stavros Sachtouris
        :returns: (dict)
1255 bc223d91 Stavros Sachtouris
        """
1256 24ff0a35 Stavros Sachtouris
        return filter_in(
1257 24ff0a35 Stavros Sachtouris
            self.get_object_info(obj, version=version),
1258 3dabe5d2 Stavros Sachtouris
            'X-Object-Meta')
1259 8af4cc0b Stavros Sachtouris
1260 bc223d91 Stavros Sachtouris
    def get_object_sharing(self, obj):
1261 bc223d91 Stavros Sachtouris
        """
1262 bc223d91 Stavros Sachtouris
        :param obj: (str) remote object path
1263 bc223d91 Stavros Sachtouris

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

1287 bc223d91 Stavros Sachtouris
        :param obj: (str) remote object path
1288 bc223d91 Stavros Sachtouris

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

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

1297 915b99b5 Stavros Sachtouris
        :returns: (dict) response headers
1298 28470086 Stavros Sachtouris
        """
1299 4067cdaf Stavros Sachtouris
1300 00336c85 Stavros Sachtouris
        perms = dict(read=read_permission or '', write=write_permission or '')
1301 915b99b5 Stavros Sachtouris
        r = self.object_post(obj, update=True, permissions=perms)
1302 915b99b5 Stavros Sachtouris
        return r.headers
1303 28470086 Stavros Sachtouris
1304 bc223d91 Stavros Sachtouris
    def del_object_sharing(self, obj):
1305 bc223d91 Stavros Sachtouris
        """
1306 bc223d91 Stavros Sachtouris
        :param obj: (str) remote object path
1307 bc223d91 Stavros Sachtouris
        """
1308 915b99b5 Stavros Sachtouris
        return self.set_object_sharing(obj)
1309 bc223d91 Stavros Sachtouris
1310 bc223d91 Stavros Sachtouris
    def append_object(self, obj, source_file, upload_cb=None):
1311 bc223d91 Stavros Sachtouris
        """
1312 bc223d91 Stavros Sachtouris
        :param obj: (str) remote object path
1313 bc223d91 Stavros Sachtouris

1314 bc223d91 Stavros Sachtouris
        :param source_file: open file descriptor
1315 f49084df Stavros Sachtouris

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

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

1373 915b99b5 Stavros Sachtouris
        :returns: (dict) response headers
1374 bc223d91 Stavros Sachtouris
        """
1375 b5b4441e Stavros Sachtouris
        ctype = self.get_object_info(obj)['content-type']
1376 915b99b5 Stavros Sachtouris
        r = self.object_post(
1377 24ff0a35 Stavros Sachtouris
            obj,
1378 3dabe5d2 Stavros Sachtouris
            update=True,
1379 3dabe5d2 Stavros Sachtouris
            content_range='bytes 0-%s/*' % upto_bytes,
1380 b5b4441e Stavros Sachtouris
            content_type=ctype,
1381 3dabe5d2 Stavros Sachtouris
            object_bytes=upto_bytes,
1382 bc223d91 Stavros Sachtouris
            source_object=path4url(self.container, obj))
1383 915b99b5 Stavros Sachtouris
        return r.headers
1384 ee62607e Stavros Sachtouris
1385 614a20dc Stavros Sachtouris
    def overwrite_object(
1386 614a20dc Stavros Sachtouris
            self, obj, start, end, source_file,
1387 614a20dc Stavros Sachtouris
            content_type=None, upload_cb=None):
1388 bc223d91 Stavros Sachtouris
        """Overwrite a part of an object from local source file
1389 bc223d91 Stavros Sachtouris

1390 bc223d91 Stavros Sachtouris
        :param obj: (str) remote object path
1391 bc223d91 Stavros Sachtouris

1392 bc223d91 Stavros Sachtouris
        :param start: (int) position in bytes to start overwriting from
1393 bc223d91 Stavros Sachtouris

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

1396 bc223d91 Stavros Sachtouris
        :param source_file: open file descriptor
1397 bc223d91 Stavros Sachtouris

1398 614a20dc Stavros Sachtouris
        :param content_type: (str) default: application/octet-stream
1399 614a20dc Stavros Sachtouris

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

1453 bc223d91 Stavros Sachtouris
        :param src_object: (str) source object path
1454 bc223d91 Stavros Sachtouris

1455 bc223d91 Stavros Sachtouris
        :param dst_container: (str) destination container
1456 bc223d91 Stavros Sachtouris

1457 bc223d91 Stavros Sachtouris
        :param dst_object: (str) destination object path
1458 bc223d91 Stavros Sachtouris

1459 bc223d91 Stavros Sachtouris
        :param source_version: (str) source object version
1460 bc223d91 Stavros Sachtouris

1461 3a066af4 Stavros Sachtouris
        :param source_account: (str) account to copy from
1462 3a066af4 Stavros Sachtouris

1463 bc223d91 Stavros Sachtouris
        :param public: (bool)
1464 bc223d91 Stavros Sachtouris

1465 bc223d91 Stavros Sachtouris
        :param content_type: (str)
1466 bc223d91 Stavros Sachtouris

1467 bc223d91 Stavros Sachtouris
        :param delimiter: (str)
1468 55c75058 Stavros Sachtouris

1469 55c75058 Stavros Sachtouris
        :returns: (dict) response headers
1470 bc223d91 Stavros Sachtouris
        """
1471 277ca4ed Dionysis Zindros
        self._assert_account()
1472 7d420701 Stavros Sachtouris
        self.container = dst_container
1473 7d420701 Stavros Sachtouris
        src_path = path4url(src_container, src_object)
1474 55c75058 Stavros Sachtouris
        r = self.object_put(
1475 33487500 Stavros Sachtouris
            dst_object or src_object,
1476 3dabe5d2 Stavros Sachtouris
            success=201,
1477 3dabe5d2 Stavros Sachtouris
            copy_from=src_path,
1478 3dabe5d2 Stavros Sachtouris
            content_length=0,
1479 3dabe5d2 Stavros Sachtouris
            source_version=source_version,
1480 3a066af4 Stavros Sachtouris
            source_account=source_account,
1481 3dabe5d2 Stavros Sachtouris
            public=public,
1482 3dabe5d2 Stavros Sachtouris
            content_type=content_type,
1483 7d420701 Stavros Sachtouris
            delimiter=delimiter)
1484 55c75058 Stavros Sachtouris
        return r.headers
1485 a5e0629d Stavros Sachtouris
1486 24ff0a35 Stavros Sachtouris
    def move_object(
1487 24ff0a35 Stavros Sachtouris
            self, src_container, src_object, dst_container,
1488 24ff0a35 Stavros Sachtouris
            dst_object=False,
1489 4f266635 Stavros Sachtouris
            source_account=None,
1490 24ff0a35 Stavros Sachtouris
            source_version=None,
1491 24ff0a35 Stavros Sachtouris
            public=False,
1492 24ff0a35 Stavros Sachtouris
            content_type=None,
1493 24ff0a35 Stavros Sachtouris
            delimiter=None):
1494 bc223d91 Stavros Sachtouris
        """
1495 bc223d91 Stavros Sachtouris
        :param src_container: (str) source container
1496 bc223d91 Stavros Sachtouris

1497 bc223d91 Stavros Sachtouris
        :param src_object: (str) source object path
1498 bc223d91 Stavros Sachtouris

1499 bc223d91 Stavros Sachtouris
        :param dst_container: (str) destination container
1500 bc223d91 Stavros Sachtouris

1501 bc223d91 Stavros Sachtouris
        :param dst_object: (str) destination object path
1502 bc223d91 Stavros Sachtouris

1503 4f266635 Stavros Sachtouris
        :param source_account: (str) account to move from
1504 4f266635 Stavros Sachtouris

1505 bc223d91 Stavros Sachtouris
        :param source_version: (str) source object version
1506 bc223d91 Stavros Sachtouris

1507 bc223d91 Stavros Sachtouris
        :param public: (bool)
1508 bc223d91 Stavros Sachtouris

1509 bc223d91 Stavros Sachtouris
        :param content_type: (str)
1510 bc223d91 Stavros Sachtouris

1511 bc223d91 Stavros Sachtouris
        :param delimiter: (str)
1512 55c75058 Stavros Sachtouris

1513 55c75058 Stavros Sachtouris
        :returns: (dict) response headers
1514 bc223d91 Stavros Sachtouris
        """
1515 277ca4ed Dionysis Zindros
        self._assert_account()
1516 a5e0629d Stavros Sachtouris
        self.container = dst_container
1517 a5e0629d Stavros Sachtouris
        dst_object = dst_object or src_object
1518 a5e0629d Stavros Sachtouris
        src_path = path4url(src_container, src_object)
1519 55c75058 Stavros Sachtouris
        r = self.object_put(
1520 24ff0a35 Stavros Sachtouris
            dst_object,
1521 3dabe5d2 Stavros Sachtouris
            success=201,
1522 3dabe5d2 Stavros Sachtouris
            move_from=src_path,
1523 3dabe5d2 Stavros Sachtouris
            content_length=0,
1524 4f266635 Stavros Sachtouris
            source_account=source_account,
1525 3dabe5d2 Stavros Sachtouris
            source_version=source_version,
1526 3dabe5d2 Stavros Sachtouris
            public=public,
1527 3dabe5d2 Stavros Sachtouris
            content_type=content_type,
1528 a23f6ffe Stavros Sachtouris
            delimiter=delimiter)
1529 55c75058 Stavros Sachtouris
        return r.headers
1530 a23f6ffe Stavros Sachtouris
1531 a23f6ffe Stavros Sachtouris
    def get_sharing_accounts(self, limit=None, marker=None, *args, **kwargs):
1532 bc223d91 Stavros Sachtouris
        """Get accounts that share with self.account
1533 bc223d91 Stavros Sachtouris

1534 bc223d91 Stavros Sachtouris
        :param limit: (str)
1535 bc223d91 Stavros Sachtouris

1536 bc223d91 Stavros Sachtouris
        :param marker: (str)
1537 bc223d91 Stavros Sachtouris

1538 bc223d91 Stavros Sachtouris
        :returns: (dict)
1539 bc223d91 Stavros Sachtouris
        """
1540 277ca4ed Dionysis Zindros
        self._assert_account()
1541 a23f6ffe Stavros Sachtouris
1542 3dabe5d2 Stavros Sachtouris
        self.set_param('format', 'json')
1543 3dabe5d2 Stavros Sachtouris
        self.set_param('limit', limit, iff=limit is not None)
1544 3dabe5d2 Stavros Sachtouris
        self.set_param('marker', marker, iff=marker is not None)
1545 a23f6ffe Stavros Sachtouris
1546 a23f6ffe Stavros Sachtouris
        path = ''
1547 a23f6ffe Stavros Sachtouris
        success = kwargs.pop('success', (200, 204))
1548 3dabe5d2 Stavros Sachtouris
        r = self.get(path, *args, success=success, **kwargs)
1549 38dc5d2f Stavros Sachtouris
        return r.json
1550 38dc5d2f Stavros Sachtouris
1551 bc223d91 Stavros Sachtouris
    def get_object_versionlist(self, obj):
1552 bc223d91 Stavros Sachtouris
        """
1553 bc223d91 Stavros Sachtouris
        :param obj: (str) remote object path
1554 bc223d91 Stavros Sachtouris

1555 bc223d91 Stavros Sachtouris
        :returns: (list)
1556 bc223d91 Stavros Sachtouris
        """
1557 277ca4ed Dionysis Zindros
        self._assert_container()
1558 bc223d91 Stavros Sachtouris
        r = self.object_get(obj, format='json', version='list')
1559 38dc5d2f Stavros Sachtouris
        return r.json['versions']