change backend to raise custom exceptions
[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={}):
284         """Create a new container with the given name.
285         
286         Raises:
287             NotAllowedError: Operation not permitted
288             
289             ContainerExists: Container already exists
290             
291             ValueError: Invalid policy defined
292         """
293         return
294     
295     def delete_container(self, user, account, container, until=None):
296         """Delete/purge the container with the given name.
297         
298         Raises:
299             NotAllowedError: Operation not permitted
300             
301             ItemNotExists: Container does not exist
302             
303             ContainerNotEmpty: Container is not empty
304         """
305         return
306     
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.
309         
310         Parameters:
311             'prefix': List objects starting with 'prefix'
312             
313             'delimiter': Return unique names before 'delimiter' and after 'prefix'
314             
315             'marker': Start list from the next item after 'marker'
316             
317             'limit': Number of objects to return
318             
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
324             
325             'domain': Metadata domain for keys
326             
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 =, !=, <=, >=, <, >
330             
331             'shared': Only list objects with permissions set
332              
333             'size_range': Include objects with byte size in (from, to).
334                           Use None to specify unlimited
335             
336             'public': Only list public objects
337              
338         
339         Raises:
340             NotAllowedError: Operation not permitted
341             
342             ItemNotExists: Container does not exist
343         """
344         return []
345     
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.
348         
349         Same parameters with list_objects. Returned dicts have no user-defined
350         metadata and, if until is not None, a None 'modified' timestamp.
351         
352         Raises:
353             NotAllowedError: Operation not permitted
354             
355             ItemNotExists: Container does not exist
356         """
357         return []
358     
359     def list_object_permissions(self, user, account, container, prefix=''):
360         """Return a list of paths that enforce permissions under a container.
361         
362         Raises:
363             NotAllowedError: Operation not permitted
364         """
365         return []
366     
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."""
369         return {}
370     
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.
373         
374         The keys returned are all user-defined, except:
375             'name': The object name
376             
377             'bytes': The total data size
378             
379             'type': The content type
380             
381             'hash': The hashmap hash
382             
383             'modified': Last modification timestamp (overall)
384             
385             'modified_by': The user that committed the object (version requested)
386             
387             'version': The version identifier
388             
389             'version_timestamp': The version's modification timestamp
390             
391             'uuid': A unique identifier that persists data or metadata updates and renames
392             
393             'checksum': The MD5 sum of the object (may be empty)
394         
395         Raises:
396             NotAllowedError: Operation not permitted
397             
398             ItemNotExists: Container/object does not exist
399             
400             VersionNotExists: Version does not exist
401         """
402         return {}
403     
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.
406         
407         Parameters:
408             'domain': Metadata domain
409             
410             'meta': Dictionary with metadata to update
411             
412             'replace': Replace instead of update
413         
414         Raises:
415             NotAllowedError: Operation not permitted
416             
417             ItemNotExists: Container/object does not exist
418         """
419         return ''
420     
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.
425         
426         The dictionary keys are (also used for defining the action):
427             'read': The object is readable by the users/groups in the list
428             
429             'write': The object is writable by the users/groups in the list
430         
431         Raises:
432             NotAllowedError: Operation not permitted
433             
434             ItemNotExists: Container/object does not exist
435         """
436         return {}
437     
438     def update_object_permissions(self, user, account, container, name, permissions):
439         """Update (set) the permissions associated with the object.
440         
441         Parameters:
442             'permissions': Dictionary with permissions to set
443         
444         Raises:
445             NotAllowedError: Operation not permitted
446             
447             ItemNotExists: Container/object does not exist
448             
449             ValueError: Invalid users/groups in permissions
450         """
451         return
452     
453     def get_object_public(self, user, account, container, name):
454         """Return the public id of the object if applicable.
455         
456         Raises:
457             NotAllowedError: Operation not permitted
458             
459             ItemNotExists: Container/object does not exist
460         """
461         return None
462     
463     def update_object_public(self, user, account, container, name, public):
464         """Update the public status of the object.
465         
466         Parameters:
467             'public': Boolean value
468         
469         Raises:
470             NotAllowedError: Operation not permitted
471             
472             ItemNotExists: Container/object does not exist
473         """
474         return
475     
476     def get_object_hashmap(self, user, account, container, name, version=None):
477         """Return the object's size and a list with partial hashes.
478         
479         Raises:
480             NotAllowedError: Operation not permitted
481             
482             ItemNotExists: Container/object does not exist
483             
484             VersionNotExists: Version does not exist
485         """
486         return 0, []
487     
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.
490         
491         Parameters:
492             'domain': Metadata domain
493             
494             'meta': Dictionary with metadata to change
495             
496             'replace_meta': Replace metadata instead of update
497             
498             'permissions': Updated object permissions
499         
500         Raises:
501             NotAllowedError: Operation not permitted
502             
503             ItemNotExists: Container does not exist
504             
505             ValueError: Invalid users/groups in permissions
506             
507             QuotaError: Account or container quota exceeded
508         """
509         return ''
510     
511     def update_object_checksum(self, user, account, container, name, version, checksum):
512         """Update an object's checksum."""
513         return
514     
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.
517         
518         Parameters:
519             'domain': Metadata domain
520             
521             'meta': Dictionary with metadata to change from source to destination
522             
523             'replace_meta': Replace metadata instead of update
524             
525             'permissions': New object permissions
526             
527             'src_version': Copy from the version provided
528             
529             'delimiter': Copy objects whose path starts with src_name + delimiter
530         
531         Raises:
532             NotAllowedError: Operation not permitted
533             
534             ItemNotExists: Container/object does not exist
535             
536             VersionNotExists: Version does not exist
537             
538             ValueError: Invalid users/groups in permissions
539             
540             QuotaError: Account or container quota exceeded
541         """
542         return ''
543     
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.
546         
547         Parameters:
548             'domain': Metadata domain
549             
550             'meta': Dictionary with metadata to change from source to destination
551             
552             'replace_meta': Replace metadata instead of update
553             
554             'permissions': New object permissions
555             
556             'delimiter': Move objects whose path starts with src_name + delimiter
557         
558         Raises:
559             NotAllowedError: Operation not permitted
560             
561             ItemNotExists: Container/object does not exist
562             
563             ValueError: Invalid users/groups in permissions
564             
565             QuotaError: Account or container quota exceeded
566         """
567         return ''
568     
569     def delete_object(self, user, account, container, name, until=None, delimiter=None):
570         """Delete/purge an object.
571         
572         Parameters:
573             'delimiter': Delete objects whose path starting with name + delimiter
574         
575         Raises:
576             NotAllowedError: Operation not permitted
577             
578             ItemNotExists: Container/object does not exist
579         """
580         return
581     
582     def list_versions(self, user, account, container, name):
583         """Return a list of all (version, version_timestamp) tuples for an object.
584         
585         Raises:
586             NotAllowedError: Operation not permitted
587         """
588         return []
589     
590     def get_uuid(self, user, uuid):
591         """Return the (account, container, name) for the UUID given.
592         
593         Raises:
594             NotAllowedError: Operation not permitted
595             
596             NameError: UUID does not exist
597         """
598         return None
599     
600     def get_public(self, user, public):
601         """Return the (account, container, name) for the public id given.
602         
603         Raises:
604             NotAllowedError: Operation not permitted
605             
606             NameError: Public id does not exist
607         """
608         return None
609     
610     def get_block(self, hash):
611         """Return a block's data.
612         
613         Raises:
614             ItemNotExists: Block does not exist
615         """
616         return ''
617     
618     def put_block(self, data):
619         """Store a block and return the hash."""
620         return 0
621     
622     def update_block(self, hash, data, offset=0):
623         """Update a known block and return the hash.
624         
625         Raises:
626             IndexError: Offset or data outside block limits
627         """
628         return 0