Statistics
| Branch: | Tag: | Revision:

root / kamaki / clients / pithos / rest_api.py @ 1c366ac9

History | View | Annotate | Download (31.6 kB)

1
# Copyright 2012-2013 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
36

    
37

    
38
class PithosRestClient(StorageClient):
39

    
40
    def account_head(
41
            self,
42
            until=None,
43
            if_modified_since=None,
44
            if_unmodified_since=None,
45
            *args, **kwargs):
46
        """ Full Pithos+ HEAD at account level
47

48
        --- request parameters ---
49

50
        :param until: (string) optional timestamp
51

52
        --- request headers ---
53

54
        :param if_modified_since: (string) Retrieve if account has changed
55
            since provided timestamp
56

57
        :param if_unmodified_since: (string) Retrieve if account has not
58
            change since provided timestamp
59

60
        :returns: ConnectionResponse
61
        """
62

    
63
        self._assert_account()
64
        path = path4url(self.account)
65

    
66
        self.set_param('until', until, iff=until)
67
        self.set_header('If-Modified-Since', if_modified_since)
68
        self.set_header('If-Unmodified-Since', if_unmodified_since)
69

    
70
        success = kwargs.pop('success', 204)
71
        return self.head(path, *args, success=success, **kwargs)
72

    
73
    def account_get(
74
            self,
75
            limit=None,
76
            marker=None,
77
            format='json',
78
            show_only_shared=False,
79
            public=False,
80
            until=None,
81
            if_modified_since=None,
82
            if_unmodified_since=None,
83
            *args, **kwargs):
84
        """  Full Pithos+ GET at account level
85

86
        --- request parameters ---
87

88
        :param limit: (integer) The amount of results requested
89
            (server will use default value if None)
90

91
        :param marker: string Return containers with name
92
            lexicographically after marker
93

94
        :param format: (string) reply format can be json or xml
95
            (default: json)
96

97
        :param shared: (bool) If true, only shared containers will be
98
            included in results
99

100
        :param until: (string) optional timestamp
101

102
        --- request headers ---
103

104
        :param if_modified_since: (string) Retrieve if account has changed
105
            since provided timestamp
106

107
        :param if_unmodified_since: (string) Retrieve if account has not
108
            changed since provided timestamp
109

110
        :returns: ConnectionResponse
111
        """
112
        self._assert_account()
113

    
114
        self.set_param('limit', limit, iff=limit)
115
        self.set_param('marker', marker, iff=marker)
116
        self.set_param('format', format, iff=format)
117
        self.set_param('shared', iff=show_only_shared)
118
        self.set_param('public', iff=public)
119
        self.set_param('until', until, iff=until)
120

    
121
        self.set_header('If-Modified-Since', if_modified_since)
122
        self.set_header('If-Unmodified-Since', if_unmodified_since)
123

    
124
        path = path4url(self.account)
125
        success = kwargs.pop('success', (200, 204))
126
        return self.get(path, *args, success=success, **kwargs)
127

    
128
    def account_post(
129
            self,
130
            update=True,
131
            groups={},
132
            metadata=None,
133
            quota=None,
134
            versioning=None,
135
            *args, **kwargs):
136
        """ Full Pithos+ POST at account level
137

138
        --- request parameters ---
139

140
        :param update: (bool) if True, Do not replace metadata/groups
141

142
        --- request headers ---
143

144
        :param groups: (dict) Optional user defined groups in the form
145
            { 'group1':['user1', 'user2', ...],
146
            'group2':['userA', 'userB', ...], }
147

148
        :param metadata: (dict) Optional user defined metadata in the form
149
            { 'name1': 'value1', 'name2': 'value2', ... }
150

151
        :param quota: (integer) If supported, sets the Account quota
152

153
        :param versioning: (string) If supported, sets the Account versioning
154
            to 'auto' or some other supported versioning string
155

156
        :returns: ConnectionResponse
157
        """
158

    
159
        self._assert_account()
160

    
161
        self.set_param('update', '', iff=update)
162

    
163
        if groups:
164
            for group, usernames in groups.items():
165
                userstr = ''
166
                dlm = ''
167
                for user in usernames:
168
                    userstr = userstr + dlm + user
169
                    dlm = ','
170
                self.set_header('X-Account-Group-' + group, userstr)
171
        if metadata:
172
            for metaname, metaval in metadata.items():
173
                self.set_header('X-Account-Meta-' + metaname, metaval)
174
        self.set_header('X-Account-Policy-Quota', quota)
175
        self.set_header('X-Account-Policy-Versioning', versioning)
176

    
177
        path = path4url(self.account)
178
        success = kwargs.pop('success', 202)
179
        return self.post(path, *args, success=success, **kwargs)
180

    
181
    def container_head(
182
            self,
183
            until=None,
184
            if_modified_since=None,
185
            if_unmodified_since=None,
186
            *args, **kwargs):
187
        """ Full Pithos+ HEAD at container level
188

189
        --- request params ---
190

191
        :param until: (string) optional timestamp
192

193
        --- request headers ---
194

195
        :param if_modified_since: (string) Retrieve if account has changed
196
            since provided timestamp
197

198
        :param if_unmodified_since: (string) Retrieve if account has not
199
            changed since provided timestamp
200

201
        :returns: ConnectionResponse
202
        """
203

    
204
        self._assert_container()
205

    
206
        self.set_param('until', until, iff=until)
207

    
208
        self.set_header('If-Modified-Since', if_modified_since)
209
        self.set_header('If-Unmodified-Since', if_unmodified_since)
210

    
211
        path = path4url(self.account, self.container)
212
        success = kwargs.pop('success', 204)
213
        return self.head(path, *args, success=success, **kwargs)
214

    
215
    def container_get(
216
            self,
217
            limit=None,
218
            marker=None,
219
            prefix=None,
220
            delimiter=None,
221
            path=None,
222
            format='json',
223
            meta=[],
224
            show_only_shared=False,
225
            public=False,
226
            until=None,
227
            if_modified_since=None,
228
            if_unmodified_since=None,
229
            *args, **kwargs):
230
        """ Full Pithos+ GET at container level
231

232
        --- request parameters ---
233

234
        :param limit: (integer) The amount of results requested
235
            (server will use default value if None)
236

237
        :param marker: (string) Return containers with name lexicographically
238
            after marker
239

240
        :param prefix: (string) Return objects starting with prefix
241

242
        :param delimiter: (string) Return objects up to the delimiter
243

244
        :param path: (string) assume prefix = path and delimiter = /
245
            (overwrites prefix and delimiter)
246

247
        :param format: (string) reply format can be json or xml (default:json)
248

249
        :param meta: (list) Return objects that satisfy the key queries in
250
            the specified comma separated list (use <key>, !<key> for
251
            existence queries, <key><op><value> for value queries, where <op>
252
            can be one of =, !=, <=, >=, <, >)
253

254
        :param show_only_shared: (bool) If true, only shared containers will
255
            be included in results
256

257
        :param until: (string) optional timestamp
258

259
        --- request headers ---
260

261
        :param if_modified_since: (string) Retrieve if account has changed
262
            since provided timestamp
263

264
        :param if_unmodified_since: (string) Retrieve if account has not
265
            changed since provided timestamp
266

267
        :returns: ConnectionResponse
268
        """
269

    
270
        self._assert_container()
271

    
272
        self.set_param('limit', limit, iff=limit)
273
        self.set_param('marker', marker, iff=marker)
274
        if not path:
275
            self.set_param('prefix', prefix, iff=prefix)
276
            self.set_param('delimiter', delimiter, iff=delimiter)
277
        else:
278
            self.set_param('path', path)
279
        self.set_param('format', format, iff=format)
280
        self.set_param('shared', iff=show_only_shared)
281
        self.set_param('public', iff=public)
282
        if meta:
283
            self.set_param('meta',  ','.join(meta))
284
        self.set_param('until', until, iff=until)
285

    
286
        self.set_header('If-Modified-Since', if_modified_since)
287
        self.set_header('If-Unmodified-Since', if_unmodified_since)
288

    
289
        path = path4url(self.account, self.container)
290
        success = kwargs.pop('success', 200)
291
        return self.get(path, *args, success=success, **kwargs)
292

    
293
    def container_put(
294
            self,
295
            quota=None, versioning=None, metadata=None,
296
            *args, **kwargs):
297
        """ Full Pithos+ PUT at container level
298

299
        --- request headers ---
300

301
        :param quota: (integer) Size limit in KB
302

303
        :param versioning: (string) 'auto' or other string supported by server
304

305
        :param metadata: (dict) Optional user defined metadata in the form
306
            { 'name1': 'value1', 'name2': 'value2', ... }
307

308
        :returns: ConnectionResponse
309
        """
310
        self._assert_container()
311

    
312
        self.set_header('X-Container-Policy-Quota', quota)
313
        self.set_header('X-Container-Policy-Versioning', versioning)
314
        if metadata:
315
            for metaname, metaval in metadata.items():
316
                self.set_header('X-Container-Meta-' + metaname, metaval)
317

    
318
        path = path4url(self.account, self.container)
319
        success = kwargs.pop('success', (201, 202))
320
        return self.put(path, *args, success=success, **kwargs)
321

    
322
    def container_post(
323
            self,
324
            update=True,
325
            format='json',
326
            quota=None,
327
            versioning=None,
328
            metadata=None,
329
            content_type=None,
330
            content_length=None,
331
            transfer_encoding=None,
332
            *args, **kwargs):
333
        """ Full Pithos+ POST at container level
334

335
        --- request params ---
336

337
        :param update: (bool)  if True, Do not replace metadata/groups
338

339
        :param format: (string) json (default) or xml
340

341
        --- request headers ---
342

343
        :param quota: (integer) Size limit in KB
344

345
        :param versioning: (string) 'auto' or other string supported by server
346

347
        :param metadata: (dict) Optional user defined metadata in the form
348
            { 'name1': 'value1', 'name2': 'value2', ... }
349

350
        :param content_type: (string) set a custom content type
351

352
        :param content_length: (string) set a custrom content length
353

354
        :param transfer_encoding: (string) set a custom transfer encoding
355

356
        :returns: ConnectionResponse
357
        """
358
        self._assert_container()
359

    
360
        self.set_param('update', '', iff=update)
361
        self.set_param('format', format, iff=format)
362

    
363
        self.set_header('X-Container-Policy-Quota', quota)
364
        self.set_header('X-Container-Policy-Versioning', versioning)
365
        if metadata:
366
            for metaname, metaval in metadata.items():
367
                self.set_header('X-Container-Meta-' + metaname, metaval)
368
        self.set_header('Content-Type', content_type)
369
        self.set_header('Content-Length', content_length)
370
        self.set_header('Transfer-Encoding', transfer_encoding)
371

    
372
        path = path4url(self.account, self.container)
373
        success = kwargs.pop('success', 202)
374
        return self.post(path, *args, success=success, **kwargs)
375

    
376
    def container_delete(self, until=None, delimiter=None, *args, **kwargs):
377
        """ Full Pithos+ DELETE at container level
378

379
        --- request parameters ---
380

381
        :param until: (timestamp string) if defined, container is purged up to
382
            that time
383

384
        :returns: ConnectionResponse
385
        """
386

    
387
        self._assert_container()
388

    
389
        self.set_param('until', until, iff=until)
390
        self.set_param('delimiter', delimiter, iff=delimiter)
391

    
392
        path = path4url(self.account, self.container)
393
        success = kwargs.pop('success', 204)
394
        return self.delete(path, *args, success=success, **kwargs)
395

    
396
    def object_head(
397
            self, obj,
398
            version=None,
399
            if_etag_match=None,
400
            if_etag_not_match=None,
401
            if_modified_since=None,
402
            if_unmodified_since=None,
403
            *args, **kwargs):
404
        """ Full Pithos+ HEAD at object level
405

406
        --- request parameters ---
407

408
        :param version: (string) optional version identified
409

410
        --- request headers ---
411

412
        :param if_etag_match: (string) if provided, return only results
413
            with etag matching with this
414

415
        :param if_etag_not_match: (string) if provided, return only results
416
            with etag not matching with this
417

418
        :param if_modified_since: (string) Retrieve if account has changed
419
            since provided timestamp
420

421
        :param if_unmodified_since: (string) Retrieve if account has not
422
            changed since provided timestamp
423

424
        :returns: ConnectionResponse
425
        """
426

    
427
        self._assert_container()
428

    
429
        self.set_param('version', version, iff=version)
430

    
431
        self.set_header('If-Match', if_etag_match)
432
        self.set_header('If-None-Match', if_etag_not_match)
433
        self.set_header('If-Modified-Since', if_modified_since)
434
        self.set_header('If-Unmodified-Since', if_unmodified_since)
435

    
436
        path = path4url(self.account, self.container, obj)
437
        success = kwargs.pop('success', 200)
438
        return self.head(path, *args, success=success, **kwargs)
439

    
440
    def object_get(
441
            self, obj,
442
            format='json',
443
            hashmap=False,
444
            version=None,
445
            data_range=None,
446
            if_range=False,
447
            if_etag_match=None,
448
            if_etag_not_match=None,
449
            if_modified_since=None,
450
            if_unmodified_since=None,
451
            *args, **kwargs):
452
        """ Full Pithos+ GET at object level
453

454
        --- request parameters ---
455

456
        :param format: (string) json (default) or xml
457

458
        :param hashmap: (bool) Optional request for hashmap
459

460
        :param version: (string) optional version identified
461

462
        --- request headers ---
463

464
        :param data_range: (string) Optional range of data to retrieve
465

466
        :param if_range: (bool)
467

468
        :param if_etag_match: (string) if provided, return only results
469
            with etag matching with this
470

471
        :param if_etag_not_match: (string) if provided, return only results
472
            with etag not matching with this
473

474
        :param if_modified_since: (string) Retrieve if account has changed
475
            since provided timestamp
476

477
        :param if_unmodified_since: (string) Retrieve if account has not
478
            changed since provided timestamp
479

480
        :returns: ConnectionResponse
481
        """
482

    
483
        self._assert_container()
484

    
485
        self.set_param('format', format, iff=format)
486
        self.set_param('hashmap', hashmap, iff=hashmap)
487
        self.set_param('version', version, iff=version)
488

    
489
        self.set_header('Range', data_range)
490
        self.set_header('If-Range', '', if_range and data_range)
491
        self.set_header('If-Match', if_etag_match, )
492
        self.set_header('If-None-Match', if_etag_not_match)
493
        self.set_header('If-Modified-Since', if_modified_since)
494
        self.set_header('If-Unmodified-Since', if_unmodified_since)
495

    
496
        path = path4url(self.account, self.container, obj)
497
        success = kwargs.pop('success', 200)
498
        return self.get(path, *args, success=success, **kwargs)
499

    
500
    def object_put(
501
            self, obj,
502
            format='json',
503
            hashmap=False,
504
            delimiter=None,
505
            if_etag_match=None,
506
            if_etag_not_match=None,
507
            etag=None,
508
            content_length=None,
509
            content_type=None,
510
            transfer_encoding=None,
511
            copy_from=None,
512
            move_from=None,
513
            source_account=None,
514
            source_version=None,
515
            content_encoding=None,
516
            content_disposition=None,
517
            manifest=None,
518
            permissions=None,
519
            public=None,
520
            metadata=None,
521
            *args, **kwargs):
522
        """ Full Pithos+ PUT at object level
523

524
        --- request parameters ---
525

526
        :param format: (string) json (default) or xml
527

528
        :param hashmap: (bool) Optional hashmap provided instead of data
529

530
        --- request headers ---
531

532
        :param if_etag_match: (string) if provided, return only results
533
            with etag matching with this
534

535
        :param if_etag_not_match: (string) if provided, return only results
536
            with etag not matching with this
537

538
        :param etag: (string) The MD5 hash of the object (optional to check
539
            written data)
540

541
        :param content_length: (integer) The size of the data written
542

543
        :param content_type: (string) The MIME content type of the object
544

545
        :param transfer_encoding: (string) Set to chunked to specify
546
            incremental uploading (if used, Content-Length is ignored)
547

548
        :param copy_from: (string) The source path in the form
549
            /<container>/<object>
550

551
        :param move_from: (string) The source path in the form
552
            /<container>/<object>
553

554
        :param source_account: (string) The source account to copy/move from
555

556
        :param source_version: (string) The source version to copy from
557

558
        :param conent_encoding: (string) The encoding of the object
559

560
        :param content_disposition: (string) Presentation style of the object
561

562
        :param manifest: (string) Object parts prefix in
563
            /<container>/<object> form
564

565
        :param permissions: (dict) Object permissions in the form (all fields
566
            are optional)
567
            { 'read':[user1, group1, user2, ...],
568
            'write':['user3, group2, group3, ...] }
569

570
        :param public: (bool) If true, Object is published, False, unpublished
571

572
        :param metadata: (dict) Optional user defined metadata in the form
573
            {'meta-key-1':'meta-value-1', 'meta-key-2':'meta-value-2', ...}
574

575
        :returns: ConnectionResponse
576
        """
577

    
578
        self._assert_container()
579

    
580
        self.set_param('format', format, iff=format)
581
        self.set_param('hashmap', hashmap, iff=hashmap)
582
        self.set_param('delimiter', delimiter, iff=delimiter)
583

    
584
        self.set_header('If-Match', if_etag_match)
585
        self.set_header('If-None-Match', if_etag_not_match)
586
        self.set_header('ETag', etag)
587
        self.set_header('Content-Length', content_length)
588
        self.set_header('Content-Type', content_type)
589
        self.set_header('Transfer-Encoding', transfer_encoding)
590
        self.set_header('X-Copy-From', copy_from)
591
        self.set_header('X-Move-From', move_from)
592
        self.set_header('X-Source-Account', source_account)
593
        self.set_header('X-Source-Version', source_version)
594
        self.set_header('Content-Encoding', content_encoding)
595
        self.set_header('Content-Disposition', content_disposition)
596
        self.set_header('X-Object-Manifest', manifest)
597
        if permissions:
598
            perms = None
599
            if permissions:
600
                for perm_type, perm_list in permissions.items():
601
                    if not perms:
602
                        perms = ''  # Remove permissions
603
                    if perm_list:
604
                        perms += ';' if perms else ''
605
                        perms += '%s=%s' % (perm_type, ','.join(perm_list))
606
            self.set_header('X-Object-Sharing', perms)
607
        self.set_header('X-Object-Public', public, public is not None)
608
        if metadata:
609
            for key, val in metadata.items():
610
                self.set_header('X-Object-Meta-' + key, val)
611

    
612
        path = path4url(self.account, self.container, obj)
613
        success = kwargs.pop('success', 201)
614
        return self.put(path, *args, success=success, **kwargs)
615

    
616
    def object_copy(
617
            self, obj, destination,
618
            format='json',
619
            ignore_content_type=False,
620
            if_etag_match=None,
621
            if_etag_not_match=None,
622
            destination_account=None,
623
            content_type=None,
624
            content_encoding=None,
625
            content_disposition=None,
626
            source_version=None,
627
            permissions=None,
628
            public=None,
629
            metadata=None,
630
            *args, **kwargs):
631
        """ Full Pithos+ COPY at object level
632

633
        --- request parameters ---
634

635
        :param format: (string) json (default) or xml
636

637
        :param ignore_content_type: (bool) Ignore the supplied Content-Type
638

639
        --- request headers ---
640

641
        :param if_etag_match: (string) if provided, copy only results
642
            with etag matching with this
643

644
        :param if_etag_not_match: (string) if provided, copy only results
645
            with etag not matching with this
646

647
        :param destination: (string) The destination path in the form
648
            /<container>/<object>
649

650
        :param destination_account: (string) The destination account to copy to
651

652
        :param content_type: (string) The MIME content type of the object
653

654
        :param content_encoding: (string) The encoding of the object
655

656
        :param content_disposition: (string) Object resentation style
657

658
        :param source_version: (string) The source version to copy from
659

660
        :param permissions: (dict) Object permissions in the form
661
            (all fields are optional)
662
            { 'read':[user1, group1, user2, ...],
663
            'write':['user3, group2, group3, ...] }
664

665
        :param public: (bool) If true, Object is published, False, unpublished
666

667
        :param metadata: (dict) Optional user defined metadata in the form
668
            {'meta-key-1':'meta-value-1', 'meta-key-2':'meta-value-2', ...}
669
            Metadata are appended to the source metadata. In case of same
670
            keys, they replace the old metadata
671

672
        :returns: ConnectionResponse
673
        """
674

    
675
        self._assert_container()
676

    
677
        self.set_param('format', format, iff=format)
678
        self.set_param('ignore_content_type', iff=ignore_content_type)
679

    
680
        self.set_header('If-Match', if_etag_match)
681
        self.set_header('If-None-Match', if_etag_not_match)
682
        self.set_header('Destination', destination)
683
        self.set_header('Destination-Account', destination_account)
684
        self.set_header('Content-Type', content_type)
685
        self.set_header('Content-Encoding', content_encoding)
686
        self.set_header('Content-Disposition', content_disposition)
687
        self.set_header('X-Source-Version', source_version)
688
        if permissions:
689
            perms = ''
690
            for perm_type, perm_list in permissions.items():
691
                if not perms:
692
                    perms = ''  # Remove permissions
693
                if perm_list:
694
                    perms += ';' if perms else ''
695
                    perms += '%s=%s' % (perm_type, ','.join(perm_list))
696
            self.set_header('X-Object-Sharing', perms)
697
        self.set_header('X-Object-Public', public, public is not None)
698
        if metadata:
699
            for key, val in metadata.items():
700
                self.set_header('X-Object-Meta-' + key, val)
701

    
702
        path = path4url(self.account, self.container, obj)
703
        success = kwargs.pop('success', 201)
704
        return self.copy(path, *args, success=success, **kwargs)
705

    
706
    def object_move(
707
            self, object,
708
            format='json',
709
            ignore_content_type=False,
710
            if_etag_match=None,
711
            if_etag_not_match=None,
712
            destination=None,
713
            destination_account=None,
714
            content_type=None,
715
            content_encoding=None,
716
            content_disposition=None,
717
            permissions={},
718
            public=None,
719
            metadata={},
720
            *args, **kwargs):
721
        """ Full Pithos+ COPY at object level
722

723
        --- request parameters ---
724

725
        :param format: (string) json (default) or xml
726

727
        :param ignore_content_type: (bool) Ignore the supplied Content-Type
728

729
        --- request headers ---
730

731
        :param if_etag_match: (string) if provided, return only results
732
            with etag matching with this
733

734
        :param if_etag_not_match: (string) if provided, return only results
735
            with etag not matching with this
736

737
        :param destination: (string) The destination path in the form
738
            /<container>/<object>
739

740
        :param destination_account: (string) The destination account to copy to
741

742
        :param content_type: (string) The MIME content type of the object
743

744
        :param content_encoding: (string) The encoding of the object
745

746
        :param content_disposition: (string) Object presentation style
747

748
        :param source_version: (string) The source version to copy from
749

750
        :param permissions: (dict) Object permissions in the form
751
            (all fields are optional)
752
            { 'read':[user1, group1, user2, ...],
753
            'write':['user3, group2, group3, ...] }
754

755
        :param public: (bool) If true, Object is published, False, unpublished
756

757
        :param metadata: (dict) Optional user defined metadata in the form
758
            {'meta-key-1':'meta-value-1', 'meta-key-2':'meta-value-2', ...}
759

760
        :returns: ConnectionResponse
761
        """
762

    
763
        self._assert_container()
764

    
765
        self.set_param('format', format, iff=format)
766
        self.set_param('ignore_content_type', iff=ignore_content_type)
767

    
768
        self.set_header('If-Match', if_etag_match)
769
        self.set_header('If-None-Match', if_etag_not_match)
770
        self.set_header('Destination', destination)
771
        self.set_header('Destination-Account', destination_account)
772
        self.set_header('Content-Type', content_type)
773
        self.set_header('Content-Encoding', content_encoding)
774
        self.set_header('Content-Disposition', content_disposition)
775
        perms = ';'.join(
776
            ['%s=%s' % (k, ','.join(v)) for k, v in permissions.items() if (
777
                v)]) if (permissions) else ''
778
        self.set_header('X-Object-Sharing', perms, iff=permissions)
779
        self.set_header('X-Object-Public', public, public is not None)
780
        if metadata:
781
            for key, val in metadata.items():
782
                self.set_header('X-Object-Meta-' + key, val)
783

    
784
        path = path4url(self.account, self.container, object)
785
        success = kwargs.pop('success', 201)
786
        return self.move(path, *args, success=success, **kwargs)
787

    
788
    def object_post(
789
            self, obj,
790
            format='json',
791
            update=True,
792
            if_etag_match=None,
793
            if_etag_not_match=None,
794
            content_length=None,
795
            content_type=None,
796
            content_range=None,
797
            transfer_encoding=None,
798
            content_encoding=None,
799
            content_disposition=None,
800
            source_object=None,
801
            source_account=None,
802
            source_version=None,
803
            object_bytes=None,
804
            manifest=None,
805
            permissions={},
806
            public=None,
807
            metadata={},
808
            *args, **kwargs):
809
        """ Full Pithos+ POST at object level
810

811
        --- request parameters ---
812

813
        :param format: (string) json (default) or xml
814

815
        :param update: (bool) Do not replace metadata
816

817
        --- request headers ---
818

819
        :param if_etag_match: (string) if provided, return only results
820
            with etag matching with this
821

822
        :param if_etag_not_match: (string) if provided, return only results
823
            with etag not matching with this
824

825
        :param content_length: (string) The size of the data written
826

827
        :param content_type: (string) The MIME content type of the object
828

829
        :param content_range: (string) The range of data supplied
830

831
        :param transfer_encoding: (string) Set to chunked to specify
832
            incremental uploading (if used, Content-Length is ignored)
833

834
        :param content_encoding: (string) The encoding of the object
835

836
        :param content_disposition: (string) Object presentation style
837

838
        :param source_object: (string) Update with data from the object at
839
            path /<container>/<object>
840

841
        :param source_account: (string) The source account to update from
842

843
        :param source_version: (string) The source version to copy from
844

845
        :param object_bytes: (integer) The updated objects final size
846

847
        :param manifest: (string) Object parts prefix as /<container>/<object>
848

849
        :param permissions: (dict) Object permissions in the form (all fields
850
            are optional)
851
            { 'read':[user1, group1, user2, ...],
852
            'write':['user3, group2, group3, ...] }
853

854
        :param public: (bool) If true, Object is published, False, unpublished
855

856
        :param metadata: (dict) Optional user defined metadata in the form
857
            {'meta-key-1':'meta-value-1', 'meta-key-2':'meta-value-2', ...}
858

859
        :returns: ConnectionResponse
860
        """
861

    
862
        self._assert_container()
863

    
864
        self.set_param('format', format, iff=format)
865
        self.set_param('update', '', iff=update)
866

    
867
        self.set_header('If-Match', if_etag_match)
868
        self.set_header('If-None-Match', if_etag_not_match)
869
        self.set_header(
870
            'Content-Length', content_length, iff=not transfer_encoding)
871
        self.set_header('Content-Type', content_type)
872
        self.set_header('Content-Range', content_range)
873
        self.set_header('Transfer-Encoding', transfer_encoding)
874
        self.set_header('Content-Encoding', content_encoding)
875
        self.set_header('Content-Disposition', content_disposition)
876
        self.set_header('X-Source-Object', source_object)
877
        self.set_header('X-Source-Account', source_account)
878
        self.set_header('X-Source-Version', source_version)
879
        self.set_header('X-Object-Bytes', object_bytes)
880
        self.set_header('X-Object-Manifest', manifest)
881
        perms = ';'.join(
882
            ['%s=%s' % (k, ','.join(v)) for k, v in permissions.items() if (
883
                v)]) if (permissions) else ''
884
        self.set_header('X-Object-Sharing', perms, iff=permissions)
885
        self.set_header('X-Object-Public', public, public is not None)
886
        for key, val in metadata.items():
887
            self.set_header('X-Object-Meta-' + key, val)
888

    
889
        path = path4url(self.account, self.container, obj)
890
        success = kwargs.pop('success', (202, 204))
891
        return self.post(path, *args, success=success, **kwargs)
892

    
893
    def object_delete(
894
            self, object,
895
            until=None, delimiter=None,
896
            *args, **kwargs):
897
        """ Full Pithos+ DELETE at object level
898

899
        --- request parameters ---
900

901
        :param until: (string) Optional timestamp
902

903
        :returns: ConnectionResponse
904
        """
905
        self._assert_container()
906

    
907
        self.set_param('until', until, iff=until)
908
        self.set_param('delimiter', delimiter, iff=delimiter)
909

    
910
        path = path4url(self.account, self.container, object)
911
        success = kwargs.pop('success', 204)
912
        return self.delete(path, *args, success=success, **kwargs)