Statistics
| Branch: | Tag: | Revision:

root / kamaki / clients / pithos_rest_api.py @ 9a7efb0d

History | View | Annotate | Download (29.9 kB)

1
# Copyright 2011-2012 GRNET S.A. All rights reserved.
2
#
3
# Redistribution and use in source and binary forms, with or
4
# without modification, are permitted provided that the following
5
# conditions are met:
6
#
7
#   1. Redistributions of source code must retain the above
8
#      copyright notice, this list of conditions and the following
9
#      disclaimer.
10
#
11
#   2. Redistributions in binary form must reproduce the above
12
#      copyright notice, this list of conditions and the following
13
#      disclaimer in the documentation and/or other materials
14
#      provided with the distribution.
15
#
16
# THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
17
# OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19
# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR
20
# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
23
# USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
24
# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
26
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27
# POSSIBILITY OF SUCH DAMAGE.
28
#
29
# The views and conclusions contained in the software and
30
# documentation are those of the authors and should not be
31
# interpreted as representing official policies, either expressed
32
# or implied, of GRNET S.A.
33

    
34
from .storage import StorageClient, ClientError
35
from .utils import path4url, list2str
36

    
37
class PithosRestAPI(StorageClient):
38

    
39
    def account_head(self, until = None,
40
        if_modified_since=None, if_unmodified_since=None, *args, **kwargs):
41
        """ Full Pithos+ HEAD at account level
42
        --- request parameters ---
43
        @param until (string): optional timestamp
44
        --- --- optional request headers ---
45
        @param if_modified_since (string): Retrieve if account has changed since provided timestamp
46
        @param if_unmodified_since (string): Retrieve if account has not changed since provided timestamp
47
        """
48
        self.assert_account()
49
        path = path4url(self.account)
50

    
51
        self.set_param('until', until, iff = until is not None)
52
        self.set_header('If-Modified-Since', if_modified_since)
53
        self.set_header('If-Unmodified-Since', if_unmodified_since)
54

    
55
        success = kwargs.pop('success', 204)
56
        return self.head(path, *args, success=success, **kwargs)
57

    
58
    def account_get(self, limit=None, marker=None, format='json', show_only_shared=False, until=None,
59
        if_modified_since=None, if_unmodified_since=None, *args, **kwargs):
60
        """  Full Pithos+ GET at account level
61
        --- request parameters ---
62
        @param limit (integer): The amount of results requested (server will use default value if None)
63
        @param marker (string): Return containers with name lexicographically after marker
64
        @param format (string): reply format can be json or xml (default: json)
65
        @param shared (bool): If true, only shared containers will be included in results
66
        @param until (string): optional timestamp
67
        --- --- optional request headers ---
68
        @param if_modified_since (string): Retrieve if account has changed since provided timestamp
69
        @param if_unmodified_since (string): Retrieve if account has not changed since provided timestamp
70
        """
71
        self.assert_account()
72

    
73
        self.set_param('format',format, iff = format is not None)
74
        self.set_param('limit',limit, iff = limit is not None)
75
        self.set_param('marker',marker, iff = marker is not None)
76
        self.set_param('shared', iff = show_only_shared)
77
        self.set_param('until',until, iff = until is not None)
78

    
79
        self.set_header('If-Modified-Since', if_modified_since)
80
        self.set_header('If-Unmodified-Since', if_unmodified_since)
81

    
82
        path = path4url(self.account)
83
        success = kwargs.pop('success', (200, 204))
84
        return self.get(path, *args, success = success, **kwargs)
85

    
86
    def account_post(self, update=True,
87
        groups={}, metadata=None, quota=None, versioning=None, *args, **kwargs):
88
        """ Full Pithos+ POST at account level
89
        --- request parameters ---
90
        @param update (bool): if True, Do not replace metadata/groups
91
        --- request headers ---
92
        @groups (dict): Optional user defined groups in the form
93
                    {   'group1':['user1', 'user2', ...], 
94
                        'group2':['userA', 'userB', ...], ...
95
                    }
96
        @metadata (dict): Optional user defined metadata in the form
97
                    {   'name1': 'value1',
98
                        'name2': 'value2', ...
99
                    }
100
        @param quota(integer): If supported, sets the Account quota
101
        @param versioning(string): If supported, sets the Account versioning
102
                    to 'auto' or some other supported versioning string
103
        """
104
        self.assert_account()
105

    
106
        self.set_param('update', iff = update)
107

    
108
        for group, usernames in groups.items():
109
            userstr = ''
110
            dlm = ''
111
            for user in usernames:
112
                userstr = userstr + dlm + user
113
                dlm = ','
114
            self.set_header('X-Account-Group-'+group, userstr)
115
        if metadata is not None:
116
            for metaname, metaval in metadata.items():
117
                self.set_header('X-Account-Meta-'+metaname, metaval)
118
        self.set_header('X-Account-Policy-Quota', quota)
119
        self.set_header('X-Account-Policy-Versioning', versioning)
120

    
121
        path = path4url(self.account)
122
        success = kwargs.pop('success', 202)
123
        return self.post(path, *args, success=success, **kwargs)
124

    
125
    def container_head(self, until=None,
126
        if_modified_since=None, if_unmodified_since=None, *args, **kwargs):
127
        """ Full Pithos+ HEAD at container level
128
        --- request params ---
129
        @param until (string): optional timestamp
130
        --- optional request headers --- 
131
        @param if_modified_since (string): Retrieve if account has changed since provided timestamp
132
        @param if_unmodified_since (string): Retrieve if account has not changed since provided timestamp
133
        """
134
        self.assert_container()
135

    
136
        self.set_param('until', until, iff=until is not None)
137

    
138
        self.set_header('If-Modified-Since', if_modified_since)
139
        self.set_header('If-Unmodified-Since', if_unmodified_since)
140

    
141
        path = path4url(self.account, self.container)
142
        success = kwargs.pop('success', 204)
143
        return self.head(path, *args, success=success, **kwargs)
144

    
145
    def container_get(self, limit = None, marker = None, prefix=None, delimiter=None, path = None,
146
        format='json', meta=[], show_only_shared=False, until=None,
147
        if_modified_since=None, if_unmodified_since=None, *args, **kwargs):
148
        """ Full Pithos+ GET at container level
149
        --- request parameters ---
150
        @param limit (integer): The amount of results requested (server qill use default value if None)
151
        @param marker (string): Return containers with name lexicographically after marker
152
        @param prefix (string): Return objects starting with prefix
153
        @param delimiter (string): Return objects up to the delimiter
154
        @param path (string): assume prefix = path and delimiter = / (overwrites prefix
155
        and delimiter)
156
        @param format (string): reply format can be json or xml (default: json)
157
        @param meta (list): Return objects that satisfy the key queries in the specified
158
        comma separated list (use <key>, !<key> for existence queries, <key><op><value>
159
        for value queries, where <op> can be one of =, !=, <=, >=, <, >)
160
        @param shared (bool): If true, only shared containers will be included in results
161
        @param until (string): optional timestamp
162
        --- --- optional request headers ---
163
        @param if_modified_since (string): Retrieve if account has changed since provided timestamp
164
        @param if_unmodified_since (string): Retrieve if account has not changed since provided timestamp
165
        """
166
        self.assert_container()
167

    
168
        self.set_param('format', format, iff=format is not None)
169
        self.set_param('limit', limit, iff=limit is not None)
170
        self.set_param('marker', marker, iff=marker is not None)
171
        if path is None:
172
            self.set_param('prefix', prefix, iff=prefix is not None)
173
            self.set_param('delimiter', delimiter, iff=delimiter is not None)
174
        else:
175
            self.set_param('path', path)
176
        self.set_param('shared', iff=show_only_shared)
177
        self.set_param('meta', list2str(meta), iff=meta is not None and len(meta) > 0)
178
        self.set_param('until', until, iff=until is not None)
179

    
180
        self.set_header('If-Modified-Since', if_modified_since)
181
        self.set_header('If-Unmodified-Since', if_unmodified_since)
182

    
183
        path = path4url(self.account, self.container)
184
        success = kwargs.pop('success', 200)
185
        return self.get(path, *args, success=success, **kwargs)
186

    
187
    def container_put(self, quota=None, versioning=None, metadata=None, *args, **kwargs):
188
        """ Full Pithos+ PUT at container level
189
        --- request headers ---
190
        @param quota (integer): Size limit in KB
191
        @param versioning (string): 'auto' or other string supported by server
192
        @metadata (dict): Optional user defined metadata in the form
193
        {   'name1': 'value1',
194
        'name2': 'value2', ...
195
        }
196
        """
197
        self.assert_container()
198

    
199
        if metadata is not None:
200
            for metaname, metaval in metadata.items():
201
                self.set_header('X-Container-Meta-'+metaname, metaval)
202
        self.set_header('X-Container-Policy-Quota', quota)
203
        self.set_header('X-Container-Policy-Versioning', versioning)
204

    
205
        path = path4url(self.account, self.container)
206
        success = kwargs.pop('success',(201, 202))
207
        return self.put(path, *args, success=success, **kwargs)
208

    
209
    def container_post(self, update=True, format='json',
210
        quota=None, versioning=None, metadata=None, content_type=None, content_length=None,
211
        transfer_encoding=None, *args, **kwargs):
212
        """ Full Pithos+ POST at container level
213
        --- request params ---
214
        @param update (bool):  if True, Do not replace metadata/groups
215
        @param format(string): json (default) or xml
216
        --- request headers ---
217
        @param quota (integer): Size limit in KB
218
        @param versioning (string): 'auto' or other string supported by server
219
        @metadata (dict): Optional user defined metadata in the form
220
        {   'name1': 'value1',
221
        'name2': 'value2', ...
222
        }
223
        @param content_type (string): set a custom content type
224
        @param content_length (string): set a custrom content length
225
        @param transfer_encoding (string): set a custrom transfer encoding
226
        """
227
        self.assert_container()
228

    
229
        self.set_param('format', format, iff=format is not None)
230
        self.set_param('update', iff=update)
231

    
232
        if metadata is not None:
233
            for metaname, metaval in metadata.items():
234
                self.set_header('X-Container-Meta-'+metaname, metaval)
235
        self.set_header('X-Container-Policy-Quota', quota)
236
        self.set_header('X-Container-Policy-Versioning', versioning)
237
        self.set_header('Content-Type', content_type)
238
        self.set_header('Content-Length', content_length)
239
        self.set_header('Transfer-Encoding', transfer_encoding)
240

    
241
        path = path4url(self.account, self.container)
242
        success = kwargs.pop('success', 202)
243
        return self.post(path, *args, success=success, **kwargs)
244

    
245
    def container_delete(self, until=None, delimiter=None, *args, **kwargs):
246
        """ Full Pithos+ DELETE at container level
247
        --- request parameters ---
248
        @param until (timestamp string): if defined, container is purged up to that time
249
        """
250
        self.assert_container()        
251

    
252
        self.set_param('until', until, iff=until is not None)
253
        self.set_param('delimiter', delimiter, iff=delimiter is not None)
254

    
255
        path=path4url(self.account, self.container)
256
        success = kwargs.pop('success', 204)
257
        return self.delete(path, success=success)
258

    
259
    def object_head(self, object, version=None, if_etag_match=None, if_etag_not_match = None,
260
        if_modified_since = None, if_unmodified_since = None, *args, **kwargs):
261
        """ Full Pithos+ HEAD at object level
262
        --- request parameters ---
263
        @param version (string): optional version identified
264
        --- request headers ---
265
        @param if_etag_match (string): if provided, return only results
266
                with etag matching with this
267
        @param if_etag_not_match (string): if provided, return only results
268
                with etag not matching with this
269
        @param if_modified_since (string): Retrieve if account has changed since provided timestamp
270
        @param if_unmodified_since (string): Retrieve if account has not changed since provided timestamp
271
        """
272
        self.assert_container()
273

    
274
        self.set_param('version', version, iff=version is not None)
275

    
276
        self.set_header('If-Match', if_etag_match)
277
        self.set_header('If-None-Match', if_etag_not_match)
278
        self.set_header('If-Modified-Since', if_modified_since)
279
        self.set_header('If-Unmodified-Since', if_unmodified_since)
280

    
281
        path=path4url(self.account, self.container, object)
282
        success = kwargs.pop('success', 200)
283
        return self.head(path, *args, success=success, **kwargs)
284

    
285
    def object_get(self, object, format='json', hashmap=False, version=None,
286
        data_range=None, if_range=False, if_etag_match=None, if_etag_not_match = None,
287
        if_modified_since = None, if_unmodified_since = None, *args, **kwargs):
288
        """ Full Pithos+ GET at object level
289
        --- request parameters ---
290
        @param format (string): json (default) or xml
291
        @param hashmap (bool): Optional request for hashmap
292
        @param version (string): optional version identified
293
        --- request headers ---
294
        @param data_range (string): Optional range of data to retrieve
295
        @param if_range (bool): 
296
        @param if_etag_match (string): if provided, return only results
297
                with etag matching with this
298
        @param if_etag_not_match (string): if provided, return only results
299
                with etag not matching with this
300
        @param if_modified_since (string): Retrieve if account has changed since provided timestamp
301
        @param if_unmodified_since (string): Retrieve if account has not changed since provided timestamp
302
        """
303
        self.assert_container()
304

    
305
        self.set_param('format', format, iff=format is not None)
306
        self.set_param('version', version, iff=version is not None)
307
        self.set_param('hashmap', hashmap, iff=hashmap)
308

    
309
        self.set_header('Range', data_range)
310
        self.set_header('If-Range', '', if_range is True and data_range is not None)
311
        self.set_header('If-Match', if_etag_match, )
312
        self.set_header('If-None-Match', if_etag_not_match)
313
        self.set_header('If-Modified-Since', if_modified_since)
314
        self.set_header('If-Unmodified-Since', if_unmodified_since)
315

    
316
        path=path4url(self.account, self.container, object)
317
        success = kwargs.pop('success', 200)
318
        return self.get(path, *args, success=success, **kwargs)
319

    
320
    def object_put(self, object, format='json', hashmap=False, delimiter = None, if_etag_match=None,
321
        if_etag_not_match = None, etag=None, content_length = None, content_type=None,
322
        transfer_encoding=None, copy_from=None, move_from=None, source_account=None,
323
        source_version=None, content_encoding = None, content_disposition=None, manifest = None,
324
        permitions =None, public = None, metadata=None, *args, **kwargs):
325
        """ Full Pithos+ PUT at object level
326
        --- request parameters ---
327
        @param format (string): json (default) or xml
328
        @param hashmap (bool): Optional hashmap provided instead of data
329
        --- request headers ---
330
        @param if_etag_match (string): if provided, return only results
331
                with etag matching with this
332
        @param if_etag_not_match (string): if provided, return only results
333
                with etag not matching with this
334
        @param etag (string): The MD5 hash of the object (optional to check written data)
335
        @param content_length (integer): The size of the data written
336
        @param content_type (string): The MIME content type of the object
337
        @param transfer_encoding (string): Set to chunked to specify incremental uploading (if used, Content-Length is ignored)
338
        @param copy_from (string): The source path in the form /<container>/<object>
339
        @param move_from (string): The source path in the form /<container>/<object>
340
        @param source_account (string): The source account to copy/move from
341
        @param source_version (string): The source version to copy from
342
        @param conent_encoding (string): The encoding of the object
343
        @param content_disposition (string): The presentation style of the object
344
        @param manifest (string): Object parts prefix in /<container>/<object> form
345
        @param permitions (dict): Object permissions in the form (all fields are optional)
346
                {'read':[user1, group1, user2, ...], 'write':['user3, group2, group3, ...]}
347
        @param public (bool): If true, Object is publicly accessible, if false, not
348
        @param metadata (dict): Optional user defined metadata in the form
349
                {'meta-key-1':'meta-value-1', 'meta-key-2':'meta-value-2', ...}
350
        """
351
        self.assert_container()
352

    
353
        self.set_param('format', format, iff=format is not None)
354
        self.set_param('hashmap', hashmap, iff=hashmap)
355
        self.set_param('delimiter', delimiter, iff=delimiter is not None)
356

    
357
        self.set_header('If-Match', if_etag_match)
358
        self.set_header('If-None-Match', if_etag_not_match)
359
        self.set_header('ETag', etag)
360
        self.set_header('Content-Length', content_length)
361
        self.set_header('Content-Type', content_type)
362
        self.set_header('Transfer-Encoding', transfer_encoding)
363
        self.set_header('X-Copy-From', copy_from)
364
        self.set_header('X-Move-From', move_from)
365
        self.set_header('X-Source-Account', source_account)
366
        self.set_header('X-Source-Version', source_version)
367
        self.set_header('Content-Encoding', content_encoding)
368
        self.set_header('Content-Disposition', content_disposition)
369
        self.set_header('X-Object-Manifest', manifest)
370
        perms = None
371
        if permitions is not None:
372
            for permition_type, permition_list in permitions.items():
373
                if perms is None:
374
                    perms = '' #Remove permitions
375
                if len(permition_list) == 0:
376
                    continue
377
                perms += ';'+permition_type if len(perms) > 0 else permition_type
378
                perms += '='+list2str(permition_list, seperator=',')
379
        self.set_header('X-Object-Sharing', perms)
380
        self.set_header('X-Object-Public', public)
381
        if metadata is not None:
382
            for key, val in metadata.items():
383
                self.set_header('X-Object-Meta-'+key, val)
384

    
385
        path=path4url(self.account, self.container, object)
386
        success = kwargs.pop('success', 201)
387
        return self.put(path, *args, success=success, **kwargs)
388

    
389
    def object_copy(self, object, destination, format='json', ignore_content_type=False,
390
        if_etag_match=None, if_etag_not_match=None, destination_account=None,
391
        content_type=None, content_encoding=None, content_disposition=None, source_version=None,
392
        permitions=None, public=False, metadata=None, *args, **kwargs):
393
        """ Full Pithos+ COPY at object level
394
        --- request parameters ---
395
        @param format (string): json (default) or xml
396
        @param ignore_content_type (bool): Ignore the supplied Content-Type
397
        --- request headers ---
398
         @param if_etag_match (string): if provided, copy only results
399
                with etag matching with this
400
        @param if_etag_not_match (string): if provided, copy only results
401
                with etag not matching with this
402
        @param destination (string): The destination path in the form /<container>/<object>
403
        @param destination_account (string): The destination account to copy to
404
        @param content_type (string): The MIME content type of the object
405
        @param content_encoding (string): The encoding of the object
406
        @param content_disposition (string): The presentation style of the object
407
        @param source_version (string): The source version to copy from
408
        @param permitions (dict): Object permissions in the form (all fields are optional)
409
                {'read':[user1, group1, user2, ...], 'write':['user3, group2, group3, ...]}
410
                permitions override source permitions, removing any old permitions
411
        @param public (bool): If true, Object is publicly accessible, if else, not
412
        @param metadata (dict): Optional user defined metadata in the form
413
                {'meta-key-1':'meta-value-1', 'meta-key-2':'meta-value-2', ...}
414
                Metadata are appended to the source metadata. In case of same keys, they
415
                replace the old metadata
416
        """
417
        self.assert_container()
418

    
419
        self.set_param('format', format, iff=format is not None)
420
        self.set_param('ignore_content_type', iff=ignore_content_type)
421

    
422
        self.set_header('If-Match', if_etag_match)
423
        self.set_header('If-None-Match', if_etag_not_match)
424
        self.set_header('Destination', destination)
425
        self.set_header('Destination-Account', destination_account)
426
        self.set_header('Content-Type', content_type)
427
        self.set_header('Content-Encoding', content_encoding)
428
        self.set_header('Content-Disposition', content_disposition)
429
        self.set_header('X-Source-Version', source_version)
430
        perms = None
431
        if permitions is not None:
432
            for permition_type, permition_list in permitions.items():
433
                if perms is None:
434
                    perms = '' #Remove permitions
435
                if len(permition_list) == 0:
436
                    continue
437
                perms += ';'+permition_type if len(perms) > 0 else permition_type
438
                perms += '='+list2str(permition_list, seperator=',')
439
        self.set_header('X-Object-Sharing', perms)
440
        self.set_header('X-Object-Public', public)
441
        if metadata is not None:
442
            for key, val in metadata.items():
443
                self.set_header('X-Object-Meta-'+key, val)
444

    
445
        path = path4url(self.account, self.container, object)
446
        success = kwargs.pop('success', 201)
447
        return self.copy(path, *args, success=success, **kwargs)
448

    
449
    def object_move(self, object, format='json', ignore_content_type=False,
450
        if_etag_match=None, if_etag_not_match=None, destination=None, destination_account=None,
451
        content_type=None, content_encoding=None, content_disposition=None, permitions={},
452
        public=False, metadata={}, *args, **kwargs):
453
        """ Full Pithos+ COPY at object level
454
        --- request parameters ---
455
        @param format (string): json (default) or xml
456
        @param ignore_content_type (bool): Ignore the supplied Content-Type
457
        --- request headers ---
458
         @param if_etag_match (string): if provided, return only results
459
                with etag matching with this
460
        @param if_etag_not_match (string): if provided, return only results
461
                with etag not matching with this
462
        @param destination (string): The destination path in the form /<container>/<object>
463
        @param destination_account (string): The destination account to copy to
464
        @param content_type (string): The MIME content type of the object
465
        @param content_encoding (string): The encoding of the object
466
        @param content_disposition (string): The presentation style of the object
467
        @param source_version (string): The source version to copy from
468
        @param permitions (dict): Object permissions in the form (all fields are optional)
469
                {'read':[user1, group1, user2, ...], 'write':['user3, group2, group3, ...]}
470
        @param public (bool): If true, Object is publicly accessible, if false, not
471
        @param metadata (dict): Optional user defined metadata in the form
472
                {'meta-key-1':'meta-value-1', 'meta-key-2':'meta-value-2', ...}
473
        """
474
        self.assert_container()
475

    
476
        self.set_param('format', format, iff=format is not None)
477
        self.set_param('ignore_content_type', iff=ignore_content_type)
478

    
479
        self.set_header('If-Match', if_etag_match)
480
        self.set_header('If-None-Match', if_etag_not_match)
481
        self.set_header('Destination', destination)
482
        self.set_header('Destination-Account', destination_account)
483
        self.set_header('Content-Type', content_type)
484
        self.set_header('Content-Encoding', content_encoding)
485
        self.set_header('Content-Disposition', content_disposition)
486
        perms = None
487
        for permition_type, permition_list in permitions.items():
488
            if perms is None:
489
                perms = '' #Remove permitions
490
            if len(permition_list) == 0:
491
                continue
492
            perms += ';'+permition_type if len(perms) > 0 else permition_type
493
            perms += '='+list2str(permition_list, seperator=',')
494
        self.set_header('X-Object-Sharing', perms)
495
        self.set_header('X-Object-Public', public)
496
        for key, val in metadata.items():
497
            self.set_header('X-Object-Meta-'+key, val)
498

    
499
        path = path4url(self.account, self.container, object)
500
        success = kwargs.pop('success', 201)
501
        return self.move(path, *args, success=success, **kwargs)
502

    
503
    def object_post(self, object, format='json', update=True,
504
        if_etag_match=None, if_etag_not_match=None, content_length=None, content_type=None,
505
        content_range=None, transfer_encoding=None, content_encoding=None, content_disposition=None,
506
        source_object=None, source_account=None, source_version=None, object_bytes=None,
507
        manifest=None, permitions={}, public=False, metadata={}, *args, **kwargs):
508
        """ Full Pithos+ POST at object level
509
        --- request parameters ---
510
        @param format (string): json (default) or xml
511
        @param update (bool): Do not replace metadata
512
        --- request headers ---
513
        @param if_etag_match (string): if provided, return only results
514
                with etag matching with this
515
        @param if_etag_not_match (string): if provided, return only results
516
                with etag not matching with this
517
        @param content_length (string): The size of the data written
518
        @param content_type (string): The MIME content type of the object
519
        @param content_range (string): The range of data supplied
520
        @param transfer_encoding (string): Set to chunked to specify incremental uploading
521
                (if used, Content-Length is ignored)
522
        @param content_encoding (string): The encoding of the object
523
        @param content_disposition (string): The presentation style of the object
524
        @param source_object (string): Update with data from the object at path /<container>/<object>
525
        @param source_account (string): The source account to update from
526
        @param source_version (string): The source version to copy from
527
        @param object_bytes (integer): The updated objects final size
528
        @param manifest (string): Object parts prefix in /<container>/<object> form
529
        @param permitions (dict): Object permissions in the form (all fields are optional)
530
                {'read':[user1, group1, user2, ...], 'write':['user3, group2, group3, ...]}
531
        @param public (bool): If true, Object is publicly accessible, if false, not
532
        @param metadata (dict): Optional user defined metadata in the form
533
                {'meta-key-1':'meta-value-1', 'meta-key-2':'meta-value-2', ...}
534
        """
535
        self.assert_container()
536

    
537
        self.set_param('format', format, iff=format is not None)
538
        self.set_param('update', iff = update)
539

    
540
        self.set_header('If-Match', if_etag_match)
541
        self.set_header('If-None-Match', if_etag_not_match)
542
        self.set_header('Content-Length', content_length, iff=transfer_encoding is None)
543
        self.set_header('Content-Type', content_type)
544
        self.set_header('Content-Range', content_range)
545
        self.set_header('Transfer-Encoding', transfer_encoding)
546
        self.set_header('Content-Encoding', content_encoding)
547
        self.set_header('Content-Disposition', content_disposition)
548
        self.set_header('X-Source-Object', source_object)
549
        self.set_header('X-Source-Account', source_account)
550
        self.set_header('X-Source-Version', source_version)
551
        self.set_header('X-Object-Bytes', object_bytes)
552
        self.set_header('X-Object-Manifest', manifest)
553
        perms = None
554
        for permition_type, permition_list in permitions.items():
555
            if perms is None:
556
                perms = '' #Remove permitions
557
            if len(permition_list) == 0:
558
                continue
559
            perms += ';'+permition_type if len(perms) > 0 else permition_type
560
            perms += '='+list2str(permition_list, seperator=',')
561
        self.set_header('X-Object-Sharing', perms)
562
        self.set_header('X-Object-Public', public)
563
        for key, val in metadata.items():
564
            self.set_header('X-Object-Meta-'+key, val)
565

    
566
        path = path4url(self.account, self.container, object)
567
        success=kwargs.pop('success', (202, 204))
568
        return self.post(path, *args, success=success, **kwargs)
569
       
570
    def object_delete(self, object, until=None, delimiter=None, *args, **kwargs):
571
        """ Full Pithos+ DELETE at object level
572
        --- request parameters --- 
573
        @param until (string): Optional timestamp
574
        """
575
        self.assert_container()
576

    
577
        self.set_param('until', until, iff=until is not None)
578
        self.set_param('delimiter', delimiter, iff=delimiter is not None)
579

    
580
        path = path4url(self.account, self.container, object)
581
        success = kwargs.pop('success', 204)
582
        return self.delete(path, *args, success=success, **kwargs)