1 # Copyright 2011 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 class NotAllowedError(Exception):
37 class QuotaError(Exception):
40 class BaseBackend(object):
41 """Abstract backend class that serves as a reference for actual implementations.
43 The purpose of the backend is to provide the necessary functions for handling data
44 and metadata. It is responsible for the actual storage and retrieval of information.
46 Note that the account level is always valid as it is checked from another subsystem.
48 When not replacing metadata/groups/policy, keys with empty values should be deleted.
50 The following variables should be available:
51 'hash_algorithm': Suggested is 'sha256'
53 'block_size': Suggested is 4MB
55 'default_policy': A dictionary with default policy settings
58 def __init__(self, db_module, db_connection, block_module, block_path):
59 """Initialize backend with supplied modules and options."""
63 """Close the backend connection."""
66 def list_accounts(self, user, marker=None, limit=10000):
67 """Return a list of accounts the user can access.
70 'marker': Start list from the next item after 'marker'
72 'limit': Number of containers to return
76 def get_account_meta(self, user, account, domain, until=None):
77 """Return a dictionary with the account metadata for the domain.
79 The keys returned are all user-defined, except:
80 'name': The account name
82 'count': The number of containers (or 0)
84 'bytes': The total data size (or 0)
86 'modified': Last modification timestamp (overall)
88 'until_timestamp': Last modification until the timestamp provided
91 NotAllowedError: Operation not permitted
95 def update_account_meta(self, user, account, domain, meta, replace=False):
96 """Update the metadata associated with the account for the domain.
99 'domain': Metadata domain
101 'meta': Dictionary with metadata to update
103 'replace': Replace instead of update
106 NotAllowedError: Operation not permitted
110 def get_account_groups(self, user, account):
111 """Return a dictionary with the user groups defined for this account.
114 NotAllowedError: Operation not permitted
118 def update_account_groups(self, user, account, groups, replace=False):
119 """Update the groups associated with the account.
122 NotAllowedError: Operation not permitted
124 ValueError: Invalid data in groups
128 def get_account_policy(self, user, account):
129 """Return a dictionary with the account policy.
131 The keys returned are:
132 'quota': The maximum bytes allowed (default is 0 - unlimited)
134 'versioning': Can be 'auto', 'manual' or 'none' (default is 'manual')
137 NotAllowedError: Operation not permitted
141 def update_account_policy(self, user, account, policy, replace=False):
142 """Update the policy associated with the account.
145 NotAllowedError: Operation not permitted
147 ValueError: Invalid policy defined
151 def put_account(self, user, account, policy={}):
152 """Create a new account with the given name.
155 NotAllowedError: Operation not permitted
157 ValueError: Invalid policy defined
161 def delete_account(self, user, account):
162 """Delete the account with the given name.
165 NotAllowedError: Operation not permitted
167 IndexError: Account is not empty
171 def list_containers(self, user, account, marker=None, limit=10000, shared=False, until=None):
172 """Return a list of container names existing under an account.
175 'marker': Start list from the next item after 'marker'
177 'limit': Number of containers to return
179 'shared': Only list containers with permissions set
183 NotAllowedError: Operation not permitted
187 def get_container_meta(self, user, account, container, domain, until=None):
188 """Return a dictionary with the container metadata for the domain.
190 The keys returned are all user-defined, except:
191 'name': The container name
193 'count': The number of objects
195 'bytes': The total data size
197 'modified': Last modification timestamp (overall)
199 'until_timestamp': Last modification until the timestamp provided
202 NotAllowedError: Operation not permitted
204 NameError: Container does not exist
208 def update_container_meta(self, user, account, container, domain, meta, replace=False):
209 """Update the metadata associated with the container for the domain.
212 'domain': Metadata domain
214 'meta': Dictionary with metadata to update
216 'replace': Replace instead of update
219 NotAllowedError: Operation not permitted
221 NameError: Container does not exist
225 def get_container_policy(self, user, account, container):
226 """Return a dictionary with the container policy.
228 The keys returned are:
229 'quota': The maximum bytes allowed (default is 0 - unlimited)
231 'versioning': Can be 'auto', 'manual' or 'none' (default is 'manual')
234 NotAllowedError: Operation not permitted
236 NameError: Container does not exist
240 def update_container_policy(self, user, account, container, policy, replace=False):
241 """Update the policy associated with the container.
244 NotAllowedError: Operation not permitted
246 NameError: Container does not exist
248 ValueError: Invalid policy defined
252 def put_container(self, user, account, container, policy={}):
253 """Create a new container with the given name.
256 NotAllowedError: Operation not permitted
258 NameError: Container already exists
260 ValueError: Invalid policy defined
264 def delete_container(self, user, account, container, until=None):
265 """Delete/purge the container with the given name.
268 NotAllowedError: Operation not permitted
270 NameError: Container does not exist
272 IndexError: Container is not empty
276 def list_objects(self, user, account, container, prefix='', delimiter=None, marker=None, limit=10000, virtual=True, domain=None, keys=[], shared=False, until=None):
277 """Return a list of object (name, version_id) tuples existing under a container.
280 'prefix': List objects starting with 'prefix'
282 'delimiter': Return unique names before 'delimiter' and after 'prefix'
284 'marker': Start list from the next item after 'marker'
286 'limit': Number of objects to return
288 'virtual': If not set, the result will only include names starting
289 with 'prefix' and ending without a 'delimiter' or with
290 the first occurance of the 'delimiter' after 'prefix'.
291 If set, the result will include all names after 'prefix',
292 up to and including the 'delimiter' if it is found
294 'domain': Metadata domain for keys
296 'keys': Include objects that satisfy the key queries in the list.
297 Use 'key', '!key' for existence queries, 'key op value' for
298 value queries, where 'op' can be one of =, !=, <=, >=, <, >
300 'shared': Only list objects with permissions set
303 NotAllowedError: Operation not permitted
305 NameError: Container does not exist
309 def list_object_meta(self, user, account, container, domain, until=None):
310 """Return a list with all the container's object meta keys for the domain.
313 NotAllowedError: Operation not permitted
315 NameError: Container does not exist
319 def get_object_meta(self, user, account, container, name, domain, version=None):
320 """Return a dictionary with the object metadata for the domain.
322 The keys returned are all user-defined, except:
323 'name': The object name
325 'bytes': The total data size
327 'hash': The hashmap hash
329 'modified': Last modification timestamp (overall)
331 'modified_by': The user that committed the object (version requested)
333 'version': The version identifier
335 'version_timestamp': The version's modification timestamp
337 'uuid': A unique identifier that persists data or metadata updates and renames
340 NotAllowedError: Operation not permitted
342 NameError: Container/object does not exist
344 IndexError: Version does not exist
348 def update_object_meta(self, user, account, container, name, domain, meta, replace=False):
349 """Update the metadata associated with the object for the domain and return the new version.
352 'domain': Metadata domain
354 'meta': Dictionary with metadata to update
356 'replace': Replace instead of update
359 NotAllowedError: Operation not permitted
361 NameError: Container/object does not exist
365 def get_object_permissions(self, user, account, container, name):
366 """Return the action allowed on the object, the path
367 from which the object gets its permissions from,
368 along with a dictionary containing the permissions.
370 The dictionary keys are (also used for defining the action):
371 'read': The object is readable by the users/groups in the list
373 'write': The object is writable by the users/groups in the list
376 NotAllowedError: Operation not permitted
378 NameError: Container/object does not exist
382 def update_object_permissions(self, user, account, container, name, permissions):
383 """Update the permissions associated with the object.
386 'permissions': Dictionary with permissions to update
389 NotAllowedError: Operation not permitted
391 NameError: Container/object does not exist
393 ValueError: Invalid users/groups in permissions
395 AttributeError: Can not set permissions, as this object
396 is already shared/private by another object higher
397 in the hierarchy, or setting permissions here will
398 invalidate other permissions deeper in the hierarchy
402 def get_object_public(self, user, account, container, name):
403 """Return the public id of the object if applicable.
406 NotAllowedError: Operation not permitted
408 NameError: Container/object does not exist
412 def update_object_public(self, user, account, container, name, public):
413 """Update the public status of the object.
416 'public': Boolean value
419 NotAllowedError: Operation not permitted
421 NameError: Container/object does not exist
425 def get_object_hashmap(self, user, account, container, name, version=None):
426 """Return the object's size and a list with partial hashes.
429 NotAllowedError: Operation not permitted
431 NameError: Container/object does not exist
433 IndexError: Version does not exist
437 def update_object_hashmap(self, user, account, container, name, size, hashmap, domain, meta={}, replace_meta=False, permissions=None):
438 """Create/update an object with the specified size and partial hashes and return the new version.
441 'domain': Metadata domain
443 'meta': Dictionary with metadata to change
445 'replace_meta': Replace metadata instead of update
447 'permissions': Updated object permissions
450 NotAllowedError: Operation not permitted
452 NameError: Container does not exist
454 ValueError: Invalid users/groups in permissions
456 AttributeError: Can not set permissions
458 QuotaError: Account or container quota exceeded
462 def copy_object(self, user, src_account, src_container, src_name, dest_account, dest_container, dest_name, domain, meta={}, replace_meta=False, permissions=None, src_version=None):
463 """Copy an object's data and metadata and return the new version.
466 'domain': Metadata domain
468 'meta': Dictionary with metadata to change from source to destination
470 'replace_meta': Replace metadata instead of update
472 'permissions': New object permissions
474 'src_version': Copy from the version provided
477 NotAllowedError: Operation not permitted
479 NameError: Container/object does not exist
481 IndexError: Version does not exist
483 ValueError: Invalid users/groups in permissions
485 AttributeError: Can not set permissions
487 QuotaError: Account or container quota exceeded
491 def move_object(self, user, src_account, src_container, src_name, dest_account, dest_container, dest_name, domain, meta={}, replace_meta=False, permissions=None):
492 """Move an object's data and metadata and return the new version.
495 'domain': Metadata domain
497 'meta': Dictionary with metadata to change from source to destination
499 'replace_meta': Replace metadata instead of update
501 'permissions': New object permissions
504 NotAllowedError: Operation not permitted
506 NameError: Container/object does not exist
508 ValueError: Invalid users/groups in permissions
510 AttributeError: Can not set permissions
512 QuotaError: Account or container quota exceeded
516 def delete_object(self, user, account, container, name, until=None):
517 """Delete/purge an object.
520 NotAllowedError: Operation not permitted
522 NameError: Container/object does not exist
526 def list_versions(self, user, account, container, name):
527 """Return a list of all (version, version_timestamp) tuples for an object.
530 NotAllowedError: Operation not permitted
534 def get_uuid(self, user, uuid):
535 """Return the (account, container, name) for the UUID given.
538 NotAllowedError: Operation not permitted
540 NameError: UUID does not exist
544 def get_public(self, user, public):
545 """Return the (account, container, name) for the public id given.
548 NotAllowedError: Operation not permitted
550 NameError: Public id does not exist
554 def get_block(self, hash):
555 """Return a block's data.
558 NameError: Block does not exist
562 def put_block(self, data):
563 """Store a block and return the hash."""
566 def update_block(self, hash, data, offset=0):
567 """Update a known block and return the hash.
570 IndexError: Offset or data outside block limits