Add insufficient quota replies in frontend. Document.
authorAntony Chazapis <chazapis@gmail.com>
Mon, 17 Oct 2011 14:30:04 +0000 (17:30 +0300)
committerAntony Chazapis <chazapis@gmail.com>
Mon, 17 Oct 2011 14:30:04 +0000 (17:30 +0300)
Refs #1165

docs/source/devguide.rst
pithos/api/functions.py
pithos/api/util.py
pithos/backends/base.py
pithos/backends/modular.py

index 7f3e2f6..baa25dc 100644 (file)
@@ -25,13 +25,14 @@ Document Revisions
 =========================  ================================
 Revision                   Description
 =========================  ================================
-0.7 (Oct 13, 2011)         Suggest upload/download methods using hashmaps.
+0.7 (Oct 17, 2011)         Suggest upload/download methods using hashmaps.
 \                          Propose syncing algorithm.
 \                          Support cross-account object copy and move.
 \                          Pass token as a request parameter when using ``POST`` via an HTML form.
 \                          Optionally use source account to update object from another object.
 \                          Use container ``POST`` to upload missing blocks of data.
 \                          Report policy in account headers.
+\                          Add insufficient quota reply.
 0.6 (Sept 13, 2011)        Reply with Merkle hash as the ETag when updating objects.
 \                          Include version id in object replace/change replies.
 \                          Change conflict (409) replies format to text.
@@ -763,14 +764,15 @@ X-Object-Version            The object's new version
 
 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.
 
-===========================  ==============================
-Return Code                  Description
-===========================  ==============================
-201 (Created)                The object has been created
-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)
-411 (Length Required)        Missing ``Content-Length`` or ``Content-Type`` in the request
-422 (Unprocessable Entity)   The MD5 checksum of the data written to the storage system does not match the (optionally) supplied ETag value
-===========================  ==============================
+==============================  ==============================
+Return Code                     Description
+==============================  ==============================
+201 (Created)                   The object has been created
+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)
+411 (Length Required)           Missing ``Content-Length`` or ``Content-Type`` in the request
+413 (Request Entity Too Large)  Insufficient quota to complete the request
+422 (Unprocessable Entity)      The MD5 checksum of the data written to the storage system does not match the (optionally) supplied ETag value
+==============================  ==============================
 
 
 COPY
@@ -803,12 +805,13 @@ X-Object-Version            The object's new version
 
 |
 
-===========================  ==============================
-Return Code                  Description
-===========================  ==============================
-201 (Created)                The object has been created
-409 (Conflict)               There are conflicting permissions (a list of conflicting sharing paths will be included in the reply - in simple text format)
-===========================  ==============================
+==============================  ==============================
+Return Code                     Description
+==============================  ==============================
+201 (Created)                   The object has been created
+409 (Conflict)                  There are conflicting permissions (a list of conflicting sharing paths will be included in the reply - in simple text format)
+413 (Request Entity Too Large)  Insufficient quota to complete the request
+==============================  ==============================
 
 
 MOVE
@@ -878,15 +881,16 @@ X-Object-Version            The object's new version
 
 |
 
-===========================  ==============================
-Return Code                  Description
-===========================  ==============================
-202 (Accepted)               The request has been accepted (not a data update)
-204 (No Content)             The request succeeded (data updated)
-409 (Conflict)               There are conflicting permissions (a list of conflicting sharing paths will be included in the reply - in simple text format)
-411 (Length Required)        Missing ``Content-Length`` in the request
-416 (Range Not Satisfiable)  The supplied range is invalid
-===========================  ==============================
+==============================  ==============================
+Return Code                     Description
+==============================  ==============================
+202 (Accepted)                  The request has been accepted (not a data update)
+204 (No Content)                The request succeeded (data updated)
+409 (Conflict)                  There are conflicting permissions (a list of conflicting sharing paths will be included in the reply - in simple text format)
+411 (Length Required)           Missing ``Content-Length`` in the request
+413 (Request Entity Too Large)  Insufficient quota to complete the request
+416 (Range Not Satisfiable)     The supplied range is invalid
+==============================  ==============================
 
 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. ::
 
@@ -906,11 +910,12 @@ X-Object-Version            The object's new version
 
 |
 
-===========================  ==============================
-Return Code                  Description
-===========================  ==============================
-201 (Created)                The object has been created
-===========================  ==============================
+==============================  ==============================
+Return Code                     Description
+==============================  ==============================
+201 (Created)                   The object has been created
+413 (Request Entity Too Large)  Insufficient quota to complete the request
+==============================  ==============================
 
 
 DELETE
index 8255772..2d21ed9 100644 (file)
@@ -43,7 +43,7 @@ from django.utils.encoding import smart_unicode, smart_str
 from xml.dom import minidom
 
 from pithos.api.faults import (Fault, NotModified, BadRequest, Unauthorized, ItemNotFound, Conflict,
-    LengthRequired, PreconditionFailed, RangeNotSatisfiable, UnprocessableEntity)
+    LengthRequired, PreconditionFailed, RequestEntityTooLarge, RangeNotSatisfiable, UnprocessableEntity)
 from pithos.api.util import (rename_meta_key, format_header_key, printable_header_dict, get_account_headers,
     put_account_headers, get_container_headers, put_container_headers, get_object_headers, put_object_headers,
     update_manifest_meta, update_sharing_meta, update_public_meta, validate_modification_preconditions,
@@ -51,7 +51,7 @@ from pithos.api.util import (rename_meta_key, format_header_key, printable_heade
     get_int_parameter, get_content_length, get_content_range, socket_read_iterator,
     object_data_response, put_object_block, hashmap_hash, api_method, json_encode_decimal)
 from pithos.backends import connect_backend
-from pithos.backends.base import NotAllowedError
+from pithos.backends.base import NotAllowedError, QuotaError
 
 
 logger = logging.getLogger(__name__)
@@ -838,6 +838,8 @@ def object_write(request, v_account, v_container, v_object):
         raise BadRequest('Invalid sharing header')
     except AttributeError, e:
         raise Conflict('\n'.join(e.data) + '\n')
+    except QuotaError:
+        raise RequestEntityTooLarge('Quota exceeded')
     if public is not None:
         try:
             request.backend.update_object_public(request.user, v_account,
@@ -884,6 +886,8 @@ def object_write_form(request, v_account, v_container, v_object):
         raise Unauthorized('Access denied')
     except NameError:
         raise ItemNotFound('Container does not exist')
+    except QuotaError:
+        raise RequestEntityTooLarge('Quota exceeded')
     
     response = HttpResponse(status=201)
     response['ETag'] = meta['hash']
@@ -1163,6 +1167,8 @@ def object_update(request, v_account, v_container, v_object):
         raise BadRequest('Invalid sharing header')
     except AttributeError, e:
         raise Conflict('\n'.join(e.data) + '\n')
+    except QuotaError:
+        raise RequestEntityTooLarge('Quota exceeded')
     if public is not None:
         try:
             request.backend.update_object_public(request.user, v_account,
index bff9fc3..09d09ca 100644 (file)
@@ -46,10 +46,10 @@ from django.utils.encoding import smart_str
 
 from pithos.api.compat import parse_http_date_safe, parse_http_date
 from pithos.api.faults import (Fault, NotModified, BadRequest, Unauthorized, ItemNotFound,
-                                Conflict, LengthRequired, PreconditionFailed, RangeNotSatisfiable,
-                                ServiceUnavailable)
+                                Conflict, LengthRequired, PreconditionFailed, RequestEntityTooLarge,
+                                RangeNotSatisfiable, ServiceUnavailable)
 from pithos.backends import connect_backend
-from pithos.backends.base import NotAllowedError
+from pithos.backends.base import NotAllowedError, QuotaError
 
 import logging
 import re
@@ -310,6 +310,8 @@ def copy_or_move_object(request, src_account, src_container, src_name, dest_acco
         raise BadRequest('Invalid sharing header')
     except AttributeError, e:
         raise Conflict('\n'.join(e.data) + '\n')
+    except QuotaError:
+        raise RequestEntityTooLarge('Quota exceeded')
     if public is not None:
         try:
             request.backend.update_object_public(request.user, dest_account, dest_container, dest_name, public)
index 3d76ab0..25e51ee 100644 (file)
@@ -34,6 +34,9 @@
 class NotAllowedError(Exception):
     pass
 
+class QuotaError(Exception):
+    pass
+
 class BaseBackend(object):
     """Abstract backend class that serves as a reference for actual implementations.
     
@@ -427,6 +430,8 @@ class BaseBackend(object):
             ValueError: Invalid users/groups in permissions
             
             AttributeError: Can not set permissions
+            
+            QuotaError: Account or container quota exceeded
         """
         return ''
     
@@ -452,6 +457,8 @@ class BaseBackend(object):
             ValueError: Invalid users/groups in permissions
             
             AttributeError: Can not set permissions
+            
+            QuotaError: Account or container quota exceeded
         """
         return ''
     
@@ -473,6 +480,8 @@ class BaseBackend(object):
             ValueError: Invalid users/groups in permissions
             
             AttributeError: Can not set permissions
+            
+            QuotaError: Account or container quota exceeded
         """
         return ''
     
index 3a0d57b..8d8652e 100644 (file)
@@ -38,7 +38,7 @@ import logging
 import hashlib
 import binascii
 
-from base import NotAllowedError, BaseBackend
+from base import NotAllowedError, QuotaError, BaseBackend
 from lib.hashfiler import Mapper, Blocker
 
 ( CLUSTER_NORMAL, CLUSTER_HISTORY, CLUSTER_DELETED ) = range(3)
@@ -546,7 +546,7 @@ class ModularBackend(BaseBackend):
             if (account_quota > 0 and self._get_statistics(account_node)[1] + size_delta > account_quota) or \
                (container_quota > 0 and self._get_statistics(container_node)[1] + size_delta > container_quota):
                 # This must be executed in a transaction, so the version is never created if it fails.
-                raise
+                raise QuotaError
         
         if not replace_meta and src_version_id is not None:
             self.node.attribute_copy(src_version_id, dest_version_id)