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 AccountExists(NameError):
48 class ContainerExists(NameError):
51 class AccountNotEmpty(IndexError):
54 class ContainerNotEmpty(IndexError):
57 class ItemNotExists(NameError):
60 class VersionNotExists(IndexError):
63 class BaseBackend(object):
64 """Abstract backend class that serves as a reference for actual implementations.
66 The purpose of the backend is to provide the necessary functions for handling data
67 and metadata. It is responsible for the actual storage and retrieval of information.
69 Note that the account level is always valid as it is checked from another subsystem.
71 When not replacing metadata/groups/policy, keys with empty values should be deleted.
73 The following variables should be available:
74 'hash_algorithm': Suggested is 'sha256'
76 'block_size': Suggested is 4MB
78 'default_policy': A dictionary with default policy settings
82 """Close the backend connection."""
85 def list_accounts(self, user, marker=None, limit=10000):
86 """Return a list of accounts the user can access.
89 'marker': Start list from the next item after 'marker'
91 'limit': Number of containers to return
95 def get_account_meta(self, user, account, domain, until=None, include_user_defined=True):
96 """Return a dictionary with the account metadata for the domain.
98 The keys returned are all user-defined, except:
99 'name': The account name
101 'count': The number of containers (or 0)
103 'bytes': The total data size (or 0)
105 'modified': Last modification timestamp (overall)
107 'until_timestamp': Last modification until the timestamp provided
110 NotAllowedError: Operation not permitted
114 def update_account_meta(self, user, account, domain, meta, replace=False):
115 """Update the metadata associated with the account for the domain.
118 'domain': Metadata domain
120 'meta': Dictionary with metadata to update
122 'replace': Replace instead of update
125 NotAllowedError: Operation not permitted
129 def get_account_groups(self, user, account):
130 """Return a dictionary with the user groups defined for this account.
133 NotAllowedError: Operation not permitted
137 def update_account_groups(self, user, account, groups, replace=False):
138 """Update the groups associated with the account.
141 NotAllowedError: Operation not permitted
143 ValueError: Invalid data in groups
147 def get_account_policy(self, user, account):
148 """Return a dictionary with the account policy.
150 The keys returned are:
151 'quota': The maximum bytes allowed (default is 0 - unlimited)
153 'versioning': Can be 'auto', 'manual' or 'none' (default is 'manual')
156 NotAllowedError: Operation not permitted
160 def update_account_policy(self, user, account, policy, replace=False):
161 """Update the policy associated with the account.
164 NotAllowedError: Operation not permitted
166 ValueError: Invalid policy defined
170 def put_account(self, user, account, policy={}):
171 """Create a new account with the given name.
174 NotAllowedError: Operation not permitted
176 ValueError: Invalid policy defined
180 def delete_account(self, user, account):
181 """Delete the account with the given name.
184 NotAllowedError: Operation not permitted
186 AccountNotEmpty: Account is not empty
190 def list_containers(self, user, account, marker=None, limit=10000, shared=False, until=None, public=False):
191 """Return a list of container names existing under an account.
194 'marker': Start list from the next item after 'marker'
196 'limit': Number of containers to return
198 'shared': Only list containers with permissions set
200 'public': Only list containers containing public objects
204 NotAllowedError: Operation not permitted
208 def list_container_meta(self, user, account, container, domain, until=None):
209 """Return a list with all the container's object meta keys for the domain.
212 NotAllowedError: Operation not permitted
214 ItemNotExists: Container does not exist
218 def get_container_meta(self, user, account, container, domain, until=None, include_user_defined=True):
219 """Return a dictionary with the container metadata for the domain.
221 The keys returned are all user-defined, except:
222 'name': The container name
224 'count': The number of objects
226 'bytes': The total data size
228 'modified': Last modification timestamp (overall)
230 'until_timestamp': Last modification until the timestamp provided
233 NotAllowedError: Operation not permitted
235 ItemNotExists: Container does not exist
239 def update_container_meta(self, user, account, container, domain, meta, replace=False):
240 """Update the metadata associated with the container for the domain.
243 'domain': Metadata domain
245 'meta': Dictionary with metadata to update
247 'replace': Replace instead of update
250 NotAllowedError: Operation not permitted
252 ItemNotExists: Container does not exist
256 def get_container_policy(self, user, account, container):
257 """Return a dictionary with the container policy.
259 The keys returned are:
260 'quota': The maximum bytes allowed (default is 0 - unlimited)
262 'versioning': Can be 'auto', 'manual' or 'none' (default is 'manual')
265 NotAllowedError: Operation not permitted
267 ItemNotExists: Container does not exist
271 def update_container_policy(self, user, account, container, policy, replace=False):
272 """Update the policy associated with the container.
275 NotAllowedError: Operation not permitted
277 ItemNotExists: Container does not exist
279 ValueError: Invalid policy defined
283 def put_container(self, user, account, container, policy={}, delimiter=None):
284 """Create a new container with the given name.
287 'delimiter': If present deletes container contents instead of the container
290 NotAllowedError: Operation not permitted
292 ContainerExists: Container already exists
294 ValueError: Invalid policy defined
298 def delete_container(self, user, account, container, until=None):
299 """Delete/purge the container with the given name.
302 NotAllowedError: Operation not permitted
304 ItemNotExists: Container does not exist
306 ContainerNotEmpty: Container is not empty
310 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, public=False):
311 """Return a list of object (name, version_id) tuples existing under a container.
314 'prefix': List objects starting with 'prefix'
316 'delimiter': Return unique names before 'delimiter' and after 'prefix'
318 'marker': Start list from the next item after 'marker'
320 'limit': Number of objects to return
322 'virtual': If not set, the result will only include names starting
323 with 'prefix' and ending without a 'delimiter' or with
324 the first occurance of the 'delimiter' after 'prefix'.
325 If set, the result will include all names after 'prefix',
326 up to and including the 'delimiter' if it is found
328 'domain': Metadata domain for keys
330 'keys': Include objects that satisfy the key queries in the list.
331 Use 'key', '!key' for existence queries, 'key op value' for
332 value queries, where 'op' can be one of =, !=, <=, >=, <, >
334 'shared': Only list objects with permissions set
336 'size_range': Include objects with byte size in (from, to).
337 Use None to specify unlimited
339 'public': Only list public objects
343 NotAllowedError: Operation not permitted
345 ItemNotExists: Container does not exist
349 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):
350 """Return a list of object metadata dicts existing under a container.
352 Same parameters with list_objects. Returned dicts have no user-defined
353 metadata and, if until is not None, a None 'modified' timestamp.
356 NotAllowedError: Operation not permitted
358 ItemNotExists: Container does not exist
362 def list_object_permissions(self, user, account, container, prefix=''):
363 """Return a list of paths that enforce permissions under a container.
366 NotAllowedError: Operation not permitted
370 def list_object_public(self, user, account, container, prefix=''):
371 """Return a dict mapping paths to public ids for objects that are public under a container."""
374 def get_object_meta(self, user, account, container, name, domain, version=None, include_user_defined=True):
375 """Return a dictionary with the object metadata for the domain.
377 The keys returned are all user-defined, except:
378 'name': The object name
380 'bytes': The total data size
382 'type': The content type
384 'hash': The hashmap hash
386 'modified': Last modification timestamp (overall)
388 'modified_by': The user that committed the object (version requested)
390 'version': The version identifier
392 'version_timestamp': The version's modification timestamp
394 'uuid': A unique identifier that persists data or metadata updates and renames
396 'checksum': The MD5 sum of the object (may be empty)
399 NotAllowedError: Operation not permitted
401 ItemNotExists: Container/object does not exist
403 VersionNotExists: Version does not exist
407 def update_object_meta(self, user, account, container, name, domain, meta, replace=False):
408 """Update the metadata associated with the object for the domain and return the new version.
411 'domain': Metadata domain
413 'meta': Dictionary with metadata to update
415 'replace': Replace instead of update
418 NotAllowedError: Operation not permitted
420 ItemNotExists: Container/object does not exist
424 def get_object_permissions(self, user, account, container, name):
425 """Return the action allowed on the object, the path
426 from which the object gets its permissions from,
427 along with a dictionary containing the permissions.
429 The dictionary keys are (also used for defining the action):
430 'read': The object is readable by the users/groups in the list
432 'write': The object is writable by the users/groups in the list
435 NotAllowedError: Operation not permitted
437 ItemNotExists: Container/object does not exist
441 def update_object_permissions(self, user, account, container, name, permissions):
442 """Update (set) the permissions associated with the object.
445 'permissions': Dictionary with permissions to set
448 NotAllowedError: Operation not permitted
450 ItemNotExists: Container/object does not exist
452 ValueError: Invalid users/groups in permissions
456 def get_object_public(self, user, account, container, name):
457 """Return the public id of the object if applicable.
460 NotAllowedError: Operation not permitted
462 ItemNotExists: Container/object does not exist
466 def update_object_public(self, user, account, container, name, public):
467 """Update the public status of the object.
470 'public': Boolean value
473 NotAllowedError: Operation not permitted
475 ItemNotExists: Container/object does not exist
479 def get_object_hashmap(self, user, account, container, name, version=None):
480 """Return the object's size and a list with partial hashes.
483 NotAllowedError: Operation not permitted
485 ItemNotExists: Container/object does not exist
487 VersionNotExists: Version does not exist
491 def update_object_hashmap(self, user, account, container, name, size, type, hashmap, checksum, domain, meta={}, replace_meta=False, permissions=None):
492 """Create/update an object with the specified size and partial hashes and return the new version.
495 'domain': Metadata domain
497 'meta': Dictionary with metadata to change
499 'replace_meta': Replace metadata instead of update
501 'permissions': Updated object permissions
504 NotAllowedError: Operation not permitted
506 ItemNotExists: Container does not exist
508 ValueError: Invalid users/groups in permissions
510 QuotaError: Account or container quota exceeded
514 def update_object_checksum(self, user, account, container, name, version, checksum):
515 """Update an object's checksum."""
518 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, delimiter=None):
519 """Copy an object's data and metadata and return the new version.
522 'domain': Metadata domain
524 'meta': Dictionary with metadata to change from source to destination
526 'replace_meta': Replace metadata instead of update
528 'permissions': New object permissions
530 'src_version': Copy from the version provided
532 'delimiter': Copy objects whose path starts with src_name + delimiter
535 NotAllowedError: Operation not permitted
537 ItemNotExists: Container/object does not exist
539 VersionNotExists: Version does not exist
541 ValueError: Invalid users/groups in permissions
543 QuotaError: Account or container quota exceeded
547 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, delimiter=None):
548 """Move an object's data and metadata and return the new version.
551 'domain': Metadata domain
553 'meta': Dictionary with metadata to change from source to destination
555 'replace_meta': Replace metadata instead of update
557 'permissions': New object permissions
559 'delimiter': Move objects whose path starts with src_name + delimiter
562 NotAllowedError: Operation not permitted
564 ItemNotExists: Container/object does not exist
566 ValueError: Invalid users/groups in permissions
568 QuotaError: Account or container quota exceeded
572 def delete_object(self, user, account, container, name, until=None, delimiter=None):
573 """Delete/purge an object.
576 'delimiter': Delete objects whose path starting with name + delimiter
579 NotAllowedError: Operation not permitted
581 ItemNotExists: Container/object does not exist
585 def list_versions(self, user, account, container, name):
586 """Return a list of all (version, version_timestamp) tuples for an object.
589 NotAllowedError: Operation not permitted
593 def get_uuid(self, user, uuid):
594 """Return the (account, container, name) for the UUID given.
597 NotAllowedError: Operation not permitted
599 NameError: UUID does not exist
603 def get_public(self, user, public):
604 """Return the (account, container, name) for the public id given.
607 NotAllowedError: Operation not permitted
609 NameError: Public id does not exist
613 def get_block(self, hash):
614 """Return a block's data.
617 ItemNotExists: Block does not exist
621 def put_block(self, data):
622 """Store a block and return the hash."""
625 def update_block(self, hash, data, offset=0):
626 """Update a known block and return the hash.
629 IndexError: Offset or data outside block limits