Revision 5df6c6d1
b/docs/source/devguide.rst | ||
---|---|---|
25 | 25 |
========================= ================================ |
26 | 26 |
Revision Description |
27 | 27 |
========================= ================================ |
28 |
0.7 (Oct 13, 2011) Suggest upload/download methods using hashmaps.
|
|
28 |
0.7 (Oct 17, 2011) Suggest upload/download methods using hashmaps.
|
|
29 | 29 |
\ Propose syncing algorithm. |
30 | 30 |
\ Support cross-account object copy and move. |
31 | 31 |
\ Pass token as a request parameter when using ``POST`` via an HTML form. |
32 | 32 |
\ Optionally use source account to update object from another object. |
33 | 33 |
\ Use container ``POST`` to upload missing blocks of data. |
34 | 34 |
\ Report policy in account headers. |
35 |
\ Add insufficient quota reply. |
|
35 | 36 |
0.6 (Sept 13, 2011) Reply with Merkle hash as the ETag when updating objects. |
36 | 37 |
\ Include version id in object replace/change replies. |
37 | 38 |
\ Change conflict (409) replies format to text. |
... | ... | |
763 | 764 |
|
764 | 765 |
The ``X-Object-Sharing`` header may include either a ``read=...`` comma-separated user/group list, or a ``write=...`` comma-separated user/group list, or both separated by a semicolon (``;``). Groups are specified as ``<account>:<group>``. To publish the object, set ``X-Object-Public`` to ``true``. To unpublish, set to ``false``, or use an empty header value. |
765 | 766 |
|
766 |
=========================== ============================== |
|
767 |
Return Code Description |
|
768 |
=========================== ============================== |
|
769 |
201 (Created) The object has been created |
|
770 |
409 (Conflict) The object can not be created from the provided hashmap, or there are conflicting permissions (a list of missing hashes, or a list of conflicting sharing paths will be included in the reply - in simple text format) |
|
771 |
411 (Length Required) Missing ``Content-Length`` or ``Content-Type`` in the request |
|
772 |
422 (Unprocessable Entity) The MD5 checksum of the data written to the storage system does not match the (optionally) supplied ETag value |
|
773 |
=========================== ============================== |
|
767 |
============================== ============================== |
|
768 |
Return Code Description |
|
769 |
============================== ============================== |
|
770 |
201 (Created) The object has been created |
|
771 |
409 (Conflict) The object can not be created from the provided hashmap, or there are conflicting permissions (a list of missing hashes, or a list of conflicting sharing paths will be included in the reply - in simple text format) |
|
772 |
411 (Length Required) Missing ``Content-Length`` or ``Content-Type`` in the request |
|
773 |
413 (Request Entity Too Large) Insufficient quota to complete the request |
|
774 |
422 (Unprocessable Entity) The MD5 checksum of the data written to the storage system does not match the (optionally) supplied ETag value |
|
775 |
============================== ============================== |
|
774 | 776 |
|
775 | 777 |
|
776 | 778 |
COPY |
... | ... | |
803 | 805 |
|
804 | 806 |
| |
805 | 807 |
|
806 |
=========================== ============================== |
|
807 |
Return Code Description |
|
808 |
=========================== ============================== |
|
809 |
201 (Created) The object has been created |
|
810 |
409 (Conflict) There are conflicting permissions (a list of conflicting sharing paths will be included in the reply - in simple text format) |
|
811 |
=========================== ============================== |
|
808 |
============================== ============================== |
|
809 |
Return Code Description |
|
810 |
============================== ============================== |
|
811 |
201 (Created) The object has been created |
|
812 |
409 (Conflict) There are conflicting permissions (a list of conflicting sharing paths will be included in the reply - in simple text format) |
|
813 |
413 (Request Entity Too Large) Insufficient quota to complete the request |
|
814 |
============================== ============================== |
|
812 | 815 |
|
813 | 816 |
|
814 | 817 |
MOVE |
... | ... | |
878 | 881 |
|
879 | 882 |
| |
880 | 883 |
|
881 |
=========================== ============================== |
|
882 |
Return Code Description |
|
883 |
=========================== ============================== |
|
884 |
202 (Accepted) The request has been accepted (not a data update) |
|
885 |
204 (No Content) The request succeeded (data updated) |
|
886 |
409 (Conflict) There are conflicting permissions (a list of conflicting sharing paths will be included in the reply - in simple text format) |
|
887 |
411 (Length Required) Missing ``Content-Length`` in the request |
|
888 |
416 (Range Not Satisfiable) The supplied range is invalid |
|
889 |
=========================== ============================== |
|
884 |
============================== ============================== |
|
885 |
Return Code Description |
|
886 |
============================== ============================== |
|
887 |
202 (Accepted) The request has been accepted (not a data update) |
|
888 |
204 (No Content) The request succeeded (data updated) |
|
889 |
409 (Conflict) There are conflicting permissions (a list of conflicting sharing paths will be included in the reply - in simple text format) |
|
890 |
411 (Length Required) Missing ``Content-Length`` in the request |
|
891 |
413 (Request Entity Too Large) Insufficient quota to complete the request |
|
892 |
416 (Range Not Satisfiable) The supplied range is invalid |
|
893 |
============================== ============================== |
|
890 | 894 |
|
891 | 895 |
The ``POST`` method can also be used for creating an object via a standard HTML form. If the request ``Content-Type`` is ``multipart/form-data``, none of the above headers will be processed. The form should have an ``X-Object-Data`` field, as in the following example. The token is passed as a request parameter. :: |
892 | 896 |
|
... | ... | |
906 | 910 |
|
907 | 911 |
| |
908 | 912 |
|
909 |
=========================== ============================== |
|
910 |
Return Code Description |
|
911 |
=========================== ============================== |
|
912 |
201 (Created) The object has been created |
|
913 |
=========================== ============================== |
|
913 |
============================== ============================== |
|
914 |
Return Code Description |
|
915 |
============================== ============================== |
|
916 |
201 (Created) The object has been created |
|
917 |
413 (Request Entity Too Large) Insufficient quota to complete the request |
|
918 |
============================== ============================== |
|
914 | 919 |
|
915 | 920 |
|
916 | 921 |
DELETE |
b/pithos/api/functions.py | ||
---|---|---|
43 | 43 |
from xml.dom import minidom |
44 | 44 |
|
45 | 45 |
from pithos.api.faults import (Fault, NotModified, BadRequest, Unauthorized, ItemNotFound, Conflict, |
46 |
LengthRequired, PreconditionFailed, RangeNotSatisfiable, UnprocessableEntity) |
|
46 |
LengthRequired, PreconditionFailed, RequestEntityTooLarge, RangeNotSatisfiable, UnprocessableEntity)
|
|
47 | 47 |
from pithos.api.util import (rename_meta_key, format_header_key, printable_header_dict, get_account_headers, |
48 | 48 |
put_account_headers, get_container_headers, put_container_headers, get_object_headers, put_object_headers, |
49 | 49 |
update_manifest_meta, update_sharing_meta, update_public_meta, validate_modification_preconditions, |
... | ... | |
51 | 51 |
get_int_parameter, get_content_length, get_content_range, socket_read_iterator, |
52 | 52 |
object_data_response, put_object_block, hashmap_hash, api_method, json_encode_decimal) |
53 | 53 |
from pithos.backends import connect_backend |
54 |
from pithos.backends.base import NotAllowedError |
|
54 |
from pithos.backends.base import NotAllowedError, QuotaError
|
|
55 | 55 |
|
56 | 56 |
|
57 | 57 |
logger = logging.getLogger(__name__) |
... | ... | |
838 | 838 |
raise BadRequest('Invalid sharing header') |
839 | 839 |
except AttributeError, e: |
840 | 840 |
raise Conflict('\n'.join(e.data) + '\n') |
841 |
except QuotaError: |
|
842 |
raise RequestEntityTooLarge('Quota exceeded') |
|
841 | 843 |
if public is not None: |
842 | 844 |
try: |
843 | 845 |
request.backend.update_object_public(request.user, v_account, |
... | ... | |
884 | 886 |
raise Unauthorized('Access denied') |
885 | 887 |
except NameError: |
886 | 888 |
raise ItemNotFound('Container does not exist') |
889 |
except QuotaError: |
|
890 |
raise RequestEntityTooLarge('Quota exceeded') |
|
887 | 891 |
|
888 | 892 |
response = HttpResponse(status=201) |
889 | 893 |
response['ETag'] = meta['hash'] |
... | ... | |
1163 | 1167 |
raise BadRequest('Invalid sharing header') |
1164 | 1168 |
except AttributeError, e: |
1165 | 1169 |
raise Conflict('\n'.join(e.data) + '\n') |
1170 |
except QuotaError: |
|
1171 |
raise RequestEntityTooLarge('Quota exceeded') |
|
1166 | 1172 |
if public is not None: |
1167 | 1173 |
try: |
1168 | 1174 |
request.backend.update_object_public(request.user, v_account, |
b/pithos/api/util.py | ||
---|---|---|
46 | 46 |
|
47 | 47 |
from pithos.api.compat import parse_http_date_safe, parse_http_date |
48 | 48 |
from pithos.api.faults import (Fault, NotModified, BadRequest, Unauthorized, ItemNotFound, |
49 |
Conflict, LengthRequired, PreconditionFailed, RangeNotSatisfiable,
|
|
50 |
ServiceUnavailable) |
|
49 |
Conflict, LengthRequired, PreconditionFailed, RequestEntityTooLarge,
|
|
50 |
RangeNotSatisfiable, ServiceUnavailable)
|
|
51 | 51 |
from pithos.backends import connect_backend |
52 |
from pithos.backends.base import NotAllowedError |
|
52 |
from pithos.backends.base import NotAllowedError, QuotaError
|
|
53 | 53 |
|
54 | 54 |
import logging |
55 | 55 |
import re |
... | ... | |
310 | 310 |
raise BadRequest('Invalid sharing header') |
311 | 311 |
except AttributeError, e: |
312 | 312 |
raise Conflict('\n'.join(e.data) + '\n') |
313 |
except QuotaError: |
|
314 |
raise RequestEntityTooLarge('Quota exceeded') |
|
313 | 315 |
if public is not None: |
314 | 316 |
try: |
315 | 317 |
request.backend.update_object_public(request.user, dest_account, dest_container, dest_name, public) |
b/pithos/backends/base.py | ||
---|---|---|
34 | 34 |
class NotAllowedError(Exception): |
35 | 35 |
pass |
36 | 36 |
|
37 |
class QuotaError(Exception): |
|
38 |
pass |
|
39 |
|
|
37 | 40 |
class BaseBackend(object): |
38 | 41 |
"""Abstract backend class that serves as a reference for actual implementations. |
39 | 42 |
|
... | ... | |
427 | 430 |
ValueError: Invalid users/groups in permissions |
428 | 431 |
|
429 | 432 |
AttributeError: Can not set permissions |
433 |
|
|
434 |
QuotaError: Account or container quota exceeded |
|
430 | 435 |
""" |
431 | 436 |
return '' |
432 | 437 |
|
... | ... | |
452 | 457 |
ValueError: Invalid users/groups in permissions |
453 | 458 |
|
454 | 459 |
AttributeError: Can not set permissions |
460 |
|
|
461 |
QuotaError: Account or container quota exceeded |
|
455 | 462 |
""" |
456 | 463 |
return '' |
457 | 464 |
|
... | ... | |
473 | 480 |
ValueError: Invalid users/groups in permissions |
474 | 481 |
|
475 | 482 |
AttributeError: Can not set permissions |
483 |
|
|
484 |
QuotaError: Account or container quota exceeded |
|
476 | 485 |
""" |
477 | 486 |
return '' |
478 | 487 |
|
b/pithos/backends/modular.py | ||
---|---|---|
38 | 38 |
import hashlib |
39 | 39 |
import binascii |
40 | 40 |
|
41 |
from base import NotAllowedError, BaseBackend |
|
41 |
from base import NotAllowedError, QuotaError, BaseBackend
|
|
42 | 42 |
from lib.hashfiler import Mapper, Blocker |
43 | 43 |
|
44 | 44 |
( CLUSTER_NORMAL, CLUSTER_HISTORY, CLUSTER_DELETED ) = range(3) |
... | ... | |
546 | 546 |
if (account_quota > 0 and self._get_statistics(account_node)[1] + size_delta > account_quota) or \ |
547 | 547 |
(container_quota > 0 and self._get_statistics(container_node)[1] + size_delta > container_quota): |
548 | 548 |
# This must be executed in a transaction, so the version is never created if it fails. |
549 |
raise |
|
549 |
raise QuotaError
|
|
550 | 550 |
|
551 | 551 |
if not replace_meta and src_version_id is not None: |
552 | 552 |
self.node.attribute_copy(src_version_id, dest_version_id) |
Also available in: Unified diff