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={}):
284 """Create a new container with the given name.
287 NotAllowedError: Operation not permitted
289 ContainerExists: Container already exists
291 ValueError: Invalid policy defined
295 def delete_container(self, user, account, container, until=None):
296 """Delete/purge the container with the given name.
299 NotAllowedError: Operation not permitted
301 ItemNotExists: Container does not exist
303 ContainerNotEmpty: Container is not empty
307 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):
308 """Return a list of object (name, version_id) tuples existing under a container.
311 'prefix': List objects starting with 'prefix'
313 'delimiter': Return unique names before 'delimiter' and after 'prefix'
315 'marker': Start list from the next item after 'marker'
317 'limit': Number of objects to return
319 'virtual': If not set, the result will only include names starting
320 with 'prefix' and ending without a 'delimiter' or with
321 the first occurance of the 'delimiter' after 'prefix'.
322 If set, the result will include all names after 'prefix',
323 up to and including the 'delimiter' if it is found
325 'domain': Metadata domain for keys
327 'keys': Include objects that satisfy the key queries in the list.
328 Use 'key', '!key' for existence queries, 'key op value' for
329 value queries, where 'op' can be one of =, !=, <=, >=, <, >
331 'shared': Only list objects with permissions set
333 'size_range': Include objects with byte size in (from, to).
334 Use None to specify unlimited
336 'public': Only list public objects
340 NotAllowedError: Operation not permitted
342 ItemNotExists: Container does not exist
346 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):
347 """Return a list of object metadata dicts existing under a container.
349 Same parameters with list_objects. Returned dicts have no user-defined
350 metadata and, if until is not None, a None 'modified' timestamp.
353 NotAllowedError: Operation not permitted
355 ItemNotExists: Container does not exist
359 def list_object_permissions(self, user, account, container, prefix=''):
360 """Return a list of paths that enforce permissions under a container.
363 NotAllowedError: Operation not permitted
367 def list_object_public(self, user, account, container, prefix=''):
368 """Return a dict mapping paths to public ids for objects that are public under a container."""
371 def get_object_meta(self, user, account, container, name, domain, version=None, include_user_defined=True):
372 """Return a dictionary with the object metadata for the domain.
374 The keys returned are all user-defined, except:
375 'name': The object name
377 'bytes': The total data size
379 'type': The content type
381 'hash': The hashmap hash
383 'modified': Last modification timestamp (overall)
385 'modified_by': The user that committed the object (version requested)
387 'version': The version identifier
389 'version_timestamp': The version's modification timestamp
391 'uuid': A unique identifier that persists data or metadata updates and renames
393 'checksum': The MD5 sum of the object (may be empty)
396 NotAllowedError: Operation not permitted
398 ItemNotExists: Container/object does not exist
400 VersionNotExists: Version does not exist
404 def update_object_meta(self, user, account, container, name, domain, meta, replace=False):
405 """Update the metadata associated with the object for the domain and return the new version.
408 'domain': Metadata domain
410 'meta': Dictionary with metadata to update
412 'replace': Replace instead of update
415 NotAllowedError: Operation not permitted
417 ItemNotExists: Container/object does not exist
421 def get_object_permissions(self, user, account, container, name):
422 """Return the action allowed on the object, the path
423 from which the object gets its permissions from,
424 along with a dictionary containing the permissions.
426 The dictionary keys are (also used for defining the action):
427 'read': The object is readable by the users/groups in the list
429 'write': The object is writable by the users/groups in the list
432 NotAllowedError: Operation not permitted
434 ItemNotExists: Container/object does not exist
438 def update_object_permissions(self, user, account, container, name, permissions):
439 """Update (set) the permissions associated with the object.
442 'permissions': Dictionary with permissions to set
445 NotAllowedError: Operation not permitted
447 ItemNotExists: Container/object does not exist
449 ValueError: Invalid users/groups in permissions
453 def get_object_public(self, user, account, container, name):
454 """Return the public id of the object if applicable.
457 NotAllowedError: Operation not permitted
459 ItemNotExists: Container/object does not exist
463 def update_object_public(self, user, account, container, name, public):
464 """Update the public status of the object.
467 'public': Boolean value
470 NotAllowedError: Operation not permitted
472 ItemNotExists: Container/object does not exist
476 def get_object_hashmap(self, user, account, container, name, version=None):
477 """Return the object's size and a list with partial hashes.
480 NotAllowedError: Operation not permitted
482 ItemNotExists: Container/object does not exist
484 VersionNotExists: Version does not exist
488 def update_object_hashmap(self, user, account, container, name, size, type, hashmap, checksum, domain, meta={}, replace_meta=False, permissions=None):
489 """Create/update an object with the specified size and partial hashes and return the new version.
492 'domain': Metadata domain
494 'meta': Dictionary with metadata to change
496 'replace_meta': Replace metadata instead of update
498 'permissions': Updated object permissions
501 NotAllowedError: Operation not permitted
503 ItemNotExists: Container does not exist
505 ValueError: Invalid users/groups in permissions
507 QuotaError: Account or container quota exceeded
511 def update_object_checksum(self, user, account, container, name, version, checksum):
512 """Update an object's checksum."""
515 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):
516 """Copy an object's data and metadata and return the new version.
519 'domain': Metadata domain
521 'meta': Dictionary with metadata to change from source to destination
523 'replace_meta': Replace metadata instead of update
525 'permissions': New object permissions
527 'src_version': Copy from the version provided
529 'delimiter': Copy objects whose path starts with src_name + delimiter
532 NotAllowedError: Operation not permitted
534 ItemNotExists: Container/object does not exist
536 VersionNotExists: Version does not exist
538 ValueError: Invalid users/groups in permissions
540 QuotaError: Account or container quota exceeded
544 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):
545 """Move an object's data and metadata and return the new version.
548 'domain': Metadata domain
550 'meta': Dictionary with metadata to change from source to destination
552 'replace_meta': Replace metadata instead of update
554 'permissions': New object permissions
556 'delimiter': Move objects whose path starts with src_name + delimiter
559 NotAllowedError: Operation not permitted
561 ItemNotExists: Container/object does not exist
563 ValueError: Invalid users/groups in permissions
565 QuotaError: Account or container quota exceeded
569 def delete_object(self, user, account, container, name, until=None, delimiter=None):
570 """Delete/purge an object.
573 'delimiter': Delete objects whose path starting with name + delimiter
576 NotAllowedError: Operation not permitted
578 ItemNotExists: Container/object does not exist
582 def list_versions(self, user, account, container, name):
583 """Return a list of all (version, version_timestamp) tuples for an object.
586 NotAllowedError: Operation not permitted
590 def get_uuid(self, user, uuid):
591 """Return the (account, container, name) for the UUID given.
594 NotAllowedError: Operation not permitted
596 NameError: UUID does not exist
600 def get_public(self, user, public):
601 """Return the (account, container, name) for the public id given.
604 NotAllowedError: Operation not permitted
606 NameError: Public id does not exist
610 def get_block(self, hash):
611 """Return a block's data.
614 ItemNotExists: Block does not exist
618 def put_block(self, data):
619 """Store a block and return the hash."""
622 def update_block(self, hash, data, offset=0):
623 """Update a known block and return the hash.
626 IndexError: Offset or data outside block limits