1 # Copyright 2011-2012 GRNET S.A. All rights reserved.
3 # Redistribution and use in source and binary forms, with or
4 # without modification, are permitted provided that the following
7 # 1. Redistributions of source code must retain the above
8 # copyright notice, this list of conditions and the following
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.
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.
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.
34 # Default setting for new accounts.
35 DEFAULT_QUOTA = 0 # No quota.
36 DEFAULT_VERSIONING = 'auto'
39 class NotAllowedError(Exception):
42 class QuotaError(Exception):
45 class BaseBackend(object):
46 """Abstract backend class that serves as a reference for actual implementations.
48 The purpose of the backend is to provide the necessary functions for handling data
49 and metadata. It is responsible for the actual storage and retrieval of information.
51 Note that the account level is always valid as it is checked from another subsystem.
53 When not replacing metadata/groups/policy, keys with empty values should be deleted.
55 The following variables should be available:
56 'hash_algorithm': Suggested is 'sha256'
58 'block_size': Suggested is 4MB
60 'default_policy': A dictionary with default policy settings
64 """Close the backend connection."""
67 def list_accounts(self, user, marker=None, limit=10000):
68 """Return a list of accounts the user can access.
71 'marker': Start list from the next item after 'marker'
73 'limit': Number of containers to return
77 def get_account_meta(self, user, account, domain, until=None, include_user_defined=True):
78 """Return a dictionary with the account metadata for the domain.
80 The keys returned are all user-defined, except:
81 'name': The account name
83 'count': The number of containers (or 0)
85 'bytes': The total data size (or 0)
87 'modified': Last modification timestamp (overall)
89 'until_timestamp': Last modification until the timestamp provided
92 NotAllowedError: Operation not permitted
96 def update_account_meta(self, user, account, domain, meta, replace=False):
97 """Update the metadata associated with the account for the domain.
100 'domain': Metadata domain
102 'meta': Dictionary with metadata to update
104 'replace': Replace instead of update
107 NotAllowedError: Operation not permitted
111 def get_account_groups(self, user, account):
112 """Return a dictionary with the user groups defined for this account.
115 NotAllowedError: Operation not permitted
119 def update_account_groups(self, user, account, groups, replace=False):
120 """Update the groups associated with the account.
123 NotAllowedError: Operation not permitted
125 ValueError: Invalid data in groups
129 def get_account_policy(self, user, account):
130 """Return a dictionary with the account policy.
132 The keys returned are:
133 'quota': The maximum bytes allowed (default is 0 - unlimited)
135 'versioning': Can be 'auto', 'manual' or 'none' (default is 'manual')
138 NotAllowedError: Operation not permitted
142 def update_account_policy(self, user, account, policy, replace=False):
143 """Update the policy associated with the account.
146 NotAllowedError: Operation not permitted
148 ValueError: Invalid policy defined
152 def put_account(self, user, account, policy={}):
153 """Create a new account with the given name.
156 NotAllowedError: Operation not permitted
158 ValueError: Invalid policy defined
162 def delete_account(self, user, account):
163 """Delete the account with the given name.
166 NotAllowedError: Operation not permitted
168 IndexError: Account is not empty
172 def list_containers(self, user, account, marker=None, limit=10000, shared=False, until=None):
173 """Return a list of container names existing under an account.
176 'marker': Start list from the next item after 'marker'
178 'limit': Number of containers to return
180 'shared': Only list containers with permissions set
184 NotAllowedError: Operation not permitted
188 def list_container_meta(self, user, account, container, domain, until=None):
189 """Return a list with all the container's object meta keys for the domain.
192 NotAllowedError: Operation not permitted
194 NameError: Container does not exist
198 def get_container_meta(self, user, account, container, domain, until=None, include_user_defined=True):
199 """Return a dictionary with the container metadata for the domain.
201 The keys returned are all user-defined, except:
202 'name': The container name
204 'count': The number of objects
206 'bytes': The total data size
208 'modified': Last modification timestamp (overall)
210 'until_timestamp': Last modification until the timestamp provided
213 NotAllowedError: Operation not permitted
215 NameError: Container does not exist
219 def update_container_meta(self, user, account, container, domain, meta, replace=False):
220 """Update the metadata associated with the container for the domain.
223 'domain': Metadata domain
225 'meta': Dictionary with metadata to update
227 'replace': Replace instead of update
230 NotAllowedError: Operation not permitted
232 NameError: Container does not exist
236 def get_container_policy(self, user, account, container):
237 """Return a dictionary with the container policy.
239 The keys returned are:
240 'quota': The maximum bytes allowed (default is 0 - unlimited)
242 'versioning': Can be 'auto', 'manual' or 'none' (default is 'manual')
245 NotAllowedError: Operation not permitted
247 NameError: Container does not exist
251 def update_container_policy(self, user, account, container, policy, replace=False):
252 """Update the policy associated with the container.
255 NotAllowedError: Operation not permitted
257 NameError: Container does not exist
259 ValueError: Invalid policy defined
263 def put_container(self, user, account, container, policy={}):
264 """Create a new container with the given name.
267 NotAllowedError: Operation not permitted
269 NameError: Container already exists
271 ValueError: Invalid policy defined
275 def delete_container(self, user, account, container, until=None):
276 """Delete/purge the container with the given name.
279 NotAllowedError: Operation not permitted
281 NameError: Container does not exist
283 IndexError: Container is not empty
287 def list_objects(self, user, account, container, prefix='', delimiter=None, marker=None, limit=10000, virtual=True, domain=None, keys=[], shared=False, until=None, size_range=None):
288 """Return a list of object (name, version_id) tuples existing under a container.
291 'prefix': List objects starting with 'prefix'
293 'delimiter': Return unique names before 'delimiter' and after 'prefix'
295 'marker': Start list from the next item after 'marker'
297 'limit': Number of objects to return
299 'virtual': If not set, the result will only include names starting
300 with 'prefix' and ending without a 'delimiter' or with
301 the first occurance of the 'delimiter' after 'prefix'.
302 If set, the result will include all names after 'prefix',
303 up to and including the 'delimiter' if it is found
305 'domain': Metadata domain for keys
307 'keys': Include objects that satisfy the key queries in the list.
308 Use 'key', '!key' for existence queries, 'key op value' for
309 value queries, where 'op' can be one of =, !=, <=, >=, <, >
311 'shared': Only list objects with permissions set
313 'size_range': Include objects with byte size in (from, to).
314 Use None to specify unlimited
317 NotAllowedError: Operation not permitted
319 NameError: Container does not exist
323 def list_object_meta(self, user, account, container, prefix='', delimiter=None, marker=None, limit=10000, virtual=True, domain=None, keys=[], shared=False, until=None, size_range=None):
324 """Return a list of object metadata dicts existing under a container.
326 Same parameters with list_objects. Returned dicts have no user-defined
327 metadata and, if until is not None, a None 'modified' timestamp.
330 NotAllowedError: Operation not permitted
332 NameError: Container does not exist
336 def list_object_permissions(self, user, account, container, prefix=''):
337 """Return a list of paths that enforce permissions under a container.
340 NotAllowedError: Operation not permitted
344 def list_object_public(self, user, account, container, prefix=''):
345 """Return a dict mapping paths to public ids for objects that are public under a container."""
348 def get_object_meta(self, user, account, container, name, domain, version=None, include_user_defined=True):
349 """Return a dictionary with the object metadata for the domain.
351 The keys returned are all user-defined, except:
352 'name': The object name
354 'bytes': The total data size
356 'type': The content type
358 'hash': The hashmap hash
360 'modified': Last modification timestamp (overall)
362 'modified_by': The user that committed the object (version requested)
364 'version': The version identifier
366 'version_timestamp': The version's modification timestamp
368 'uuid': A unique identifier that persists data or metadata updates and renames
370 'checksum': The MD5 sum of the object (may be empty)
373 NotAllowedError: Operation not permitted
375 NameError: Container/object does not exist
377 IndexError: Version does not exist
381 def update_object_meta(self, user, account, container, name, domain, meta, replace=False):
382 """Update the metadata associated with the object for the domain and return the new version.
385 'domain': Metadata domain
387 'meta': Dictionary with metadata to update
389 'replace': Replace instead of update
392 NotAllowedError: Operation not permitted
394 NameError: Container/object does not exist
398 def get_object_permissions(self, user, account, container, name):
399 """Return the action allowed on the object, the path
400 from which the object gets its permissions from,
401 along with a dictionary containing the permissions.
403 The dictionary keys are (also used for defining the action):
404 'read': The object is readable by the users/groups in the list
406 'write': The object is writable by the users/groups in the list
409 NotAllowedError: Operation not permitted
411 NameError: Container/object does not exist
415 def update_object_permissions(self, user, account, container, name, permissions):
416 """Update (set) the permissions associated with the object.
419 'permissions': Dictionary with permissions to set
422 NotAllowedError: Operation not permitted
424 NameError: Container/object does not exist
426 ValueError: Invalid users/groups in permissions
430 def get_object_public(self, user, account, container, name):
431 """Return the public id of the object if applicable.
434 NotAllowedError: Operation not permitted
436 NameError: Container/object does not exist
440 def update_object_public(self, user, account, container, name, public):
441 """Update the public status of the object.
444 'public': Boolean value
447 NotAllowedError: Operation not permitted
449 NameError: Container/object does not exist
453 def get_object_hashmap(self, user, account, container, name, version=None):
454 """Return the object's size and a list with partial hashes.
457 NotAllowedError: Operation not permitted
459 NameError: Container/object does not exist
461 IndexError: Version does not exist
465 def update_object_hashmap(self, user, account, container, name, size, type, hashmap, checksum, domain, meta={}, replace_meta=False, permissions=None):
466 """Create/update an object with the specified size and partial hashes and return the new version.
469 'domain': Metadata domain
471 'meta': Dictionary with metadata to change
473 'replace_meta': Replace metadata instead of update
475 'permissions': Updated object permissions
478 NotAllowedError: Operation not permitted
480 NameError: Container does not exist
482 ValueError: Invalid users/groups in permissions
484 QuotaError: Account or container quota exceeded
488 def update_object_checksum(self, user, account, container, name, version, checksum):
489 """Update an object's checksum."""
492 def copy_object(self, user, src_account, src_container, src_name, dest_account, dest_container, dest_name, type, domain, meta={}, replace_meta=False, permissions=None, src_version=None):
493 """Copy an object's data and metadata and return the new version.
496 'domain': Metadata domain
498 'meta': Dictionary with metadata to change from source to destination
500 'replace_meta': Replace metadata instead of update
502 'permissions': New object permissions
504 'src_version': Copy from the version provided
507 NotAllowedError: Operation not permitted
509 NameError: Container/object does not exist
511 IndexError: Version does not exist
513 ValueError: Invalid users/groups in permissions
515 QuotaError: Account or container quota exceeded
519 def move_object(self, user, src_account, src_container, src_name, dest_account, dest_container, dest_name, type, domain, meta={}, replace_meta=False, permissions=None):
520 """Move an object's data and metadata and return the new version.
523 'domain': Metadata domain
525 'meta': Dictionary with metadata to change from source to destination
527 'replace_meta': Replace metadata instead of update
529 'permissions': New object permissions
532 NotAllowedError: Operation not permitted
534 NameError: Container/object does not exist
536 ValueError: Invalid users/groups in permissions
538 QuotaError: Account or container quota exceeded
542 def delete_object(self, user, account, container, name, until=None):
543 """Delete/purge an object.
546 NotAllowedError: Operation not permitted
548 NameError: Container/object does not exist
552 def list_versions(self, user, account, container, name):
553 """Return a list of all (version, version_timestamp) tuples for an object.
556 NotAllowedError: Operation not permitted
560 def get_uuid(self, user, uuid):
561 """Return the (account, container, name) for the UUID given.
564 NotAllowedError: Operation not permitted
566 NameError: UUID does not exist
570 def get_public(self, user, public):
571 """Return the (account, container, name) for the public id given.
574 NotAllowedError: Operation not permitted
576 NameError: Public id does not exist
580 def get_block(self, hash):
581 """Return a block's data.
584 NameError: Block does not exist
588 def put_block(self, data):
589 """Store a block and return the hash."""
592 def update_block(self, hash, data, offset=0):
593 """Update a known block and return the hash.
596 IndexError: Offset or data outside block limits