Statistics
| Branch: | Tag: | Revision:

root / kamaki / clients / pithos_rest_api.py @ 3dabe5d2

History | View | Annotate | Download (31.4 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 kamaki.clients.storage import StorageClient
35
from kamaki.clients.utils import path4url, list2str
36

    
37

    
38
class PithosRestAPI(StorageClient):
39

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

    
58
        self.set_param('until', until, iff=until is not None)
59
        self.set_header('If-Modified-Since', if_modified_since)
60
        self.set_header('If-Unmodified-Since', if_unmodified_since)
61

    
62
        success = kwargs.pop('success', 204)
63
        return self.head(path, *args, success=success, **kwargs)
64

    
65
    def account_get(self,
66
        limit=None,
67
        marker=None,
68
        format='json',
69
        show_only_shared=False,
70
        until=None,
71
        if_modified_since=None,
72
        if_unmodified_since=None,
73
        *args,
74
        **kwargs):
75
        """  Full Pithos+ GET at account level
76
        --- request parameters ---
77
        @param limit (integer): The amount of results requested
78
            (server will use default value if None)
79
        @param marker (string): Return containers with name
80
            lexicographically after marker
81
        @param format (string): reply format can be json or xml
82
            (default: json)
83
        @param shared (bool): If true, only shared containers will be
84
            included in results
85
        @param until (string): optional timestamp
86
        --- --- optional request headers ---
87
        @param if_modified_since (string): Retrieve if account has changed
88
            since provided timestamp
89
        @param if_unmodified_since (string): Retrieve if account has not
90
            changed since provided timestamp
91
        """
92
        self.assert_account()
93

    
94
        self.set_param('format', format, iff=format is not None)
95
        self.set_param('limit', limit, iff=limit is not None)
96
        self.set_param('marker', marker, iff=marker is not None)
97
        self.set_param('shared', iff=show_only_shared)
98
        self.set_param('until', until, iff=until is not None)
99

    
100
        self.set_header('If-Modified-Since', if_modified_since)
101
        self.set_header('If-Unmodified-Since', if_unmodified_since)
102

    
103
        path = path4url(self.account)
104
        success = kwargs.pop('success', (200, 204))
105
        return self.get(path, *args, success=success, **kwargs)
106

    
107
    def account_post(self,
108
        update=True,
109
        groups={},
110
        metadata=None,
111
        quota=None,
112
        versioning=None,
113
        *args,
114
        **kwargs):
115
        """ Full Pithos+ POST at account level
116
        --- request parameters ---
117
        @param update (bool): if True, Do not replace metadata/groups
118
        --- request headers ---
119
        @groups (dict): Optional user defined groups in the form
120
                    {   'group1':['user1', 'user2', ...],
121
                        'group2':['userA', 'userB', ...], ...
122
                    }
123
        @metadata (dict): Optional user defined metadata in the form
124
                    {   'name1': 'value1',
125
                        'name2': 'value2', ...
126
                    }
127
        @param quota(integer): If supported, sets the Account quota
128
        @param versioning(string): If supported, sets the Account versioning
129
                    to 'auto' or some other supported versioning string
130
        """
131
        self.assert_account()
132

    
133
        self.set_param('update', iff=update)
134

    
135
        for group, usernames in groups.items():
136
            userstr = ''
137
            dlm = ''
138
            for user in usernames:
139
                userstr = userstr + dlm + user
140
                dlm = ','
141
            self.set_header('X-Account-Group-' + group, userstr)
142
        if metadata is not None:
143
            for metaname, metaval in metadata.items():
144
                self.set_header('X-Account-Meta-' + metaname, metaval)
145
        self.set_header('X-Account-Policy-Quota', quota)
146
        self.set_header('X-Account-Policy-Versioning', versioning)
147

    
148
        path = path4url(self.account)
149
        success = kwargs.pop('success', 202)
150
        return self.post(path, *args, success=success, **kwargs)
151

    
152
    def container_head(self, until=None,
153
        if_modified_since=None, if_unmodified_since=None, *args, **kwargs):
154
        """ Full Pithos+ HEAD at container level
155
        --- request params ---
156
        @param until (string): optional timestamp
157
        --- optional request headers ---
158
        @param if_modified_since (string): Retrieve if account has changed
159
            since provided timestamp
160
        @param if_unmodified_since (string): Retrieve if account has not
161
            changed since provided timestamp
162
        """
163
        self.assert_container()
164

    
165
        self.set_param('until', until, iff=until is not None)
166

    
167
        self.set_header('If-Modified-Since', if_modified_since)
168
        self.set_header('If-Unmodified-Since', if_unmodified_since)
169

    
170
        path = path4url(self.account, self.container)
171
        success = kwargs.pop('success', 204)
172
        return self.head(path, *args, success=success, **kwargs)
173

    
174
    def container_get(self,
175
        limit=None,
176
        marker=None,
177
        prefix=None,
178
        delimiter=None,
179
        path=None,
180
        format='json',
181
        meta=[],
182
        show_only_shared=False,
183
        until=None,
184
        if_modified_since=None,
185
        if_unmodified_since=None,
186
        *args,
187
        **kwargs):
188
        """ Full Pithos+ GET at container level
189
        --- request parameters ---
190
        @param limit (integer): The amount of results requested
191
            (server qill use default value if None)
192
        @param marker (string): Return containers with name lexicographically
193
            after marker
194
        @param prefix (string): Return objects starting with prefix
195
        @param delimiter (string): Return objects up to the delimiter
196
        @param path (string): assume prefix = path and delimiter = /
197
            (overwrites prefix and delimiter)
198
        @param format (string): reply format can be json or xml (default:json)
199
        @param meta (list): Return objects that satisfy the key queries in
200
            the specified comma separated list (use <key>, !<key> for
201
            existence queries, <key><op><value> for value queries, where <op>
202
            can be one of =, !=, <=, >=, <, >)
203
        @param shared (bool): If true, only shared containers will be included
204
        in results
205
        @param until (string): optional timestamp
206
        --- --- optional request headers ---
207
        @param if_modified_since (string): Retrieve if account has changed
208
            since provided timestamp
209
        @param if_unmodified_since (string): Retrieve if account has not
210
            changed since provided timestamp
211
        """
212
        self.assert_container()
213

    
214
        self.set_param('format', format, iff=format is not None)
215
        self.set_param('limit', limit, iff=limit is not None)
216
        self.set_param('marker', marker, iff=marker is not None)
217
        if path is None:
218
            self.set_param('prefix', prefix, iff=prefix is not None)
219
            self.set_param('delimiter', delimiter, iff=delimiter is not None)
220
        else:
221
            self.set_param('path', path)
222
        self.set_param('shared', iff=show_only_shared)
223
        self.set_param('meta',
224
            list2str(meta),
225
            iff=meta is not None and len(meta) > 0)
226
        self.set_param('until', until, iff=until is not None)
227

    
228
        self.set_header('If-Modified-Since', if_modified_since)
229
        self.set_header('If-Unmodified-Since', if_unmodified_since)
230

    
231
        path = path4url(self.account, self.container)
232
        success = kwargs.pop('success', 200)
233
        return self.get(path, *args, success=success, **kwargs)
234

    
235
    def container_put(self,
236
        quota=None,
237
        versioning=None,
238
        metadata=None,
239
        *args,
240
        **kwargs):
241
        """ Full Pithos+ PUT at container level
242
        --- request headers ---
243
        @param quota (integer): Size limit in KB
244
        @param versioning (string): 'auto' or other string supported by server
245
        @metadata (dict): Optional user defined metadata in the form
246
        {   'name1': 'value1',
247
        'name2': 'value2', ...
248
        }
249
        """
250
        self.assert_container()
251

    
252
        if metadata is not None:
253
            for metaname, metaval in metadata.items():
254
                self.set_header('X-Container-Meta-' + metaname, metaval)
255
        self.set_header('X-Container-Policy-Quota', quota)
256
        self.set_header('X-Container-Policy-Versioning', versioning)
257

    
258
        path = path4url(self.account, self.container)
259
        success = kwargs.pop('success', (201, 202))
260
        return self.put(path, *args, success=success, **kwargs)
261

    
262
    def container_post(self,
263
        update=True,
264
        format='json',
265
        quota=None,
266
        versioning=None,
267
        metadata=None,
268
        content_type=None,
269
        content_length=None,
270
        transfer_encoding=None,
271
        *args,
272
        **kwargs):
273
        """ Full Pithos+ POST at container level
274
        --- request params ---
275
        @param update (bool):  if True, Do not replace metadata/groups
276
        @param format(string): json (default) or xml
277
        --- request headers ---
278
        @param quota (integer): Size limit in KB
279
        @param versioning (string): 'auto' or other string supported by server
280
        @metadata (dict): Optional user defined metadata in the form
281
        {   'name1': 'value1',
282
        'name2': 'value2', ...
283
        }
284
        @param content_type (string): set a custom content type
285
        @param content_length (string): set a custrom content length
286
        @param transfer_encoding (string): set a custrom transfer encoding
287
        """
288
        self.assert_container()
289

    
290
        self.set_param('format', format, iff=format is not None)
291
        self.set_param('update', iff=update)
292

    
293
        if metadata is not None:
294
            for metaname, metaval in metadata.items():
295
                self.set_header('X-Container-Meta-' + metaname, metaval)
296
        self.set_header('X-Container-Policy-Quota', quota)
297
        self.set_header('X-Container-Policy-Versioning', versioning)
298
        self.set_header('Content-Type', content_type)
299
        self.set_header('Content-Length', content_length)
300
        self.set_header('Transfer-Encoding', transfer_encoding)
301

    
302
        path = path4url(self.account, self.container)
303
        success = kwargs.pop('success', 202)
304
        return self.post(path, *args, success=success, **kwargs)
305

    
306
    def container_delete(self, until=None, delimiter=None, *args, **kwargs):
307
        """ Full Pithos+ DELETE at container level
308
        --- request parameters ---
309
        @param until (timestamp string): if defined, container is purged up to
310
            that time
311
        """
312
        self.assert_container()
313

    
314
        self.set_param('until', until, iff=until is not None)
315
        self.set_param('delimiter', delimiter, iff=delimiter is not None)
316

    
317
        path = path4url(self.account, self.container)
318
        success = kwargs.pop('success', 204)
319
        return self.delete(path, success=success)
320

    
321
    def object_head(self, object,
322
        version=None,
323
        if_etag_match=None,
324
        if_etag_not_match=None,
325
        if_modified_since=None,
326
        if_unmodified_since=None,
327
        *args,
328
        **kwargs):
329
        """ Full Pithos+ HEAD at object level
330
        --- request parameters ---
331
        @param version (string): optional version identified
332
        --- request headers ---
333
        @param if_etag_match (string): if provided, return only results
334
                with etag matching with this
335
        @param if_etag_not_match (string): if provided, return only results
336
                with etag not matching with this
337
        @param if_modified_since (string): Retrieve if account has changed
338
            since provided timestamp
339
        @param if_unmodified_since (string): Retrieve if account has not
340
            changed since provided timestamp
341
        """
342
        self.assert_container()
343

    
344
        self.set_param('version', version, iff=version is not None)
345

    
346
        self.set_header('If-Match', if_etag_match)
347
        self.set_header('If-None-Match', if_etag_not_match)
348
        self.set_header('If-Modified-Since', if_modified_since)
349
        self.set_header('If-Unmodified-Since', if_unmodified_since)
350

    
351
        path = path4url(self.account, self.container, object)
352
        success = kwargs.pop('success', 200)
353
        return self.head(path, *args, success=success, **kwargs)
354

    
355
    def object_get(self, object,
356
        format='json',
357
        hashmap=False,
358
        version=None,
359
        data_range=None,
360
        if_range=False,
361
        if_etag_match=None,
362
        if_etag_not_match=None,
363
        if_modified_since=None,
364
        if_unmodified_since=None,
365
        *args,
366
        **kwargs):
367
        """ Full Pithos+ GET at object level
368
        --- request parameters ---
369
        @param format (string): json (default) or xml
370
        @param hashmap (bool): Optional request for hashmap
371
        @param version (string): optional version identified
372
        --- request headers ---
373
        @param data_range (string): Optional range of data to retrieve
374
        @param if_range (bool):
375
        @param if_etag_match (string): if provided, return only results
376
                with etag matching with this
377
        @param if_etag_not_match (string): if provided, return only results
378
                with etag not matching with this
379
        @param if_modified_since (string): Retrieve if account has changed
380
            since provided timestamp
381
        @param if_unmodified_since (string): Retrieve if account has not
382
            changed since provided timestamp
383
        """
384
        self.assert_container()
385

    
386
        self.set_param('format', format, iff=format is not None)
387
        self.set_param('version', version, iff=version is not None)
388
        self.set_param('hashmap', hashmap, iff=hashmap)
389

    
390
        self.set_header('Range', data_range)
391
        self.set_header('If-Range', '',
392
            if_range is True and data_range is not None)
393
        self.set_header('If-Match', if_etag_match, )
394
        self.set_header('If-None-Match', if_etag_not_match)
395
        self.set_header('If-Modified-Since', if_modified_since)
396
        self.set_header('If-Unmodified-Since', if_unmodified_since)
397

    
398
        path = path4url(self.account, self.container, object)
399
        success = kwargs.pop('success', 200)
400
        return self.get(path, *args, success=success, **kwargs)
401

    
402
    def object_put(self, object,
403
        format='json',
404
        hashmap=False,
405
        delimiter=None,
406
        if_etag_match=None,
407
        if_etag_not_match=None,
408
        etag=None,
409
        content_length=None,
410
        content_type=None,
411
        transfer_encoding=None,
412
        copy_from=None,
413
        move_from=None,
414
        source_account=None,
415
        source_version=None,
416
        content_encoding=None,
417
        content_disposition=None,
418
        manifest=None,
419
        permissions=None,
420
        public=None,
421
        metadata=None,
422
        *args,
423
        **kwargs):
424
        """ Full Pithos+ PUT at object level
425
        --- request parameters ---
426
        @param format (string): json (default) or xml
427
        @param hashmap (bool): Optional hashmap provided instead of data
428
        --- request headers ---
429
        @param if_etag_match (string): if provided, return only results
430
                with etag matching with this
431
        @param if_etag_not_match (string): if provided, return only results
432
                with etag not matching with this
433
        @param etag (string): The MD5 hash of the object (optional to check
434
            written data)
435
        @param content_length (integer): The size of the data written
436
        @param content_type (string): The MIME content type of the object
437
        @param transfer_encoding (string): Set to chunked to specify
438
            incremental uploading (if used, Content-Length is ignored)
439
        @param copy_from (string): The source path in the form
440
            /<container>/<object>
441
        @param move_from (string): The source path in the form
442
            /<container>/<object>
443
        @param source_account (string): The source account to copy/move from
444
        @param source_version (string): The source version to copy from
445
        @param conent_encoding (string): The encoding of the object
446
        @param content_disposition (string): Presentation style of the object
447
        @param manifest (string): Object parts prefix in
448
            /<container>/<object> form
449
        @param permissions (dict): Object permissions in the form (all fields
450
            are optional)
451
            {   'read':[user1, group1, user2, ...],
452
                'write':['user3, group2, group3, ...]
453
            }
454
        @param public (bool): If true, Object is publicly accessible,
455
            if false, not
456
        @param metadata (dict): Optional user defined metadata in the form
457
                {'meta-key-1':'meta-value-1', 'meta-key-2':'meta-value-2', ...}
458
        """
459
        self.assert_container()
460

    
461
        self.set_param('format', format, iff=format is not None)
462
        self.set_param('hashmap', hashmap, iff=hashmap)
463
        self.set_param('delimiter', delimiter, iff=delimiter is not None)
464

    
465
        self.set_header('If-Match', if_etag_match)
466
        self.set_header('If-None-Match', if_etag_not_match)
467
        self.set_header('ETag', etag)
468
        self.set_header('Content-Length', content_length)
469
        self.set_header('Content-Type', content_type)
470
        self.set_header('Transfer-Encoding', transfer_encoding)
471
        self.set_header('X-Copy-From', copy_from)
472
        self.set_header('X-Move-From', move_from)
473
        self.set_header('X-Source-Account', source_account)
474
        self.set_header('X-Source-Version', source_version)
475
        self.set_header('Content-Encoding', content_encoding)
476
        self.set_header('Content-Disposition', content_disposition)
477
        self.set_header('X-Object-Manifest', manifest)
478
        perms = None
479
        if permissions:
480
            for permission_type, permission_list in permissions.items():
481
                if perms is None:
482
                    perms = ''  # Remove permissions
483
                if len(permission_list) == 0:
484
                    continue
485
                if len(perms):
486
                    perms += ';'
487
                perms += '%s=%s'\
488
                % (permission_type, list2str(permission_list, seperator=','))
489
        self.set_header('X-Object-Sharing', perms)
490
        self.set_header('X-Object-Public', public)
491
        if metadata is not None:
492
            for key, val in metadata.items():
493
                self.set_header('X-Object-Meta-' + key, val)
494

    
495
        path = path4url(self.account, self.container, object)
496
        success = kwargs.pop('success', 201)
497
        return self.put(path, *args, success=success, **kwargs)
498

    
499
    def object_copy(self, object, destination,
500
        format='json',
501
        ignore_content_type=False,
502
        if_etag_match=None,
503
        if_etag_not_match=None,
504
        destination_account=None,
505
        content_type=None,
506
        content_encoding=None,
507
        content_disposition=None,
508
        source_version=None,
509
        permissions=None,
510
        public=False,
511
        metadata=None,
512
        *args,
513
        **kwargs):
514
        """ Full Pithos+ COPY at object level
515
        --- request parameters ---
516
        @param format (string): json (default) or xml
517
        @param ignore_content_type (bool): Ignore the supplied Content-Type
518
        --- request headers ---
519
         @param if_etag_match (string): if provided, copy only results
520
                with etag matching with this
521
        @param if_etag_not_match (string): if provided, copy only results
522
                with etag not matching with this
523
        @param destination (string): The destination path in the form
524
            /<container>/<object>
525
        @param destination_account (string): The destination account to copy to
526
        @param content_type (string): The MIME content type of the object
527
        @param content_encoding (string): The encoding of the object
528
        @param content_disposition (string): Object resentation style
529
        @param source_version (string): The source version to copy from
530
        @param permissions (dict): Object permissions in the form
531
            (all fields are optional)
532
            {   'read':[user1, group1, user2, ...],
533
                'write':['user3, group2, group3, ...]
534
            }
535
            permissions override source permissions,
536
            removing any old permissions
537
        @param public (bool): If true, Object is publicly accessible
538
        @param metadata (dict): Optional user defined metadata in the form
539
            {'meta-key-1':'meta-value-1', 'meta-key-2':'meta-value-2', ...}
540
            Metadata are appended to the source metadata. In case of same
541
            keys, they replace the old metadata
542
        """
543
        self.assert_container()
544

    
545
        self.set_param('format', format, iff=format is not None)
546
        self.set_param('ignore_content_type', iff=ignore_content_type)
547

    
548
        self.set_header('If-Match', if_etag_match)
549
        self.set_header('If-None-Match', if_etag_not_match)
550
        self.set_header('Destination', destination)
551
        self.set_header('Destination-Account', destination_account)
552
        self.set_header('Content-Type', content_type)
553
        self.set_header('Content-Encoding', content_encoding)
554
        self.set_header('Content-Disposition', content_disposition)
555
        self.set_header('X-Source-Version', source_version)
556
        perms = None
557
        if permissions:
558
            for permission_type, permission_list in permissions.items():
559
                if perms is None:
560
                    perms = ''  # Remove permissions
561
                if len(permission_list) == 0:
562
                    continue
563
                if len(perms):
564
                    perms += ';'
565
                perms += '%s=%s'\
566
                % (permission_type, list2str(permission_list, seperator=','))
567
        self.set_header('X-Object-Sharing', perms)
568
        self.set_header('X-Object-Public', public)
569
        if metadata is not None:
570
            for key, val in metadata.items():
571
                self.set_header('X-Object-Meta-' + key, val)
572

    
573
        path = path4url(self.account, self.container, object)
574
        success = kwargs.pop('success', 201)
575
        return self.copy(path, *args, success=success, **kwargs)
576

    
577
    def object_move(self, object,
578
        format='json',
579
        ignore_content_type=False,
580
        if_etag_match=None,
581
        if_etag_not_match=None,
582
        destination=None,
583
        destination_account=None,
584
        content_type=None,
585
        content_encoding=None,
586
        content_disposition=None,
587
        permissions={},
588
        public=False,
589
        metadata={},
590
        *args,
591
        **kwargs):
592
        """ Full Pithos+ COPY at object level
593
        --- request parameters ---
594
        @param format (string): json (default) or xml
595
        @param ignore_content_type (bool): Ignore the supplied Content-Type
596
        --- request headers ---
597
         @param if_etag_match (string): if provided, return only results
598
                with etag matching with this
599
        @param if_etag_not_match (string): if provided, return only results
600
                with etag not matching with this
601
        @param destination (string): The destination path in the form
602
            /<container>/<object>
603
        @param destination_account (string): The destination account to copy to
604
        @param content_type (string): The MIME content type of the object
605
        @param content_encoding (string): The encoding of the object
606
        @param content_disposition (string): Object presentation style
607
        @param source_version (string): The source version to copy from
608
        @param permissions (dict): Object permissions in the form
609
            (all fields are optional)
610
            {   'read':[user1, group1, user2, ...],
611
                'write':['user3, group2, group3, ...]
612
            }
613
        @param public (bool): If true, Object is publicly accessible
614
        @param metadata (dict): Optional user defined metadata in the form
615
                {'meta-key-1':'meta-value-1', 'meta-key-2':'meta-value-2', ...}
616
        """
617
        self.assert_container()
618

    
619
        self.set_param('format', format, iff=format is not None)
620
        self.set_param('ignore_content_type', iff=ignore_content_type)
621

    
622
        self.set_header('If-Match', if_etag_match)
623
        self.set_header('If-None-Match', if_etag_not_match)
624
        self.set_header('Destination', destination)
625
        self.set_header('Destination-Account', destination_account)
626
        self.set_header('Content-Type', content_type)
627
        self.set_header('Content-Encoding', content_encoding)
628
        self.set_header('Content-Disposition', content_disposition)
629
        perms = None
630
        for permission_type, permission_list in permissions.items():
631
            if perms is None:
632
                perms = ''  # Remove permissions
633
            if len(permission_list) == 0:
634
                continue
635
            if len(perms):
636
                perms += ';'
637
            perms += '%s=%s'\
638
            % (permission_type, list2str(permission_list, seperator=','))
639
        self.set_header('X-Object-Sharing', perms)
640
        self.set_header('X-Object-Public', public)
641
        for key, val in metadata.items():
642
            self.set_header('X-Object-Meta-' + key, val)
643

    
644
        path = path4url(self.account, self.container, object)
645
        success = kwargs.pop('success', 201)
646
        return self.move(path, *args, success=success, **kwargs)
647

    
648
    def object_post(self, object,
649
        format='json',
650
        update=True,
651
        if_etag_match=None,
652
        if_etag_not_match=None,
653
        content_length=None,
654
        content_type=None,
655
        content_range=None,
656
        transfer_encoding=None,
657
        content_encoding=None,
658
        content_disposition=None,
659
        source_object=None,
660
        source_account=None,
661
        source_version=None,
662
        object_bytes=None,
663
        manifest=None,
664
        permissions={},
665
        public=False,
666
        metadata={},
667
        *args,
668
        **kwargs):
669
        """ Full Pithos+ POST at object level
670
        --- request parameters ---
671
        @param format (string): json (default) or xml
672
        @param update (bool): Do not replace metadata
673
        --- request headers ---
674
        @param if_etag_match (string): if provided, return only results
675
                with etag matching with this
676
        @param if_etag_not_match (string): if provided, return only results
677
                with etag not matching with this
678
        @param content_length (string): The size of the data written
679
        @param content_type (string): The MIME content type of the object
680
        @param content_range (string): The range of data supplied
681
        @param transfer_encoding (string): Set to chunked to specify
682
            incremental uploading (if used, Content-Length is ignored)
683
        @param content_encoding (string): The encoding of the object
684
        @param content_disposition (string): Object presentation style
685
        @param source_object (string): Update with data from the object at
686
            path /<container>/<object>
687
        @param source_account (string): The source account to update from
688
        @param source_version (string): The source version to copy from
689
        @param object_bytes (integer): The updated objects final size
690
        @param manifest (string): Object parts prefix as /<container>/<object>
691
        @param permissions (dict): Object permissions in the form (all fields
692
            are optional)
693
            {   'read':[user1, group1, user2, ...],
694
                'write':['user3, group2, group3, ...]
695
            }
696
        @param public (bool): If true, Object is publicly accessible
697
        @param metadata (dict): Optional user defined metadata in the form
698
                {'meta-key-1':'meta-value-1', 'meta-key-2':'meta-value-2', ...}
699
        """
700
        self.assert_container()
701

    
702
        self.set_param('format', format, iff=format is not None)
703
        self.set_param('update', iff=update)
704

    
705
        self.set_header('If-Match', if_etag_match)
706
        self.set_header('If-None-Match', if_etag_not_match)
707
        self.set_header('Content-Length',
708
            content_length,
709
            iff=transfer_encoding is None)
710
        self.set_header('Content-Type', content_type)
711
        self.set_header('Content-Range', content_range)
712
        self.set_header('Transfer-Encoding', transfer_encoding)
713
        self.set_header('Content-Encoding', content_encoding)
714
        self.set_header('Content-Disposition', content_disposition)
715
        self.set_header('X-Source-Object', source_object)
716
        self.set_header('X-Source-Account', source_account)
717
        self.set_header('X-Source-Version', source_version)
718
        self.set_header('X-Object-Bytes', object_bytes)
719
        self.set_header('X-Object-Manifest', manifest)
720
        perms = None
721
        for permission_type, permission_list in permissions.items():
722
            if perms is None:
723
                perms = ''  # Remove permissions
724
            if len(permission_list) == 0:
725
                continue
726
            if len(perms):
727
                perms += ';'
728
            perms += '%s=%s'\
729
            % (permission_type, list2str(permission_list, seperator=','))
730
        self.set_header('X-Object-Sharing', perms)
731
        self.set_header('X-Object-Public', public)
732
        for key, val in metadata.items():
733
            self.set_header('X-Object-Meta-' + key, val)
734

    
735
        path = path4url(self.account, self.container, object)
736
        success = kwargs.pop('success', (202, 204))
737
        return self.post(path, *args, success=success, **kwargs)
738

    
739
    def object_delete(self, object,
740
        until=None,
741
        delimiter=None,
742
        *args,
743
        **kwargs):
744
        """ Full Pithos+ DELETE at object level
745
        --- request parameters ---
746
        @param until (string): Optional timestamp
747
        """
748
        self.assert_container()
749

    
750
        self.set_param('until', until, iff=until is not None)
751
        self.set_param('delimiter', delimiter, iff=delimiter is not None)
752

    
753
        path = path4url(self.account, self.container, object)
754
        success = kwargs.pop('success', 204)
755
        return self.delete(path, *args, success=success, **kwargs)