Revert "Have BaseBackend inherit from ArgBasedSingleton"
[pithos] / snf-pithos-backend / pithos / backends / base.py
1 # Copyright 2011-2012 GRNET S.A. All rights reserved.
2
3 # Redistribution and use in source and binary forms, with or
4 # without modification, are permitted provided that the following
5 # conditions are met:
6
7 #   1. Redistributions of source code must retain the above
8 #      copyright notice, this list of conditions and the following
9 #      disclaimer.
10
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.
15
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.
28
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.
33
34 # Default setting for new accounts.
35 DEFAULT_QUOTA = 0 # No quota.
36 DEFAULT_VERSIONING = 'auto'
37
38
39 class NotAllowedError(Exception):
40     pass
41
42 class QuotaError(Exception):
43     pass
44
45 class AccountExists(NameError):
46     pass
47     
48 class ContainerExists(NameError):
49     pass
50
51 class AccountNotEmpty(IndexError):
52     pass
53
54 class ContainerNotEmpty(IndexError):
55     pass
56
57 class ItemNotExists(NameError):
58     pass
59
60 class VersionNotExists(IndexError):
61     pass
62
63 class BaseBackend(object):
64     """Abstract backend class that serves as a reference for actual implementations.
65     
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.
68     
69     Note that the account level is always valid as it is checked from another subsystem.
70     
71     When not replacing metadata/groups/policy, keys with empty values should be deleted.
72     
73     The following variables should be available:
74         'hash_algorithm': Suggested is 'sha256'
75         
76         'block_size': Suggested is 4MB
77         
78         'default_policy': A dictionary with default policy settings
79     """
80     
81     def close(self):
82         """Close the backend connection."""
83         pass
84     
85     def list_accounts(self, user, marker=None, limit=10000):
86         """Return a list of accounts the user can access.
87         
88         Parameters:
89             'marker': Start list from the next item after 'marker'
90             
91             'limit': Number of containers to return
92         """
93         return []
94     
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.
97         
98         The keys returned are all user-defined, except:
99             'name': The account name
100             
101             'count': The number of containers (or 0)
102             
103             'bytes': The total data size (or 0)
104             
105             'modified': Last modification timestamp (overall)
106             
107             'until_timestamp': Last modification until the timestamp provided
108         
109         Raises:
110             NotAllowedError: Operation not permitted
111         """
112         return {}
113     
114     def update_account_meta(self, user, account, domain, meta, replace=False):
115         """Update the metadata associated with the account for the domain.
116         
117         Parameters:
118             'domain': Metadata domain
119             
120             'meta': Dictionary with metadata to update
121             
122             'replace': Replace instead of update
123         
124         Raises:
125             NotAllowedError: Operation not permitted
126         """
127         return
128     
129     def get_account_groups(self, user, account):
130         """Return a dictionary with the user groups defined for this account.
131         
132         Raises:
133             NotAllowedError: Operation not permitted
134         """
135         return {}
136     
137     def update_account_groups(self, user, account, groups, replace=False):
138         """Update the groups associated with the account.
139         
140         Raises:
141             NotAllowedError: Operation not permitted
142             
143             ValueError: Invalid data in groups
144         """
145         return
146     
147     def get_account_policy(self, user, account):
148         """Return a dictionary with the account policy.
149         
150         The keys returned are:
151             'quota': The maximum bytes allowed (default is 0 - unlimited)
152             
153             'versioning': Can be 'auto', 'manual' or 'none' (default is 'manual')
154         
155         Raises:
156             NotAllowedError: Operation not permitted
157         """
158         return {}
159     
160     def update_account_policy(self, user, account, policy, replace=False):
161         """Update the policy associated with the account.
162         
163         Raises:
164             NotAllowedError: Operation not permitted
165             
166             ValueError: Invalid policy defined
167         """
168         return
169     
170     def put_account(self, user, account, policy={}):
171         """Create a new account with the given name.
172         
173         Raises:
174             NotAllowedError: Operation not permitted
175             
176             ValueError: Invalid policy defined
177         """
178         return
179     
180     def delete_account(self, user, account):
181         """Delete the account with the given name.
182         
183         Raises:
184             NotAllowedError: Operation not permitted
185             
186             AccountNotEmpty: Account is not empty
187         """
188         return
189     
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.
192         
193         Parameters:
194             'marker': Start list from the next item after 'marker'
195             
196             'limit': Number of containers to return
197             
198             'shared': Only list containers with permissions set
199             
200             'public': Only list containers containing public objects
201             
202         
203         Raises:
204             NotAllowedError: Operation not permitted
205         """
206         return []
207     
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.
210         
211         Raises:
212             NotAllowedError: Operation not permitted
213             
214             ItemNotExists: Container does not exist
215         """
216         return []
217     
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.
220         
221         The keys returned are all user-defined, except:
222             'name': The container name
223             
224             'count': The number of objects
225             
226             'bytes': The total data size
227             
228             'modified': Last modification timestamp (overall)
229             
230             'until_timestamp': Last modification until the timestamp provided
231         
232         Raises:
233             NotAllowedError: Operation not permitted
234             
235             ItemNotExists: Container does not exist
236         """
237         return {}
238     
239     def update_container_meta(self, user, account, container, domain, meta, replace=False):
240         """Update the metadata associated with the container for the domain.
241         
242         Parameters:
243             'domain': Metadata domain
244             
245             'meta': Dictionary with metadata to update
246             
247             'replace': Replace instead of update
248         
249         Raises:
250             NotAllowedError: Operation not permitted
251             
252             ItemNotExists: Container does not exist
253         """
254         return
255     
256     def get_container_policy(self, user, account, container):
257         """Return a dictionary with the container policy.
258         
259         The keys returned are:
260             'quota': The maximum bytes allowed (default is 0 - unlimited)
261             
262             'versioning': Can be 'auto', 'manual' or 'none' (default is 'manual')
263         
264         Raises:
265             NotAllowedError: Operation not permitted
266             
267             ItemNotExists: Container does not exist
268         """
269         return {}
270     
271     def update_container_policy(self, user, account, container, policy, replace=False):
272         """Update the policy associated with the container.
273         
274         Raises:
275             NotAllowedError: Operation not permitted
276             
277             ItemNotExists: Container does not exist
278             
279             ValueError: Invalid policy defined
280         """
281         return
282     
283     def put_container(self, user, account, container, policy={}, delimiter=None):
284         """Create a new container with the given name.
285         
286         Parameters:
287             'delimiter': If present deletes container contents instead of the container
288         
289         Raises:
290             NotAllowedError: Operation not permitted
291             
292             ContainerExists: Container already exists
293             
294             ValueError: Invalid policy defined
295         """
296         return
297     
298     def delete_container(self, user, account, container, until=None):
299         """Delete/purge the container with the given name.
300         
301         Raises:
302             NotAllowedError: Operation not permitted
303             
304             ItemNotExists: Container does not exist
305             
306             ContainerNotEmpty: Container is not empty
307         """
308         return
309     
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.
312         
313         Parameters:
314             'prefix': List objects starting with 'prefix'
315             
316             'delimiter': Return unique names before 'delimiter' and after 'prefix'
317             
318             'marker': Start list from the next item after 'marker'
319             
320             'limit': Number of objects to return
321             
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
327             
328             'domain': Metadata domain for keys
329             
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 =, !=, <=, >=, <, >
333             
334             'shared': Only list objects with permissions set
335              
336             'size_range': Include objects with byte size in (from, to).
337                           Use None to specify unlimited
338             
339             'public': Only list public objects
340              
341         
342         Raises:
343             NotAllowedError: Operation not permitted
344             
345             ItemNotExists: Container does not exist
346         """
347         return []
348     
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.
351         
352         Same parameters with list_objects. Returned dicts have no user-defined
353         metadata and, if until is not None, a None 'modified' timestamp.
354         
355         Raises:
356             NotAllowedError: Operation not permitted
357             
358             ItemNotExists: Container does not exist
359         """
360         return []
361     
362     def list_object_permissions(self, user, account, container, prefix=''):
363         """Return a list of paths that enforce permissions under a container.
364         
365         Raises:
366             NotAllowedError: Operation not permitted
367         """
368         return []
369     
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."""
372         return {}
373     
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.
376         
377         The keys returned are all user-defined, except:
378             'name': The object name
379             
380             'bytes': The total data size
381             
382             'type': The content type
383             
384             'hash': The hashmap hash
385             
386             'modified': Last modification timestamp (overall)
387             
388             'modified_by': The user that committed the object (version requested)
389             
390             'version': The version identifier
391             
392             'version_timestamp': The version's modification timestamp
393             
394             'uuid': A unique identifier that persists data or metadata updates and renames
395             
396             'checksum': The MD5 sum of the object (may be empty)
397         
398         Raises:
399             NotAllowedError: Operation not permitted
400             
401             ItemNotExists: Container/object does not exist
402             
403             VersionNotExists: Version does not exist
404         """
405         return {}
406     
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.
409         
410         Parameters:
411             'domain': Metadata domain
412             
413             'meta': Dictionary with metadata to update
414             
415             'replace': Replace instead of update
416         
417         Raises:
418             NotAllowedError: Operation not permitted
419             
420             ItemNotExists: Container/object does not exist
421         """
422         return ''
423     
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.
428         
429         The dictionary keys are (also used for defining the action):
430             'read': The object is readable by the users/groups in the list
431             
432             'write': The object is writable by the users/groups in the list
433         
434         Raises:
435             NotAllowedError: Operation not permitted
436             
437             ItemNotExists: Container/object does not exist
438         """
439         return {}
440     
441     def update_object_permissions(self, user, account, container, name, permissions):
442         """Update (set) the permissions associated with the object.
443         
444         Parameters:
445             'permissions': Dictionary with permissions to set
446         
447         Raises:
448             NotAllowedError: Operation not permitted
449             
450             ItemNotExists: Container/object does not exist
451             
452             ValueError: Invalid users/groups in permissions
453         """
454         return
455     
456     def get_object_public(self, user, account, container, name):
457         """Return the public id of the object if applicable.
458         
459         Raises:
460             NotAllowedError: Operation not permitted
461             
462             ItemNotExists: Container/object does not exist
463         """
464         return None
465     
466     def update_object_public(self, user, account, container, name, public):
467         """Update the public status of the object.
468         
469         Parameters:
470             'public': Boolean value
471         
472         Raises:
473             NotAllowedError: Operation not permitted
474             
475             ItemNotExists: Container/object does not exist
476         """
477         return
478     
479     def get_object_hashmap(self, user, account, container, name, version=None):
480         """Return the object's size and a list with partial hashes.
481         
482         Raises:
483             NotAllowedError: Operation not permitted
484             
485             ItemNotExists: Container/object does not exist
486             
487             VersionNotExists: Version does not exist
488         """
489         return 0, []
490     
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.
493         
494         Parameters:
495             'domain': Metadata domain
496             
497             'meta': Dictionary with metadata to change
498             
499             'replace_meta': Replace metadata instead of update
500             
501             'permissions': Updated object permissions
502         
503         Raises:
504             NotAllowedError: Operation not permitted
505             
506             ItemNotExists: Container does not exist
507             
508             ValueError: Invalid users/groups in permissions
509             
510             QuotaError: Account or container quota exceeded
511         """
512         return ''
513     
514     def update_object_checksum(self, user, account, container, name, version, checksum):
515         """Update an object's checksum."""
516         return
517     
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.
520         
521         Parameters:
522             'domain': Metadata domain
523             
524             'meta': Dictionary with metadata to change from source to destination
525             
526             'replace_meta': Replace metadata instead of update
527             
528             'permissions': New object permissions
529             
530             'src_version': Copy from the version provided
531             
532             'delimiter': Copy objects whose path starts with src_name + delimiter
533         
534         Raises:
535             NotAllowedError: Operation not permitted
536             
537             ItemNotExists: Container/object does not exist
538             
539             VersionNotExists: Version does not exist
540             
541             ValueError: Invalid users/groups in permissions
542             
543             QuotaError: Account or container quota exceeded
544         """
545         return ''
546     
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.
549         
550         Parameters:
551             'domain': Metadata domain
552             
553             'meta': Dictionary with metadata to change from source to destination
554             
555             'replace_meta': Replace metadata instead of update
556             
557             'permissions': New object permissions
558             
559             'delimiter': Move objects whose path starts with src_name + delimiter
560         
561         Raises:
562             NotAllowedError: Operation not permitted
563             
564             ItemNotExists: Container/object does not exist
565             
566             ValueError: Invalid users/groups in permissions
567             
568             QuotaError: Account or container quota exceeded
569         """
570         return ''
571     
572     def delete_object(self, user, account, container, name, until=None, delimiter=None):
573         """Delete/purge an object.
574         
575         Parameters:
576             'delimiter': Delete objects whose path starting with name + delimiter
577         
578         Raises:
579             NotAllowedError: Operation not permitted
580             
581             ItemNotExists: Container/object does not exist
582         """
583         return
584     
585     def list_versions(self, user, account, container, name):
586         """Return a list of all (version, version_timestamp) tuples for an object.
587         
588         Raises:
589             NotAllowedError: Operation not permitted
590         """
591         return []
592     
593     def get_uuid(self, user, uuid):
594         """Return the (account, container, name) for the UUID given.
595         
596         Raises:
597             NotAllowedError: Operation not permitted
598             
599             NameError: UUID does not exist
600         """
601         return None
602     
603     def get_public(self, user, public):
604         """Return the (account, container, name) for the public id given.
605         
606         Raises:
607             NotAllowedError: Operation not permitted
608             
609             NameError: Public id does not exist
610         """
611         return None
612     
613     def get_block(self, hash):
614         """Return a block's data.
615         
616         Raises:
617             ItemNotExists: Block does not exist
618         """
619         return ''
620     
621     def put_block(self, data):
622         """Store a block and return the hash."""
623         return 0
624     
625     def update_block(self, hash, data, offset=0):
626         """Update a known block and return the hash.
627         
628         Raises:
629             IndexError: Offset or data outside block limits
630         """
631         return 0