Statistics
| Branch: | Tag: | Revision:

root / kamaki / clients / pithos.py @ 2b74ab4a

History | View | Annotate | Download (26.6 kB)

1 0c6d7489 Giorgos Verigakis
# Copyright 2011-2012 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 a91e0293 Giorgos Verigakis
40 64ab4c13 Stavros Sachtouris
from binascii import hexlify
41 6a0b1658 Giorgos Verigakis
42 2b74ab4a Stavros Sachtouris
from kamaki.clients import SilentEvent
43 c270fe96 Stavros Sachtouris
from kamaki.clients.pithos_rest_api import PithosRestAPI
44 c270fe96 Stavros Sachtouris
from kamaki.clients.storage import ClientError
45 c270fe96 Stavros Sachtouris
from kamaki.clients.utils import path4url, filter_in
46 1785ad41 Stavros Sachtouris
from StringIO import StringIO
47 6a0b1658 Giorgos Verigakis
48 3dabe5d2 Stavros Sachtouris
49 6a0b1658 Giorgos Verigakis
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 642f1bbd Stavros Sachtouris
def _range_up(start, end, a_range):
56 642f1bbd Stavros Sachtouris
    if a_range:
57 642f1bbd Stavros Sachtouris
        (rstart, rend) = a_range.split('-')
58 642f1bbd Stavros Sachtouris
        (rstart, rend) = (int(rstart), int(rend))
59 642f1bbd Stavros Sachtouris
        if rstart > end or rend < start:
60 3dabe5d2 Stavros Sachtouris
            return (0, 0)
61 642f1bbd Stavros Sachtouris
        if rstart > start:
62 642f1bbd Stavros Sachtouris
            start = rstart
63 642f1bbd Stavros Sachtouris
        if rend < end:
64 642f1bbd Stavros Sachtouris
            end = rend
65 642f1bbd Stavros Sachtouris
    return (start, end)
66 642f1bbd Stavros Sachtouris
67 3dabe5d2 Stavros Sachtouris
68 64ab4c13 Stavros Sachtouris
class PithosClient(PithosRestAPI):
69 d2cea1e2 Giorgos Verigakis
    """GRNet Pithos API client"""
70 a91e0293 Giorgos Verigakis
71 e02728f9 Stavros Sachtouris
    _thread_exceptions = []
72 e02728f9 Stavros Sachtouris
73 3dabe5d2 Stavros Sachtouris
    def __init__(self, base_url, token, account=None, container=None):
74 3dabe5d2 Stavros Sachtouris
        super(PithosClient, self).__init__(base_url, token, account, container)
75 435008b6 Stavros Sachtouris
76 17edd3f4 Stavros Sachtouris
    def purge_container(self):
77 6ce9fc72 Stavros Sachtouris
        r = self.container_delete(until=unicode(time()))
78 6ce9fc72 Stavros Sachtouris
        r.release()
79 3dabe5d2 Stavros Sachtouris
80 3dabe5d2 Stavros Sachtouris
    def upload_object_unchunked(self, obj, f,
81 3dabe5d2 Stavros Sachtouris
        withHashFile=False,
82 3dabe5d2 Stavros Sachtouris
        size=None,
83 3dabe5d2 Stavros Sachtouris
        etag=None,
84 3dabe5d2 Stavros Sachtouris
        content_encoding=None,
85 3dabe5d2 Stavros Sachtouris
        content_disposition=None,
86 3dabe5d2 Stavros Sachtouris
        content_type=None,
87 3dabe5d2 Stavros Sachtouris
        sharing=None,
88 65a45524 Stavros Sachtouris
        public=None):
89 65a45524 Stavros Sachtouris
        self.assert_container()
90 65a45524 Stavros Sachtouris
91 65a45524 Stavros Sachtouris
        if withHashFile:
92 65a45524 Stavros Sachtouris
            data = f.read()
93 65a45524 Stavros Sachtouris
            try:
94 65a45524 Stavros Sachtouris
                import json
95 65a45524 Stavros Sachtouris
                data = json.dumps(json.loads(data))
96 65a45524 Stavros Sachtouris
            except ValueError:
97 3dabe5d2 Stavros Sachtouris
                raise ClientError(message='"%s" is not json-formated' % f.name,
98 3dabe5d2 Stavros Sachtouris
                    status=1)
99 65a45524 Stavros Sachtouris
            except SyntaxError:
100 3dabe5d2 Stavros Sachtouris
                raise ClientError(message='"%s" is not a valid hashmap file'\
101 3dabe5d2 Stavros Sachtouris
                % f.name, status=1)
102 65a45524 Stavros Sachtouris
            f = StringIO(data)
103 65a45524 Stavros Sachtouris
        data = f.read(size) if size is not None else f.read()
104 3dabe5d2 Stavros Sachtouris
        r = self.object_put(obj,
105 3dabe5d2 Stavros Sachtouris
            data=data,
106 3dabe5d2 Stavros Sachtouris
            etag=etag,
107 3dabe5d2 Stavros Sachtouris
            content_encoding=content_encoding,
108 3dabe5d2 Stavros Sachtouris
            content_disposition=content_disposition,
109 3dabe5d2 Stavros Sachtouris
            content_type=content_type,
110 3dabe5d2 Stavros Sachtouris
            permissions=sharing,
111 3dabe5d2 Stavros Sachtouris
            public=public,
112 3dabe5d2 Stavros Sachtouris
            success=201)
113 6ce9fc72 Stavros Sachtouris
        r.release()
114 3dabe5d2 Stavros Sachtouris
115 3dabe5d2 Stavros Sachtouris
    # upload_* auxiliary methods
116 435008b6 Stavros Sachtouris
    def put_block_async(self, data, hash):
117 e02728f9 Stavros Sachtouris
        event = SilentEvent(method=self.put_block, data=data, hash=hash)
118 e02728f9 Stavros Sachtouris
        event.start()
119 e02728f9 Stavros Sachtouris
        return event
120 435008b6 Stavros Sachtouris
121 53129af9 Giorgos Verigakis
    def put_block(self, data, hash):
122 3dabe5d2 Stavros Sachtouris
        r = self.container_post(update=True,
123 3dabe5d2 Stavros Sachtouris
            content_type='application/octet-stream',
124 3dabe5d2 Stavros Sachtouris
            content_length=len(data),
125 3dabe5d2 Stavros Sachtouris
            data=data,
126 3dabe5d2 Stavros Sachtouris
            format='json')
127 c2236544 Stavros Sachtouris
        assert r.json[0] == hash, 'Local hash does not match server'
128 3dabe5d2 Stavros Sachtouris
129 3dabe5d2 Stavros Sachtouris
    def create_object_by_manifestation(self, obj,
130 3dabe5d2 Stavros Sachtouris
        etag=None,
131 3dabe5d2 Stavros Sachtouris
        content_encoding=None,
132 3dabe5d2 Stavros Sachtouris
        content_disposition=None,
133 3dabe5d2 Stavros Sachtouris
        content_type=None,
134 3dabe5d2 Stavros Sachtouris
        sharing=None,
135 3dabe5d2 Stavros Sachtouris
        public=None):
136 65a45524 Stavros Sachtouris
        self.assert_container()
137 3dabe5d2 Stavros Sachtouris
        r = self.object_put(obj,
138 3dabe5d2 Stavros Sachtouris
            content_length=0,
139 3dabe5d2 Stavros Sachtouris
            etag=etag,
140 3dabe5d2 Stavros Sachtouris
            content_encoding=content_encoding,
141 3dabe5d2 Stavros Sachtouris
            content_disposition=content_disposition,
142 3dabe5d2 Stavros Sachtouris
            content_type=content_type,
143 3dabe5d2 Stavros Sachtouris
            permissions=sharing,
144 3dabe5d2 Stavros Sachtouris
            public=public,
145 3dabe5d2 Stavros Sachtouris
            manifest='%s/%s' % (self.container, obj))
146 6ce9fc72 Stavros Sachtouris
        r.release()
147 3dabe5d2 Stavros Sachtouris
148 64ab4c13 Stavros Sachtouris
    def _get_file_block_info(self, fileobj, size=None):
149 2f749e6e Stavros Sachtouris
        meta = self.get_container_info()
150 435008b6 Stavros Sachtouris
        blocksize = int(meta['x-container-block-size'])
151 435008b6 Stavros Sachtouris
        blockhash = meta['x-container-block-hash']
152 64ab4c13 Stavros Sachtouris
        size = size if size is not None else fstat(fileobj.fileno()).st_size
153 435008b6 Stavros Sachtouris
        nblocks = 1 + (size - 1) // blocksize
154 64ab4c13 Stavros Sachtouris
        return (blocksize, blockhash, size, nblocks)
155 64ab4c13 Stavros Sachtouris
156 3dabe5d2 Stavros Sachtouris
    def _get_missing_hashes(self, obj, json,
157 3dabe5d2 Stavros Sachtouris
        size=None,
158 3dabe5d2 Stavros Sachtouris
        format='json',
159 3dabe5d2 Stavros Sachtouris
        hashmap=True,
160 3dabe5d2 Stavros Sachtouris
        content_type=None,
161 3dabe5d2 Stavros Sachtouris
        etag=None,
162 3dabe5d2 Stavros Sachtouris
        content_encoding=None,
163 3dabe5d2 Stavros Sachtouris
        content_disposition=None,
164 3dabe5d2 Stavros Sachtouris
        permissions=None,
165 3dabe5d2 Stavros Sachtouris
        public=None,
166 3dabe5d2 Stavros Sachtouris
        success=(201, 409)):
167 3dabe5d2 Stavros Sachtouris
        r = self.object_put(obj,
168 3dabe5d2 Stavros Sachtouris
            format='json',
169 3dabe5d2 Stavros Sachtouris
            hashmap=True,
170 3dabe5d2 Stavros Sachtouris
            content_type=content_type,
171 3dabe5d2 Stavros Sachtouris
            json=json,
172 3dabe5d2 Stavros Sachtouris
            etag=etag,
173 3dabe5d2 Stavros Sachtouris
            content_encoding=content_encoding,
174 3dabe5d2 Stavros Sachtouris
            content_disposition=content_disposition,
175 3dabe5d2 Stavros Sachtouris
            permissions=permissions,
176 3dabe5d2 Stavros Sachtouris
            public=public,
177 64ab4c13 Stavros Sachtouris
            success=success)
178 64ab4c13 Stavros Sachtouris
        if r.status_code == 201:
179 6ce9fc72 Stavros Sachtouris
            r.release()
180 64ab4c13 Stavros Sachtouris
            return None
181 64ab4c13 Stavros Sachtouris
        return r.json
182 435008b6 Stavros Sachtouris
183 3dabe5d2 Stavros Sachtouris
    def _caclulate_uploaded_blocks(self,
184 3dabe5d2 Stavros Sachtouris
        blocksize,
185 3dabe5d2 Stavros Sachtouris
        blockhash,
186 3dabe5d2 Stavros Sachtouris
        size,
187 3dabe5d2 Stavros Sachtouris
        nblocks,
188 3dabe5d2 Stavros Sachtouris
        hashes,
189 3dabe5d2 Stavros Sachtouris
        hmap,
190 3dabe5d2 Stavros Sachtouris
        fileobj,
191 64ab4c13 Stavros Sachtouris
        hash_cb=None):
192 3dabe5d2 Stavros Sachtouris
        offset = 0
193 435008b6 Stavros Sachtouris
        if hash_cb:
194 435008b6 Stavros Sachtouris
            hash_gen = hash_cb(nblocks)
195 435008b6 Stavros Sachtouris
            hash_gen.next()
196 435008b6 Stavros Sachtouris
197 435008b6 Stavros Sachtouris
        for i in range(nblocks):
198 64ab4c13 Stavros Sachtouris
            block = fileobj.read(min(blocksize, size - offset))
199 435008b6 Stavros Sachtouris
            bytes = len(block)
200 435008b6 Stavros Sachtouris
            hash = pithos_hash(block, blockhash)
201 435008b6 Stavros Sachtouris
            hashes.append(hash)
202 5b263ba2 Stavros Sachtouris
            hmap[hash] = (offset, bytes)
203 435008b6 Stavros Sachtouris
            offset += bytes
204 435008b6 Stavros Sachtouris
            if hash_cb:
205 435008b6 Stavros Sachtouris
                hash_gen.next()
206 435008b6 Stavros Sachtouris
        assert offset == size
207 435008b6 Stavros Sachtouris
208 64ab4c13 Stavros Sachtouris
    def _upload_missing_blocks(self, missing, hmap, fileobj, upload_cb=None):
209 2b74ab4a Stavros Sachtouris
        """upload missing blocks asynchronously. 
210 64ab4c13 Stavros Sachtouris
        """
211 435008b6 Stavros Sachtouris
        if upload_cb:
212 435008b6 Stavros Sachtouris
            upload_gen = upload_cb(len(missing))
213 435008b6 Stavros Sachtouris
            upload_gen.next()
214 435008b6 Stavros Sachtouris
215 cad39033 Stavros Sachtouris
        self._init_thread_limit()
216 e9abe82b Stavros Sachtouris
217 435008b6 Stavros Sachtouris
        flying = []
218 435008b6 Stavros Sachtouris
        for hash in missing:
219 5b263ba2 Stavros Sachtouris
            offset, bytes = hmap[hash]
220 64ab4c13 Stavros Sachtouris
            fileobj.seek(offset)
221 64ab4c13 Stavros Sachtouris
            data = fileobj.read(bytes)
222 435008b6 Stavros Sachtouris
            r = self.put_block_async(data, hash)
223 435008b6 Stavros Sachtouris
            flying.append(r)
224 e02728f9 Stavros Sachtouris
            unfinished = []
225 f27ed9a0 Stavros Sachtouris
            for i, thread in enumerate(flying):
226 e9abe82b Stavros Sachtouris
227 cad39033 Stavros Sachtouris
                unfinished = self._watch_thread_limit(unfinished)
228 e9abe82b Stavros Sachtouris
229 e02728f9 Stavros Sachtouris
                if thread.isAlive() or thread.exception:
230 e02728f9 Stavros Sachtouris
                    unfinished.append(thread)
231 e02728f9 Stavros Sachtouris
                else:
232 435008b6 Stavros Sachtouris
                    if upload_cb:
233 435008b6 Stavros Sachtouris
                        upload_gen.next()
234 e02728f9 Stavros Sachtouris
            flying = unfinished
235 e02728f9 Stavros Sachtouris
236 e02728f9 Stavros Sachtouris
        for thread in flying:
237 e02728f9 Stavros Sachtouris
            thread.join()
238 56f0908a Stavros Sachtouris
239 19f4195f Stavros Sachtouris
        failures = [r for r in flying if r.exception]
240 a62c75d1 Stavros Sachtouris
        if len(failures):
241 e02728f9 Stavros Sachtouris
            details = ', '.join([' (%s).%s' % (i, r.exception)\
242 3dabe5d2 Stavros Sachtouris
                for i, r in enumerate(failures)])
243 3dabe5d2 Stavros Sachtouris
            raise ClientError(message="Block uploading failed",
244 3dabe5d2 Stavros Sachtouris
                status=505,
245 3dabe5d2 Stavros Sachtouris
                details=details)
246 3dabe5d2 Stavros Sachtouris
247 f27ed9a0 Stavros Sachtouris
        while upload_cb:
248 f27ed9a0 Stavros Sachtouris
            try:
249 f27ed9a0 Stavros Sachtouris
                upload_gen.next()
250 f27ed9a0 Stavros Sachtouris
            except StopIteration:
251 f27ed9a0 Stavros Sachtouris
                break
252 f27ed9a0 Stavros Sachtouris
253 3dabe5d2 Stavros Sachtouris
    def upload_object(self, obj, f,
254 3dabe5d2 Stavros Sachtouris
        size=None,
255 3dabe5d2 Stavros Sachtouris
        hash_cb=None,
256 3dabe5d2 Stavros Sachtouris
        upload_cb=None,
257 3dabe5d2 Stavros Sachtouris
        etag=None,
258 3dabe5d2 Stavros Sachtouris
        content_encoding=None,
259 3dabe5d2 Stavros Sachtouris
        content_disposition=None,
260 3dabe5d2 Stavros Sachtouris
        content_type=None,
261 3dabe5d2 Stavros Sachtouris
        sharing=None,
262 64ab4c13 Stavros Sachtouris
        public=None):
263 56f0908a Stavros Sachtouris
        self.assert_container()
264 56f0908a Stavros Sachtouris
265 64ab4c13 Stavros Sachtouris
        #init
266 3dabe5d2 Stavros Sachtouris
        block_info = (blocksize, blockhash, size, nblocks) =\
267 3dabe5d2 Stavros Sachtouris
            self._get_file_block_info(f, size)
268 64ab4c13 Stavros Sachtouris
        (hashes, hmap, offset) = ([], {}, 0)
269 3dabe5d2 Stavros Sachtouris
        if content_type is None:
270 3dabe5d2 Stavros Sachtouris
            content_type = 'application/octet-stream'
271 64ab4c13 Stavros Sachtouris
272 3dabe5d2 Stavros Sachtouris
        self._caclulate_uploaded_blocks(*block_info,
273 3dabe5d2 Stavros Sachtouris
            hashes=hashes,
274 3dabe5d2 Stavros Sachtouris
            hmap=hmap,
275 3dabe5d2 Stavros Sachtouris
            fileobj=f,
276 64ab4c13 Stavros Sachtouris
            hash_cb=hash_cb)
277 64ab4c13 Stavros Sachtouris
278 64ab4c13 Stavros Sachtouris
        hashmap = dict(bytes=size, hashes=hashes)
279 3dabe5d2 Stavros Sachtouris
        missing = self._get_missing_hashes(obj, hashmap,
280 3dabe5d2 Stavros Sachtouris
            content_type=content_type,
281 3dabe5d2 Stavros Sachtouris
            size=size,
282 3dabe5d2 Stavros Sachtouris
            etag=etag,
283 3dabe5d2 Stavros Sachtouris
            content_encoding=content_encoding,
284 3dabe5d2 Stavros Sachtouris
            content_disposition=content_disposition,
285 3dabe5d2 Stavros Sachtouris
            permissions=sharing,
286 3dabe5d2 Stavros Sachtouris
            public=public)
287 64ab4c13 Stavros Sachtouris
288 64ab4c13 Stavros Sachtouris
        if missing is None:
289 64ab4c13 Stavros Sachtouris
            return
290 f27ed9a0 Stavros Sachtouris
        try:
291 f27ed9a0 Stavros Sachtouris
            self._upload_missing_blocks(missing, hmap, f, upload_cb=upload_cb)
292 f27ed9a0 Stavros Sachtouris
        except KeyboardInterrupt:
293 f27ed9a0 Stavros Sachtouris
            print('- - - wait for threads to finish')
294 f27ed9a0 Stavros Sachtouris
            for thread in activethreads():
295 f27ed9a0 Stavros Sachtouris
                thread.join()
296 f27ed9a0 Stavros Sachtouris
            raise
297 f27ed9a0 Stavros Sachtouris
298 f27ed9a0 Stavros Sachtouris
        r = self.object_put(
299 f27ed9a0 Stavros Sachtouris
            obj,
300 3dabe5d2 Stavros Sachtouris
            format='json',
301 3dabe5d2 Stavros Sachtouris
            hashmap=True,
302 3dabe5d2 Stavros Sachtouris
            content_type=content_type,
303 3dabe5d2 Stavros Sachtouris
            json=hashmap,
304 3dabe5d2 Stavros Sachtouris
            success=201)
305 6ce9fc72 Stavros Sachtouris
        r.release()
306 3dabe5d2 Stavros Sachtouris
307 f27ed9a0 Stavros Sachtouris
    # download_* auxiliary methods
308 fbfee225 Stavros Sachtouris
    def _get_remote_blocks_info(self, obj, **restargs):
309 56f0908a Stavros Sachtouris
        #retrieve object hashmap
310 3dabe5d2 Stavros Sachtouris
        myrange = restargs.pop('data_range', None)
311 fbfee225 Stavros Sachtouris
        hashmap = self.get_object_hashmap(obj, **restargs)
312 642f1bbd Stavros Sachtouris
        restargs['data_range'] = myrange
313 56f0908a Stavros Sachtouris
        blocksize = int(hashmap['block_size'])
314 56f0908a Stavros Sachtouris
        blockhash = hashmap['block_hash']
315 56f0908a Stavros Sachtouris
        total_size = hashmap['bytes']
316 fbfee225 Stavros Sachtouris
        #assert total_size/blocksize + 1 == len(hashmap['hashes'])
317 56f0908a Stavros Sachtouris
        map_dict = {}
318 fbfee225 Stavros Sachtouris
        for i, h in enumerate(hashmap['hashes']):
319 fbfee225 Stavros Sachtouris
            map_dict[h] = i
320 fbfee225 Stavros Sachtouris
        return (blocksize, blockhash, total_size, hashmap['hashes'], map_dict)
321 64ab4c13 Stavros Sachtouris
322 3dabe5d2 Stavros Sachtouris
    def _dump_blocks_sync(self,
323 3dabe5d2 Stavros Sachtouris
        obj,
324 3dabe5d2 Stavros Sachtouris
        remote_hashes,
325 3dabe5d2 Stavros Sachtouris
        blocksize,
326 3dabe5d2 Stavros Sachtouris
        total_size,
327 3dabe5d2 Stavros Sachtouris
        dst,
328 3dabe5d2 Stavros Sachtouris
        range,
329 3dabe5d2 Stavros Sachtouris
        **restargs):
330 fbfee225 Stavros Sachtouris
        for blockid, blockhash in enumerate(remote_hashes):
331 fbfee225 Stavros Sachtouris
            if blockhash == None:
332 fbfee225 Stavros Sachtouris
                continue
333 3dabe5d2 Stavros Sachtouris
            start = blocksize * blockid
334 3dabe5d2 Stavros Sachtouris
            end = total_size - 1 if start + blocksize > total_size\
335 3dabe5d2 Stavros Sachtouris
                else start + blocksize - 1
336 642f1bbd Stavros Sachtouris
            (start, end) = _range_up(start, end, range)
337 3dabe5d2 Stavros Sachtouris
            restargs['data_range'] = 'bytes=%s-%s' % (start, end)
338 fbfee225 Stavros Sachtouris
            r = self.object_get(obj, success=(200, 206), **restargs)
339 fbfee225 Stavros Sachtouris
            self._cb_next()
340 fbfee225 Stavros Sachtouris
            dst.write(r.content)
341 fbfee225 Stavros Sachtouris
            dst.flush()
342 fbfee225 Stavros Sachtouris
343 fbfee225 Stavros Sachtouris
    def _get_block_async(self, obj, **restargs):
344 e02728f9 Stavros Sachtouris
        event = SilentEvent(self.object_get,
345 e02728f9 Stavros Sachtouris
            obj,
346 3dabe5d2 Stavros Sachtouris
            success=(200, 206),
347 3dabe5d2 Stavros Sachtouris
            **restargs)
348 e02728f9 Stavros Sachtouris
        event.start()
349 e02728f9 Stavros Sachtouris
        return event
350 fb0cd49a Stavros Sachtouris
351 48c3782c Stavros Sachtouris
    def _hash_from_file(self, fp, start, size, blockhash):
352 48c3782c Stavros Sachtouris
        fp.seek(start)
353 48c3782c Stavros Sachtouris
        block = fp.read(size)
354 48c3782c Stavros Sachtouris
        h = newhashlib(blockhash)
355 48c3782c Stavros Sachtouris
        h.update(block.strip('\x00'))
356 48c3782c Stavros Sachtouris
        return hexlify(h.digest())
357 48c3782c Stavros Sachtouris
358 e02728f9 Stavros Sachtouris
    def _thread2file(self,
359 e02728f9 Stavros Sachtouris
        flying,
360 3dabe5d2 Stavros Sachtouris
        local_file,
361 3dabe5d2 Stavros Sachtouris
        offset=0,
362 3dabe5d2 Stavros Sachtouris
        **restargs):
363 22fc09fb Stavros Sachtouris
        """write the results of a greenleted rest call to a file
364 3dabe5d2 Stavros Sachtouris
        @offset: the offset of the file up to blocksize
365 3dabe5d2 Stavros Sachtouris
            - e.g. if the range is 10-100, all
366 22fc09fb Stavros Sachtouris
        blocks will be written to normal_position - 10"""
367 fbfee225 Stavros Sachtouris
        finished = []
368 f27ed9a0 Stavros Sachtouris
        for i, (start, g) in enumerate(flying.items()):
369 e02728f9 Stavros Sachtouris
            if not g.isAlive():
370 fbfee225 Stavros Sachtouris
                if g.exception:
371 fbfee225 Stavros Sachtouris
                    raise g.exception
372 1785ad41 Stavros Sachtouris
                block = g.value.content
373 22fc09fb Stavros Sachtouris
                local_file.seek(start - offset)
374 fbfee225 Stavros Sachtouris
                local_file.write(block)
375 fbfee225 Stavros Sachtouris
                self._cb_next()
376 e02728f9 Stavros Sachtouris
                finished.append(flying.pop(start))
377 fbfee225 Stavros Sachtouris
        local_file.flush()
378 fbfee225 Stavros Sachtouris
        return finished
379 fbfee225 Stavros Sachtouris
380 3dabe5d2 Stavros Sachtouris
    def _dump_blocks_async(self,
381 3dabe5d2 Stavros Sachtouris
        obj,
382 3dabe5d2 Stavros Sachtouris
        remote_hashes,
383 3dabe5d2 Stavros Sachtouris
        blocksize,
384 3dabe5d2 Stavros Sachtouris
        total_size,
385 3dabe5d2 Stavros Sachtouris
        local_file,
386 3dabe5d2 Stavros Sachtouris
        blockhash=None,
387 3dabe5d2 Stavros Sachtouris
        resume=False,
388 3dabe5d2 Stavros Sachtouris
        filerange=None,
389 3dabe5d2 Stavros Sachtouris
        **restargs):
390 973fbcad Stavros Sachtouris
391 973fbcad Stavros Sachtouris
        file_size = fstat(local_file.fileno()).st_size if resume else 0
392 e02728f9 Stavros Sachtouris
        flying = {}
393 e02728f9 Stavros Sachtouris
        finished = []
394 22fc09fb Stavros Sachtouris
        offset = 0
395 22fc09fb Stavros Sachtouris
        if filerange is not None:
396 22fc09fb Stavros Sachtouris
            rstart = int(filerange.split('-')[0])
397 3dabe5d2 Stavros Sachtouris
            offset = rstart if blocksize > rstart else rstart % blocksize
398 f27ed9a0 Stavros Sachtouris
399 cad39033 Stavros Sachtouris
        self._init_thread_limit()
400 fbfee225 Stavros Sachtouris
        for block_hash, blockid in remote_hashes.items():
401 3dabe5d2 Stavros Sachtouris
            start = blocksize * blockid
402 3dabe5d2 Stavros Sachtouris
            if start < file_size\
403 f27ed9a0 Stavros Sachtouris
            and block_hash == self._hash_from_file(
404 f27ed9a0 Stavros Sachtouris
                    local_file,
405 f27ed9a0 Stavros Sachtouris
                    start,
406 f27ed9a0 Stavros Sachtouris
                    blocksize,
407 f27ed9a0 Stavros Sachtouris
                    blockhash):
408 3dabe5d2 Stavros Sachtouris
                self._cb_next()
409 3dabe5d2 Stavros Sachtouris
                continue
410 cad39033 Stavros Sachtouris
            self._watch_thread_limit(flying.values())
411 f27ed9a0 Stavros Sachtouris
            finished += self._thread2file(
412 f27ed9a0 Stavros Sachtouris
                flying,
413 f27ed9a0 Stavros Sachtouris
                local_file,
414 f27ed9a0 Stavros Sachtouris
                offset,
415 f27ed9a0 Stavros Sachtouris
                **restargs)
416 3dabe5d2 Stavros Sachtouris
            end = total_size - 1 if start + blocksize > total_size\
417 3dabe5d2 Stavros Sachtouris
                else start + blocksize - 1
418 22fc09fb Stavros Sachtouris
            (start, end) = _range_up(start, end, filerange)
419 22fc09fb Stavros Sachtouris
            if start == end:
420 22fc09fb Stavros Sachtouris
                self._cb_next()
421 22fc09fb Stavros Sachtouris
                continue
422 3dabe5d2 Stavros Sachtouris
            restargs['async_headers'] = {'Range': 'bytes=%s-%s' % (start, end)}
423 e02728f9 Stavros Sachtouris
            flying[start] = self._get_block_async(obj, **restargs)
424 fbfee225 Stavros Sachtouris
425 e02728f9 Stavros Sachtouris
        for thread in flying.values():
426 e02728f9 Stavros Sachtouris
            thread.join()
427 e02728f9 Stavros Sachtouris
        finished += self._thread2file(flying, local_file, offset, **restargs)
428 fbfee225 Stavros Sachtouris
429 3dabe5d2 Stavros Sachtouris
    def download_object(self,
430 3dabe5d2 Stavros Sachtouris
        obj,
431 3dabe5d2 Stavros Sachtouris
        dst,
432 3dabe5d2 Stavros Sachtouris
        download_cb=None,
433 3dabe5d2 Stavros Sachtouris
        version=None,
434 3dabe5d2 Stavros Sachtouris
        overide=False,
435 3dabe5d2 Stavros Sachtouris
        resume=False,
436 3dabe5d2 Stavros Sachtouris
        range=None,
437 3dabe5d2 Stavros Sachtouris
        if_match=None,
438 3dabe5d2 Stavros Sachtouris
        if_none_match=None,
439 3dabe5d2 Stavros Sachtouris
        if_modified_since=None,
440 fbfee225 Stavros Sachtouris
        if_unmodified_since=None):
441 fbfee225 Stavros Sachtouris
442 3dabe5d2 Stavros Sachtouris
        restargs = dict(version=version,
443 3dabe5d2 Stavros Sachtouris
            data_range=None if range is None else 'bytes=%s' % range,
444 fbfee225 Stavros Sachtouris
            if_match=if_match,
445 fbfee225 Stavros Sachtouris
            if_none_match=if_none_match,
446 fbfee225 Stavros Sachtouris
            if_modified_since=if_modified_since,
447 fbfee225 Stavros Sachtouris
            if_unmodified_since=if_unmodified_since)
448 fbfee225 Stavros Sachtouris
449 3dabe5d2 Stavros Sachtouris
        (blocksize,
450 fbfee225 Stavros Sachtouris
            blockhash,
451 fbfee225 Stavros Sachtouris
            total_size,
452 3dabe5d2 Stavros Sachtouris
            hash_list,
453 fbfee225 Stavros Sachtouris
            remote_hashes) = self._get_remote_blocks_info(obj, **restargs)
454 fbfee225 Stavros Sachtouris
        assert total_size >= 0
455 fbfee225 Stavros Sachtouris
456 fbfee225 Stavros Sachtouris
        if download_cb:
457 20c13fcc Stavros Sachtouris
            self.progress_bar_gen = download_cb(len(remote_hashes))
458 fbfee225 Stavros Sachtouris
            self._cb_next()
459 fbfee225 Stavros Sachtouris
460 fbfee225 Stavros Sachtouris
        if dst.isatty():
461 3dabe5d2 Stavros Sachtouris
            self._dump_blocks_sync(obj,
462 3dabe5d2 Stavros Sachtouris
                hash_list,
463 3dabe5d2 Stavros Sachtouris
                blocksize,
464 3dabe5d2 Stavros Sachtouris
                total_size,
465 3dabe5d2 Stavros Sachtouris
                dst,
466 3dabe5d2 Stavros Sachtouris
                range,
467 3dabe5d2 Stavros Sachtouris
                **restargs)
468 699d3bb1 Stavros Sachtouris
        else:
469 3dabe5d2 Stavros Sachtouris
            self._dump_blocks_async(obj,
470 3dabe5d2 Stavros Sachtouris
                remote_hashes,
471 3dabe5d2 Stavros Sachtouris
                blocksize,
472 3dabe5d2 Stavros Sachtouris
                total_size,
473 3dabe5d2 Stavros Sachtouris
                dst,
474 3dabe5d2 Stavros Sachtouris
                blockhash,
475 3dabe5d2 Stavros Sachtouris
                resume,
476 3dabe5d2 Stavros Sachtouris
                range,
477 3dabe5d2 Stavros Sachtouris
                **restargs)
478 22fc09fb Stavros Sachtouris
            if range is None:
479 22fc09fb Stavros Sachtouris
                dst.truncate(total_size)
480 5b263ba2 Stavros Sachtouris
481 fbfee225 Stavros Sachtouris
        self._complete_cb()
482 fbfee225 Stavros Sachtouris
483 fbfee225 Stavros Sachtouris
    #Command Progress Bar method
484 fbfee225 Stavros Sachtouris
    def _cb_next(self):
485 fbfee225 Stavros Sachtouris
        if hasattr(self, 'progress_bar_gen'):
486 fbfee225 Stavros Sachtouris
            try:
487 fbfee225 Stavros Sachtouris
                self.progress_bar_gen.next()
488 fbfee225 Stavros Sachtouris
            except:
489 fbfee225 Stavros Sachtouris
                pass
490 3dabe5d2 Stavros Sachtouris
491 fbfee225 Stavros Sachtouris
    def _complete_cb(self):
492 fbfee225 Stavros Sachtouris
        while True:
493 fbfee225 Stavros Sachtouris
            try:
494 fbfee225 Stavros Sachtouris
                self.progress_bar_gen.next()
495 fbfee225 Stavros Sachtouris
            except:
496 fbfee225 Stavros Sachtouris
                break
497 b1713259 Stavros Sachtouris
498 3dabe5d2 Stavros Sachtouris
    # Untested - except is download_object is tested first
499 3dabe5d2 Stavros Sachtouris
    def get_object_hashmap(self, obj,
500 3dabe5d2 Stavros Sachtouris
        version=None,
501 3dabe5d2 Stavros Sachtouris
        if_match=None,
502 3dabe5d2 Stavros Sachtouris
        if_none_match=None,
503 3dabe5d2 Stavros Sachtouris
        if_modified_since=None,
504 3dabe5d2 Stavros Sachtouris
        if_unmodified_since=None,
505 3dabe5d2 Stavros Sachtouris
        data_range=None):
506 d804de82 Stavros Sachtouris
        try:
507 3dabe5d2 Stavros Sachtouris
            r = self.object_get(obj,
508 3dabe5d2 Stavros Sachtouris
                hashmap=True,
509 3dabe5d2 Stavros Sachtouris
                version=version,
510 3dabe5d2 Stavros Sachtouris
                if_etag_match=if_match,
511 3dabe5d2 Stavros Sachtouris
                if_etag_not_match=if_none_match,
512 3dabe5d2 Stavros Sachtouris
                if_modified_since=if_modified_since,
513 3dabe5d2 Stavros Sachtouris
                if_unmodified_since=if_unmodified_since,
514 3dabe5d2 Stavros Sachtouris
                data_range=data_range)
515 d804de82 Stavros Sachtouris
        except ClientError as err:
516 d804de82 Stavros Sachtouris
            if err.status == 304 or err.status == 412:
517 d804de82 Stavros Sachtouris
                return {}
518 d804de82 Stavros Sachtouris
            raise
519 64ab4c13 Stavros Sachtouris
        return r.json
520 56f0908a Stavros Sachtouris
521 3a9e54b0 Stavros Sachtouris
    def set_account_group(self, group, usernames):
522 3dabe5d2 Stavros Sachtouris
        r = self.account_post(update=True, groups={group: usernames})
523 6ce9fc72 Stavros Sachtouris
        r.release()
524 eb903ba7 Stavros Sachtouris
525 c2867610 Stavros Sachtouris
    def del_account_group(self, group):
526 3dabe5d2 Stavros Sachtouris
        r = self.account_post(update=True, groups={group: []})
527 6ce9fc72 Stavros Sachtouris
        r.release()
528 c2867610 Stavros Sachtouris
529 8af4cc0b Stavros Sachtouris
    def get_account_info(self, until=None):
530 8af4cc0b Stavros Sachtouris
        r = self.account_head(until=until)
531 6657ec8c Stavros Sachtouris
        if r.status_code == 401:
532 6657ec8c Stavros Sachtouris
            raise ClientError("No authorization")
533 64ab4c13 Stavros Sachtouris
        return r.headers
534 6657ec8c Stavros Sachtouris
535 d1856abf Stavros Sachtouris
    def get_account_quota(self):
536 3dabe5d2 Stavros Sachtouris
        return filter_in(self.get_account_info(),
537 3dabe5d2 Stavros Sachtouris
            'X-Account-Policy-Quota',
538 3dabe5d2 Stavros Sachtouris
            exactMatch=True)
539 d1856abf Stavros Sachtouris
540 d1856abf Stavros Sachtouris
    def get_account_versioning(self):
541 3dabe5d2 Stavros Sachtouris
        return filter_in(self.get_account_info(),
542 3dabe5d2 Stavros Sachtouris
            'X-Account-Policy-Versioning',
543 3dabe5d2 Stavros Sachtouris
            exactMatch=True)
544 e6b39366 Stavros Sachtouris
545 8af4cc0b Stavros Sachtouris
    def get_account_meta(self, until=None):
546 3dabe5d2 Stavros Sachtouris
        return filter_in(self.get_account_info(until=until), 'X-Account-Meta-')
547 af3b2b36 Stavros Sachtouris
548 c2867610 Stavros Sachtouris
    def get_account_group(self):
549 c2867610 Stavros Sachtouris
        return filter_in(self.get_account_info(), 'X-Account-Group-')
550 c2867610 Stavros Sachtouris
551 af3b2b36 Stavros Sachtouris
    def set_account_meta(self, metapairs):
552 af3b2b36 Stavros Sachtouris
        assert(type(metapairs) is dict)
553 6ce9fc72 Stavros Sachtouris
        r = self.account_post(update=True, metadata=metapairs)
554 6ce9fc72 Stavros Sachtouris
        r.release()
555 af3b2b36 Stavros Sachtouris
556 379cd4bb Stavros Sachtouris
    def del_account_meta(self, metakey):
557 3dabe5d2 Stavros Sachtouris
        r = self.account_post(update=True, metadata={metakey: ''})
558 6ce9fc72 Stavros Sachtouris
        r.release()
559 379cd4bb Stavros Sachtouris
560 d1856abf Stavros Sachtouris
    def set_account_quota(self, quota):
561 6ce9fc72 Stavros Sachtouris
        r = self.account_post(update=True, quota=quota)
562 6ce9fc72 Stavros Sachtouris
        r.release()
563 d1856abf Stavros Sachtouris
564 d1856abf Stavros Sachtouris
    def set_account_versioning(self, versioning):
565 3dabe5d2 Stavros Sachtouris
        r = self.account_post(update=True, versioning=versioning)
566 6ce9fc72 Stavros Sachtouris
        r.release()
567 d1856abf Stavros Sachtouris
568 4fd88feb Stavros Sachtouris
    def list_containers(self):
569 4fd88feb Stavros Sachtouris
        r = self.account_get()
570 64ab4c13 Stavros Sachtouris
        return r.json
571 b758e547 Stavros Sachtouris
572 a298f2ab Stavros Sachtouris
    def del_container(self, until=None, delimiter=None):
573 a298f2ab Stavros Sachtouris
        self.assert_container()
574 3dabe5d2 Stavros Sachtouris
        r = self.container_delete(until=until,
575 3dabe5d2 Stavros Sachtouris
            delimiter=delimiter,
576 3dabe5d2 Stavros Sachtouris
            success=(204, 404, 409))
577 6ce9fc72 Stavros Sachtouris
        r.release()
578 a298f2ab Stavros Sachtouris
        if r.status_code == 404:
579 3dabe5d2 Stavros Sachtouris
            raise ClientError('Container "%s" does not exist' % self.container,
580 3dabe5d2 Stavros Sachtouris
                r.status_code)
581 a298f2ab Stavros Sachtouris
        elif r.status_code == 409:
582 3dabe5d2 Stavros Sachtouris
            raise ClientError('Container "%s" is not empty' % self.container,
583 3dabe5d2 Stavros Sachtouris
                r.status_code)
584 a298f2ab Stavros Sachtouris
585 d1856abf Stavros Sachtouris
    def get_container_versioning(self, container):
586 2f749e6e Stavros Sachtouris
        self.container = container
587 3dabe5d2 Stavros Sachtouris
        return filter_in(self.get_container_info(),
588 3dabe5d2 Stavros Sachtouris
            'X-Container-Policy-Versioning')
589 d1856abf Stavros Sachtouris
590 d1856abf Stavros Sachtouris
    def get_container_quota(self, container):
591 2f749e6e Stavros Sachtouris
        self.container = container
592 2f749e6e Stavros Sachtouris
        return filter_in(self.get_container_info(), 'X-Container-Policy-Quota')
593 e6b39366 Stavros Sachtouris
594 3dabe5d2 Stavros Sachtouris
    def get_container_info(self, until=None):
595 8af4cc0b Stavros Sachtouris
        r = self.container_head(until=until)
596 8af4cc0b Stavros Sachtouris
        return r.headers
597 8af4cc0b Stavros Sachtouris
598 3dabe5d2 Stavros Sachtouris
    def get_container_meta(self, until=None):
599 3dabe5d2 Stavros Sachtouris
        return filter_in(self.get_container_info(until=until),
600 3dabe5d2 Stavros Sachtouris
            'X-Container-Meta')
601 e6b39366 Stavros Sachtouris
602 3dabe5d2 Stavros Sachtouris
    def get_container_object_meta(self, until=None):
603 3dabe5d2 Stavros Sachtouris
        return filter_in(self.get_container_info(until=until),
604 3dabe5d2 Stavros Sachtouris
            'X-Container-Object-Meta')
605 e6b39366 Stavros Sachtouris
606 af3b2b36 Stavros Sachtouris
    def set_container_meta(self, metapairs):
607 af3b2b36 Stavros Sachtouris
        assert(type(metapairs) is dict)
608 6ce9fc72 Stavros Sachtouris
        r = self.container_post(update=True, metadata=metapairs)
609 6ce9fc72 Stavros Sachtouris
        r.release()
610 3dabe5d2 Stavros Sachtouris
611 3e544e5b Stavros Sachtouris
    def del_container_meta(self, metakey):
612 3dabe5d2 Stavros Sachtouris
        r = self.container_post(update=True, metadata={metakey: ''})
613 6ce9fc72 Stavros Sachtouris
        r.release()
614 e6b39366 Stavros Sachtouris
615 d1856abf Stavros Sachtouris
    def set_container_quota(self, quota):
616 6ce9fc72 Stavros Sachtouris
        r = self.container_post(update=True, quota=quota)
617 6ce9fc72 Stavros Sachtouris
        r.release()
618 d1856abf Stavros Sachtouris
619 d1856abf Stavros Sachtouris
    def set_container_versioning(self, versioning):
620 6ce9fc72 Stavros Sachtouris
        r = self.container_post(update=True, versioning=versioning)
621 6ce9fc72 Stavros Sachtouris
        r.release()
622 d1856abf Stavros Sachtouris
623 a298f2ab Stavros Sachtouris
    def del_object(self, obj, until=None, delimiter=None):
624 a298f2ab Stavros Sachtouris
        self.assert_container()
625 6ce9fc72 Stavros Sachtouris
        r = self.object_delete(obj, until=until, delimiter=delimiter)
626 6ce9fc72 Stavros Sachtouris
        r.release()
627 a298f2ab Stavros Sachtouris
628 af3b2b36 Stavros Sachtouris
    def set_object_meta(self, object, metapairs):
629 af3b2b36 Stavros Sachtouris
        assert(type(metapairs) is dict)
630 6ce9fc72 Stavros Sachtouris
        r = self.object_post(object, update=True, metadata=metapairs)
631 6ce9fc72 Stavros Sachtouris
        r.release()
632 87688514 Stavros Sachtouris
633 89c2e77b Stavros Sachtouris
    def del_object_meta(self, metakey, object):
634 3dabe5d2 Stavros Sachtouris
        r = self.object_post(object, update=True, metadata={metakey: ''})
635 6ce9fc72 Stavros Sachtouris
        r.release()
636 6de1f262 Stavros Sachtouris
637 87688514 Stavros Sachtouris
    def publish_object(self, object):
638 6ce9fc72 Stavros Sachtouris
        r = self.object_post(object, update=True, public=True)
639 6ce9fc72 Stavros Sachtouris
        r.release()
640 87688514 Stavros Sachtouris
641 87688514 Stavros Sachtouris
    def unpublish_object(self, object):
642 6ce9fc72 Stavros Sachtouris
        r = self.object_post(object, update=True, public=False)
643 6ce9fc72 Stavros Sachtouris
        r.release()
644 28470086 Stavros Sachtouris
645 8af4cc0b Stavros Sachtouris
    def get_object_info(self, obj, version=None):
646 8af4cc0b Stavros Sachtouris
        r = self.object_head(obj, version=version)
647 64ab4c13 Stavros Sachtouris
        return r.headers
648 8af4cc0b Stavros Sachtouris
649 8af4cc0b Stavros Sachtouris
    def get_object_meta(self, obj, version=None):
650 3dabe5d2 Stavros Sachtouris
        return filter_in(self.get_object_info(obj, version=version),
651 3dabe5d2 Stavros Sachtouris
            'X-Object-Meta')
652 8af4cc0b Stavros Sachtouris
653 f49084df Stavros Sachtouris
    def get_object_sharing(self, object):
654 3dabe5d2 Stavros Sachtouris
        r = filter_in(self.get_object_info(object),
655 3dabe5d2 Stavros Sachtouris
            'X-Object-Sharing',
656 3dabe5d2 Stavros Sachtouris
            exactMatch=True)
657 f70616fc Stavros Sachtouris
        reply = {}
658 f70616fc Stavros Sachtouris
        if len(r) > 0:
659 f70616fc Stavros Sachtouris
            perms = r['x-object-sharing'].split(';')
660 f70616fc Stavros Sachtouris
            for perm in perms:
661 f70616fc Stavros Sachtouris
                try:
662 f70616fc Stavros Sachtouris
                    perm.index('=')
663 f70616fc Stavros Sachtouris
                except ValueError:
664 f70616fc Stavros Sachtouris
                    raise ClientError('Incorrect reply format')
665 f70616fc Stavros Sachtouris
                (key, val) = perm.strip().split('=')
666 f70616fc Stavros Sachtouris
                reply[key] = val
667 f70616fc Stavros Sachtouris
        return reply
668 f49084df Stavros Sachtouris
669 3dabe5d2 Stavros Sachtouris
    def set_object_sharing(self, object,
670 3dabe5d2 Stavros Sachtouris
        read_permition=False,
671 3dabe5d2 Stavros Sachtouris
        write_permition=False):
672 28470086 Stavros Sachtouris
        """Give read/write permisions to an object.
673 3dabe5d2 Stavros Sachtouris
           @param object is the object to change sharing permissions
674 3dabe5d2 Stavros Sachtouris
 onto
675 3dabe5d2 Stavros Sachtouris
           @param read_permition is a list of users and user groups that
676 3dabe5d2 Stavros Sachtouris
                get read permition for this object
677 3dabe5d2 Stavros Sachtouris
                False means all previous read permissions
678 3dabe5d2 Stavros Sachtouris
 will be removed
679 3dabe5d2 Stavros Sachtouris
           @param write_perimition is a list of users and user groups to
680 3dabe5d2 Stavros Sachtouris
                get write permition for this object
681 3dabe5d2 Stavros Sachtouris
                False means all previous read permissions
682 3dabe5d2 Stavros Sachtouris
 will be removed
683 28470086 Stavros Sachtouris
        """
684 3dabe5d2 Stavros Sachtouris
        perms = dict(read='' if not read_permition else read_permition,
685 3dabe5d2 Stavros Sachtouris
            write='' if not write_permition else write_permition)
686 3dabe5d2 Stavros Sachtouris
        r = self.object_post(object, update=True, permissions=perms)
687 6ce9fc72 Stavros Sachtouris
        r.release()
688 28470086 Stavros Sachtouris
689 f49084df Stavros Sachtouris
    def del_object_sharing(self, object):
690 28470086 Stavros Sachtouris
        self.set_object_sharing(object)
691 f49084df Stavros Sachtouris
692 3dabe5d2 Stavros Sachtouris
    def append_object(self, object, source_file, upload_cb=None):
693 9f74ca46 Stavros Sachtouris
        """@param upload_db is a generator for showing progress of upload
694 bcabbc35 Stavros Sachtouris
            to caller application, e.g. a progress bar. Its next is called
695 bcabbc35 Stavros Sachtouris
            whenever a block is uploaded
696 bcabbc35 Stavros Sachtouris
        """
697 ab474306 Stavros Sachtouris
        self.assert_container()
698 2f749e6e Stavros Sachtouris
        meta = self.get_container_info()
699 ab474306 Stavros Sachtouris
        blocksize = int(meta['x-container-block-size'])
700 64ab4c13 Stavros Sachtouris
        filesize = fstat(source_file.fileno()).st_size
701 3dabe5d2 Stavros Sachtouris
        nblocks = 1 + (filesize - 1) // blocksize
702 ab474306 Stavros Sachtouris
        offset = 0
703 bcabbc35 Stavros Sachtouris
        if upload_cb is not None:
704 bcabbc35 Stavros Sachtouris
            upload_gen = upload_cb(nblocks)
705 ab474306 Stavros Sachtouris
        for i in range(nblocks):
706 ab474306 Stavros Sachtouris
            block = source_file.read(min(blocksize, filesize - offset))
707 ab474306 Stavros Sachtouris
            offset += len(block)
708 3dabe5d2 Stavros Sachtouris
            r = self.object_post(object,
709 3dabe5d2 Stavros Sachtouris
                update=True,
710 3dabe5d2 Stavros Sachtouris
                content_range='bytes */*',
711 3dabe5d2 Stavros Sachtouris
                content_type='application/octet-stream',
712 3dabe5d2 Stavros Sachtouris
                content_length=len(block),
713 3dabe5d2 Stavros Sachtouris
                data=block)
714 6ce9fc72 Stavros Sachtouris
            r.release()
715 3dabe5d2 Stavros Sachtouris
716 bcabbc35 Stavros Sachtouris
            if upload_cb is not None:
717 bcabbc35 Stavros Sachtouris
                upload_gen.next()
718 561116a6 Stavros Sachtouris
719 561116a6 Stavros Sachtouris
    def truncate_object(self, object, upto_bytes):
720 3dabe5d2 Stavros Sachtouris
        r = self.object_post(object,
721 3dabe5d2 Stavros Sachtouris
            update=True,
722 3dabe5d2 Stavros Sachtouris
            content_range='bytes 0-%s/*' % upto_bytes,
723 3dabe5d2 Stavros Sachtouris
            content_type='application/octet-stream',
724 3dabe5d2 Stavros Sachtouris
            object_bytes=upto_bytes,
725 4adfa919 Stavros Sachtouris
            source_object=path4url(self.container, object))
726 6ce9fc72 Stavros Sachtouris
        r.release()
727 ee62607e Stavros Sachtouris
728 3dabe5d2 Stavros Sachtouris
    def overwrite_object(self,
729 3dabe5d2 Stavros Sachtouris
        object,
730 3dabe5d2 Stavros Sachtouris
        start,
731 3dabe5d2 Stavros Sachtouris
        end,
732 3dabe5d2 Stavros Sachtouris
        source_file,
733 3dabe5d2 Stavros Sachtouris
        upload_cb=None):
734 ee62607e Stavros Sachtouris
        """Overwrite a part of an object with given source file
735 3dabe5d2 Stavros Sachtouris
           @start the part of the remote object to start overwriting from,
736 3dabe5d2 Stavros Sachtouris
                in bytes
737 ee62607e Stavros Sachtouris
           @end the part of the remote object to stop overwriting to, in bytes
738 ee62607e Stavros Sachtouris
        """
739 ee62607e Stavros Sachtouris
        self.assert_container()
740 2f749e6e Stavros Sachtouris
        meta = self.get_container_info()
741 ee62607e Stavros Sachtouris
        blocksize = int(meta['x-container-block-size'])
742 64ab4c13 Stavros Sachtouris
        filesize = fstat(source_file.fileno()).st_size
743 ee62607e Stavros Sachtouris
        datasize = int(end) - int(start) + 1
744 3dabe5d2 Stavros Sachtouris
        nblocks = 1 + (datasize - 1) // blocksize
745 ee62607e Stavros Sachtouris
        offset = 0
746 bcabbc35 Stavros Sachtouris
        if upload_cb is not None:
747 bcabbc35 Stavros Sachtouris
            upload_gen = upload_cb(nblocks)
748 ee62607e Stavros Sachtouris
        for i in range(nblocks):
749 3dabe5d2 Stavros Sachtouris
            block = source_file.read(min(blocksize,
750 3dabe5d2 Stavros Sachtouris
                filesize - offset,
751 3dabe5d2 Stavros Sachtouris
                datasize - offset))
752 ee62607e Stavros Sachtouris
            offset += len(block)
753 3dabe5d2 Stavros Sachtouris
            r = self.object_post(object,
754 3dabe5d2 Stavros Sachtouris
                update=True,
755 3dabe5d2 Stavros Sachtouris
                content_type='application/octet-stream',
756 3dabe5d2 Stavros Sachtouris
                content_length=len(block),
757 3dabe5d2 Stavros Sachtouris
                content_range='bytes %s-%s/*' % (start, end),
758 3dabe5d2 Stavros Sachtouris
                data=block)
759 6ce9fc72 Stavros Sachtouris
            r.release()
760 3dabe5d2 Stavros Sachtouris
761 bcabbc35 Stavros Sachtouris
            if upload_cb is not None:
762 bcabbc35 Stavros Sachtouris
                upload_gen.next()
763 7d420701 Stavros Sachtouris
764 3dabe5d2 Stavros Sachtouris
    def copy_object(self, src_container, src_object, dst_container,
765 3dabe5d2 Stavros Sachtouris
        dst_object=False,
766 3dabe5d2 Stavros Sachtouris
        source_version=None,
767 3dabe5d2 Stavros Sachtouris
        public=False,
768 3dabe5d2 Stavros Sachtouris
        content_type=None,
769 3dabe5d2 Stavros Sachtouris
        delimiter=None):
770 7d420701 Stavros Sachtouris
        self.assert_account()
771 7d420701 Stavros Sachtouris
        self.container = dst_container
772 7d420701 Stavros Sachtouris
        dst_object = dst_object or src_object
773 7d420701 Stavros Sachtouris
        src_path = path4url(src_container, src_object)
774 3dabe5d2 Stavros Sachtouris
        r = self.object_put(dst_object,
775 3dabe5d2 Stavros Sachtouris
            success=201,
776 3dabe5d2 Stavros Sachtouris
            copy_from=src_path,
777 3dabe5d2 Stavros Sachtouris
            content_length=0,
778 3dabe5d2 Stavros Sachtouris
            source_version=source_version,
779 3dabe5d2 Stavros Sachtouris
            public=public,
780 3dabe5d2 Stavros Sachtouris
            content_type=content_type,
781 7d420701 Stavros Sachtouris
            delimiter=delimiter)
782 6ce9fc72 Stavros Sachtouris
        r.release()
783 a5e0629d Stavros Sachtouris
784 3dabe5d2 Stavros Sachtouris
    def move_object(self, src_container, src_object, dst_container,
785 3dabe5d2 Stavros Sachtouris
        dst_object=False,
786 3dabe5d2 Stavros Sachtouris
        source_version=None,
787 3dabe5d2 Stavros Sachtouris
        public=False,
788 3dabe5d2 Stavros Sachtouris
        content_type=None,
789 3dabe5d2 Stavros Sachtouris
        delimiter=None):
790 a5e0629d Stavros Sachtouris
        self.assert_account()
791 a5e0629d Stavros Sachtouris
        self.container = dst_container
792 a5e0629d Stavros Sachtouris
        dst_object = dst_object or src_object
793 a5e0629d Stavros Sachtouris
        src_path = path4url(src_container, src_object)
794 3dabe5d2 Stavros Sachtouris
        r = self.object_put(dst_object,
795 3dabe5d2 Stavros Sachtouris
            success=201,
796 3dabe5d2 Stavros Sachtouris
            move_from=src_path,
797 3dabe5d2 Stavros Sachtouris
            content_length=0,
798 3dabe5d2 Stavros Sachtouris
            source_version=source_version,
799 3dabe5d2 Stavros Sachtouris
            public=public,
800 3dabe5d2 Stavros Sachtouris
            content_type=content_type,
801 a23f6ffe Stavros Sachtouris
            delimiter=delimiter)
802 6ce9fc72 Stavros Sachtouris
        r.release()
803 a23f6ffe Stavros Sachtouris
804 a23f6ffe Stavros Sachtouris
    def get_sharing_accounts(self, limit=None, marker=None, *args, **kwargs):
805 a23f6ffe Stavros Sachtouris
        """Get accounts that share with self.account"""
806 a23f6ffe Stavros Sachtouris
        self.assert_account()
807 a23f6ffe Stavros Sachtouris
808 3dabe5d2 Stavros Sachtouris
        self.set_param('format', 'json')
809 3dabe5d2 Stavros Sachtouris
        self.set_param('limit', limit, iff=limit is not None)
810 3dabe5d2 Stavros Sachtouris
        self.set_param('marker', marker, iff=marker is not None)
811 a23f6ffe Stavros Sachtouris
812 a23f6ffe Stavros Sachtouris
        path = ''
813 a23f6ffe Stavros Sachtouris
        success = kwargs.pop('success', (200, 204))
814 3dabe5d2 Stavros Sachtouris
        r = self.get(path, *args, success=success, **kwargs)
815 38dc5d2f Stavros Sachtouris
        return r.json
816 38dc5d2f Stavros Sachtouris
817 38dc5d2f Stavros Sachtouris
    def get_object_versionlist(self, path):
818 38dc5d2f Stavros Sachtouris
        self.assert_container()
819 38dc5d2f Stavros Sachtouris
        r = self.object_get(path, format='json', version='list')
820 38dc5d2f Stavros Sachtouris
        return r.json['versions']