Revision d50ed8d4 snf-pithos-backend/pithos/backends/base.py

b/snf-pithos-backend/pithos/backends/base.py
1 1
# Copyright 2011-2012 GRNET S.A. All rights reserved.
2
# 
2
#
3 3
# Redistribution and use in source and binary forms, with or
4 4
# without modification, are permitted provided that the following
5 5
# conditions are met:
6
# 
6
#
7 7
#   1. Redistributions of source code must retain the above
8 8
#      copyright notice, this list of conditions and the following
9 9
#      disclaimer.
10
# 
10
#
11 11
#   2. Redistributions in binary form must reproduce the above
12 12
#      copyright notice, this list of conditions and the following
13 13
#      disclaimer in the documentation and/or other materials
14 14
#      provided with the distribution.
15
# 
15
#
16 16
# THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
17 17
# OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 18
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
......
25 25
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
26 26
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27 27
# POSSIBILITY OF SUCH DAMAGE.
28
# 
28
#
29 29
# The views and conclusions contained in the software and
30 30
# documentation are those of the authors and should not be
31 31
# interpreted as representing official policies, either expressed
32 32
# or implied, of GRNET S.A.
33 33

  
34 34
# Default setting for new accounts.
35
DEFAULT_QUOTA = 0 # No quota.
35
DEFAULT_QUOTA = 0  # No quota.
36 36
DEFAULT_VERSIONING = 'auto'
37 37

  
38 38

  
39

  
39 40
class NotAllowedError(Exception):
40 41
    pass
41 42

  
43

  
42 44
class QuotaError(Exception):
43 45
    pass
44 46

  
47

  
45 48
class AccountExists(NameError):
46 49
    pass
47
    
50

  
51

  
48 52
class ContainerExists(NameError):
49 53
    pass
50 54

  
55

  
51 56
class AccountNotEmpty(IndexError):
52 57
    pass
53 58

  
59

  
54 60
class ContainerNotEmpty(IndexError):
55 61
    pass
56 62

  
63

  
57 64
class ItemNotExists(NameError):
58 65
    pass
59 66

  
67

  
60 68
class VersionNotExists(IndexError):
61 69
    pass
62 70

  
71

  
63 72
class BaseBackend(object):
64 73
    """Abstract backend class that serves as a reference for actual implementations.
65
    
74

  
66 75
    The purpose of the backend is to provide the necessary functions for handling data
67 76
    and metadata. It is responsible for the actual storage and retrieval of information.
68
    
77

  
69 78
    Note that the account level is always valid as it is checked from another subsystem.
70
    
79

  
71 80
    When not replacing metadata/groups/policy, keys with empty values should be deleted.
72
    
81

  
73 82
    The following variables should be available:
74 83
        'hash_algorithm': Suggested is 'sha256'
75
        
84

  
76 85
        'block_size': Suggested is 4MB
77
        
86

  
78 87
        'default_policy': A dictionary with default policy settings
79 88
    """
80
    
89

  
81 90
    def close(self):
82 91
        """Close the backend connection."""
83 92
        pass
84
    
93

  
85 94
    def list_accounts(self, user, marker=None, limit=10000):
86 95
        """Return a list of accounts the user can access.
87
        
96

  
88 97
        Parameters:
89 98
            'marker': Start list from the next item after 'marker'
90
            
99

  
91 100
            'limit': Number of containers to return
92 101
        """
93 102
        return []
94
    
103

  
95 104
    def get_account_meta(self, user, account, domain, until=None, include_user_defined=True):
96 105
        """Return a dictionary with the account metadata for the domain.
97
        
106

  
98 107
        The keys returned are all user-defined, except:
99 108
            'name': The account name
100
            
109

  
101 110
            'count': The number of containers (or 0)
102
            
111

  
103 112
            'bytes': The total data size (or 0)
104
            
113

  
105 114
            'modified': Last modification timestamp (overall)
106
            
115

  
107 116
            'until_timestamp': Last modification until the timestamp provided
108
        
117

  
109 118
        Raises:
110 119
            NotAllowedError: Operation not permitted
111 120
        """
112 121
        return {}
113
    
122

  
114 123
    def update_account_meta(self, user, account, domain, meta, replace=False):
115 124
        """Update the metadata associated with the account for the domain.
116
        
125

  
117 126
        Parameters:
118 127
            'domain': Metadata domain
119
            
128

  
120 129
            'meta': Dictionary with metadata to update
121
            
130

  
122 131
            'replace': Replace instead of update
123
        
132

  
124 133
        Raises:
125 134
            NotAllowedError: Operation not permitted
126 135
        """
127 136
        return
128
    
137

  
129 138
    def get_account_groups(self, user, account):
130 139
        """Return a dictionary with the user groups defined for this account.
131
        
140

  
132 141
        Raises:
133 142
            NotAllowedError: Operation not permitted
134 143
        """
135 144
        return {}
136
    
145

  
137 146
    def update_account_groups(self, user, account, groups, replace=False):
138 147
        """Update the groups associated with the account.
139
        
148

  
140 149
        Raises:
141 150
            NotAllowedError: Operation not permitted
142
            
151

  
143 152
            ValueError: Invalid data in groups
144 153
        """
145 154
        return
146
    
155

  
147 156
    def get_account_policy(self, user, account):
148 157
        """Return a dictionary with the account policy.
149
        
158

  
150 159
        The keys returned are:
151 160
            'quota': The maximum bytes allowed (default is 0 - unlimited)
152
            
161

  
153 162
            'versioning': Can be 'auto', 'manual' or 'none' (default is 'manual')
154
        
163

  
155 164
        Raises:
156 165
            NotAllowedError: Operation not permitted
157 166
        """
158 167
        return {}
159
    
168

  
160 169
    def update_account_policy(self, user, account, policy, replace=False):
161 170
        """Update the policy associated with the account.
162
        
171

  
163 172
        Raises:
164 173
            NotAllowedError: Operation not permitted
165
            
174

  
166 175
            ValueError: Invalid policy defined
167 176
        """
168 177
        return
169
    
178

  
170 179
    def put_account(self, user, account, policy={}):
171 180
        """Create a new account with the given name.
172
        
181

  
173 182
        Raises:
174 183
            NotAllowedError: Operation not permitted
175
            
184

  
176 185
            ValueError: Invalid policy defined
177 186
        """
178 187
        return
179
    
188

  
180 189
    def delete_account(self, user, account):
181 190
        """Delete the account with the given name.
182
        
191

  
183 192
        Raises:
184 193
            NotAllowedError: Operation not permitted
185
            
194

  
186 195
            AccountNotEmpty: Account is not empty
187 196
        """
188 197
        return
189
    
198

  
190 199
    def list_containers(self, user, account, marker=None, limit=10000, shared=False, until=None, public=False):
191 200
        """Return a list of container names existing under an account.
192
        
201

  
193 202
        Parameters:
194 203
            'marker': Start list from the next item after 'marker'
195
            
204

  
196 205
            'limit': Number of containers to return
197
            
206

  
198 207
            'shared': Only list containers with permissions set
199
            
208

  
200 209
            'public': Only list containers containing public objects
201
            
202
        
210

  
211

  
203 212
        Raises:
204 213
            NotAllowedError: Operation not permitted
205 214
        """
206 215
        return []
207
    
216

  
208 217
    def list_container_meta(self, user, account, container, domain, until=None):
209 218
        """Return a list with all the container's object meta keys for the domain.
210
        
219

  
211 220
        Raises:
212 221
            NotAllowedError: Operation not permitted
213
            
222

  
214 223
            ItemNotExists: Container does not exist
215 224
        """
216 225
        return []
217
    
226

  
218 227
    def get_container_meta(self, user, account, container, domain, until=None, include_user_defined=True):
219 228
        """Return a dictionary with the container metadata for the domain.
220
        
229

  
221 230
        The keys returned are all user-defined, except:
222 231
            'name': The container name
223
            
232

  
224 233
            'count': The number of objects
225
            
234

  
226 235
            'bytes': The total data size
227
            
236

  
228 237
            'modified': Last modification timestamp (overall)
229
            
238

  
230 239
            'until_timestamp': Last modification until the timestamp provided
231
        
240

  
232 241
        Raises:
233 242
            NotAllowedError: Operation not permitted
234
            
243

  
235 244
            ItemNotExists: Container does not exist
236 245
        """
237 246
        return {}
238
    
247

  
239 248
    def update_container_meta(self, user, account, container, domain, meta, replace=False):
240 249
        """Update the metadata associated with the container for the domain.
241
        
250

  
242 251
        Parameters:
243 252
            'domain': Metadata domain
244
            
253

  
245 254
            'meta': Dictionary with metadata to update
246
            
255

  
247 256
            'replace': Replace instead of update
248
        
257

  
249 258
        Raises:
250 259
            NotAllowedError: Operation not permitted
251
            
260

  
252 261
            ItemNotExists: Container does not exist
253 262
        """
254 263
        return
255
    
264

  
256 265
    def get_container_policy(self, user, account, container):
257 266
        """Return a dictionary with the container policy.
258
        
267

  
259 268
        The keys returned are:
260 269
            'quota': The maximum bytes allowed (default is 0 - unlimited)
261
            
270

  
262 271
            'versioning': Can be 'auto', 'manual' or 'none' (default is 'manual')
263
        
272

  
264 273
        Raises:
265 274
            NotAllowedError: Operation not permitted
266
            
275

  
267 276
            ItemNotExists: Container does not exist
268 277
        """
269 278
        return {}
270
    
279

  
271 280
    def update_container_policy(self, user, account, container, policy, replace=False):
272 281
        """Update the policy associated with the container.
273
        
282

  
274 283
        Raises:
275 284
            NotAllowedError: Operation not permitted
276
            
285

  
277 286
            ItemNotExists: Container does not exist
278
            
287

  
279 288
            ValueError: Invalid policy defined
280 289
        """
281 290
        return
282
    
291

  
283 292
    def put_container(self, user, account, container, policy={}, delimiter=None):
284 293
        """Create a new container with the given name.
285
        
294

  
286 295
        Parameters:
287 296
            'delimiter': If present deletes container contents instead of the container
288
        
297

  
289 298
        Raises:
290 299
            NotAllowedError: Operation not permitted
291
            
300

  
292 301
            ContainerExists: Container already exists
293
            
302

  
294 303
            ValueError: Invalid policy defined
295 304
        """
296 305
        return
297
    
306

  
298 307
    def delete_container(self, user, account, container, until=None):
299 308
        """Delete/purge the container with the given name.
300
        
309

  
301 310
        Raises:
302 311
            NotAllowedError: Operation not permitted
303
            
312

  
304 313
            ItemNotExists: Container does not exist
305
            
314

  
306 315
            ContainerNotEmpty: Container is not empty
307 316
        """
308 317
        return
309
    
318

  
310 319
    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 320
        """Return a list of object (name, version_id) tuples existing under a container.
312
        
321

  
313 322
        Parameters:
314 323
            'prefix': List objects starting with 'prefix'
315
            
324

  
316 325
            'delimiter': Return unique names before 'delimiter' and after 'prefix'
317
            
326

  
318 327
            'marker': Start list from the next item after 'marker'
319
            
328

  
320 329
            'limit': Number of objects to return
321
            
330

  
322 331
            'virtual': If not set, the result will only include names starting
323 332
                       with 'prefix' and ending without a 'delimiter' or with
324 333
                       the first occurance of the 'delimiter' after 'prefix'.
325 334
                       If set, the result will include all names after 'prefix',
326 335
                       up to and including the 'delimiter' if it is found
327
            
336

  
328 337
            'domain': Metadata domain for keys
329
            
338

  
330 339
            'keys': Include objects that satisfy the key queries in the list.
331 340
                    Use 'key', '!key' for existence queries, 'key op value' for
332 341
                    value queries, where 'op' can be one of =, !=, <=, >=, <, >
333
            
342

  
334 343
            'shared': Only list objects with permissions set
335
             
344

  
336 345
            'size_range': Include objects with byte size in (from, to).
337 346
                          Use None to specify unlimited
338
            
347

  
339 348
            'public': Only list public objects
340
             
341
        
349

  
350

  
342 351
        Raises:
343 352
            NotAllowedError: Operation not permitted
344
            
353

  
345 354
            ItemNotExists: Container does not exist
346 355
        """
347 356
        return []
348
    
357

  
349 358
    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 359
        """Return a list of object metadata dicts existing under a container.
351
        
360

  
352 361
        Same parameters with list_objects. Returned dicts have no user-defined
353 362
        metadata and, if until is not None, a None 'modified' timestamp.
354
        
363

  
355 364
        Raises:
356 365
            NotAllowedError: Operation not permitted
357
            
366

  
358 367
            ItemNotExists: Container does not exist
359 368
        """
360 369
        return []
361
    
370

  
362 371
    def list_object_permissions(self, user, account, container, prefix=''):
363 372
        """Return a list of paths that enforce permissions under a container.
364
        
373

  
365 374
        Raises:
366 375
            NotAllowedError: Operation not permitted
367 376
        """
368 377
        return []
369
    
378

  
370 379
    def list_object_public(self, user, account, container, prefix=''):
371 380
        """Return a dict mapping paths to public ids for objects that are public under a container."""
372 381
        return {}
373
    
382

  
374 383
    def get_object_meta(self, user, account, container, name, domain, version=None, include_user_defined=True):
375 384
        """Return a dictionary with the object metadata for the domain.
376
        
385

  
377 386
        The keys returned are all user-defined, except:
378 387
            'name': The object name
379
            
388

  
380 389
            'bytes': The total data size
381
            
390

  
382 391
            'type': The content type
383
            
392

  
384 393
            'hash': The hashmap hash
385
            
394

  
386 395
            'modified': Last modification timestamp (overall)
387
            
396

  
388 397
            'modified_by': The user that committed the object (version requested)
389
            
398

  
390 399
            'version': The version identifier
391
            
400

  
392 401
            'version_timestamp': The version's modification timestamp
393
            
402

  
394 403
            'uuid': A unique identifier that persists data or metadata updates and renames
395
            
404

  
396 405
            'checksum': The MD5 sum of the object (may be empty)
397
        
406

  
398 407
        Raises:
399 408
            NotAllowedError: Operation not permitted
400
            
409

  
401 410
            ItemNotExists: Container/object does not exist
402
            
411

  
403 412
            VersionNotExists: Version does not exist
404 413
        """
405 414
        return {}
406
    
415

  
407 416
    def update_object_meta(self, user, account, container, name, domain, meta, replace=False):
408 417
        """Update the metadata associated with the object for the domain and return the new version.
409
        
418

  
410 419
        Parameters:
411 420
            'domain': Metadata domain
412
            
421

  
413 422
            'meta': Dictionary with metadata to update
414
            
423

  
415 424
            'replace': Replace instead of update
416
        
425

  
417 426
        Raises:
418 427
            NotAllowedError: Operation not permitted
419
            
428

  
420 429
            ItemNotExists: Container/object does not exist
421 430
        """
422 431
        return ''
423
    
432

  
424 433
    def get_object_permissions(self, user, account, container, name):
425 434
        """Return the action allowed on the object, the path
426 435
        from which the object gets its permissions from,
427 436
        along with a dictionary containing the permissions.
428
        
437

  
429 438
        The dictionary keys are (also used for defining the action):
430 439
            'read': The object is readable by the users/groups in the list
431
            
440

  
432 441
            'write': The object is writable by the users/groups in the list
433
        
442

  
434 443
        Raises:
435 444
            NotAllowedError: Operation not permitted
436
            
445

  
437 446
            ItemNotExists: Container/object does not exist
438 447
        """
439 448
        return {}
440
    
449

  
441 450
    def update_object_permissions(self, user, account, container, name, permissions):
442 451
        """Update (set) the permissions associated with the object.
443
        
452

  
444 453
        Parameters:
445 454
            'permissions': Dictionary with permissions to set
446
        
455

  
447 456
        Raises:
448 457
            NotAllowedError: Operation not permitted
449
            
458

  
450 459
            ItemNotExists: Container/object does not exist
451
            
460

  
452 461
            ValueError: Invalid users/groups in permissions
453 462
        """
454 463
        return
455
    
464

  
456 465
    def get_object_public(self, user, account, container, name):
457 466
        """Return the public id of the object if applicable.
458
        
467

  
459 468
        Raises:
460 469
            NotAllowedError: Operation not permitted
461
            
470

  
462 471
            ItemNotExists: Container/object does not exist
463 472
        """
464 473
        return None
465
    
474

  
466 475
    def update_object_public(self, user, account, container, name, public):
467 476
        """Update the public status of the object.
468
        
477

  
469 478
        Parameters:
470 479
            'public': Boolean value
471
        
480

  
472 481
        Raises:
473 482
            NotAllowedError: Operation not permitted
474
            
483

  
475 484
            ItemNotExists: Container/object does not exist
476 485
        """
477 486
        return
478
    
487

  
479 488
    def get_object_hashmap(self, user, account, container, name, version=None):
480 489
        """Return the object's size and a list with partial hashes.
481
        
490

  
482 491
        Raises:
483 492
            NotAllowedError: Operation not permitted
484
            
493

  
485 494
            ItemNotExists: Container/object does not exist
486
            
495

  
487 496
            VersionNotExists: Version does not exist
488 497
        """
489 498
        return 0, []
490
    
499

  
491 500
    def update_object_hashmap(self, user, account, container, name, size, type, hashmap, checksum, domain, meta={}, replace_meta=False, permissions=None):
492 501
        """Create/update an object with the specified size and partial hashes and return the new version.
493
        
502

  
494 503
        Parameters:
495 504
            'domain': Metadata domain
496
            
505

  
497 506
            'meta': Dictionary with metadata to change
498
            
507

  
499 508
            'replace_meta': Replace metadata instead of update
500
            
509

  
501 510
            'permissions': Updated object permissions
502
        
511

  
503 512
        Raises:
504 513
            NotAllowedError: Operation not permitted
505
            
514

  
506 515
            ItemNotExists: Container does not exist
507
            
516

  
508 517
            ValueError: Invalid users/groups in permissions
509
            
518

  
510 519
            QuotaError: Account or container quota exceeded
511 520
        """
512 521
        return ''
513
    
522

  
514 523
    def update_object_checksum(self, user, account, container, name, version, checksum):
515 524
        """Update an object's checksum."""
516 525
        return
517
    
526

  
518 527
    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 528
        """Copy an object's data and metadata and return the new version.
520
        
529

  
521 530
        Parameters:
522 531
            'domain': Metadata domain
523
            
532

  
524 533
            'meta': Dictionary with metadata to change from source to destination
525
            
534

  
526 535
            'replace_meta': Replace metadata instead of update
527
            
536

  
528 537
            'permissions': New object permissions
529
            
538

  
530 539
            'src_version': Copy from the version provided
531
            
540

  
532 541
            'delimiter': Copy objects whose path starts with src_name + delimiter
533
        
542

  
534 543
        Raises:
535 544
            NotAllowedError: Operation not permitted
536
            
545

  
537 546
            ItemNotExists: Container/object does not exist
538
            
547

  
539 548
            VersionNotExists: Version does not exist
540
            
549

  
541 550
            ValueError: Invalid users/groups in permissions
542
            
551

  
543 552
            QuotaError: Account or container quota exceeded
544 553
        """
545 554
        return ''
546
    
555

  
547 556
    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 557
        """Move an object's data and metadata and return the new version.
549
        
558

  
550 559
        Parameters:
551 560
            'domain': Metadata domain
552
            
561

  
553 562
            'meta': Dictionary with metadata to change from source to destination
554
            
563

  
555 564
            'replace_meta': Replace metadata instead of update
556
            
565

  
557 566
            'permissions': New object permissions
558
            
567

  
559 568
            'delimiter': Move objects whose path starts with src_name + delimiter
560
        
569

  
561 570
        Raises:
562 571
            NotAllowedError: Operation not permitted
563
            
572

  
564 573
            ItemNotExists: Container/object does not exist
565
            
574

  
566 575
            ValueError: Invalid users/groups in permissions
567
            
576

  
568 577
            QuotaError: Account or container quota exceeded
569 578
        """
570 579
        return ''
571
    
580

  
572 581
    def delete_object(self, user, account, container, name, until=None, delimiter=None):
573 582
        """Delete/purge an object.
574
        
583

  
575 584
        Parameters:
576 585
            'delimiter': Delete objects whose path starting with name + delimiter
577
        
586

  
578 587
        Raises:
579 588
            NotAllowedError: Operation not permitted
580
            
589

  
581 590
            ItemNotExists: Container/object does not exist
582 591
        """
583 592
        return
584
    
593

  
585 594
    def list_versions(self, user, account, container, name):
586 595
        """Return a list of all (version, version_timestamp) tuples for an object.
587
        
596

  
588 597
        Raises:
589 598
            NotAllowedError: Operation not permitted
590 599
        """
591 600
        return []
592
    
601

  
593 602
    def get_uuid(self, user, uuid):
594 603
        """Return the (account, container, name) for the UUID given.
595
        
604

  
596 605
        Raises:
597 606
            NotAllowedError: Operation not permitted
598
            
607

  
599 608
            NameError: UUID does not exist
600 609
        """
601 610
        return None
602
    
611

  
603 612
    def get_public(self, user, public):
604 613
        """Return the (account, container, name) for the public id given.
605
        
614

  
606 615
        Raises:
607 616
            NotAllowedError: Operation not permitted
608
            
617

  
609 618
            NameError: Public id does not exist
610 619
        """
611 620
        return None
612
    
621

  
613 622
    def get_block(self, hash):
614 623
        """Return a block's data.
615
        
624

  
616 625
        Raises:
617 626
            ItemNotExists: Block does not exist
618 627
        """
619 628
        return ''
620
    
629

  
621 630
    def put_block(self, data):
622 631
        """Store a block and return the hash."""
623 632
        return 0
624
    
633

  
625 634
    def update_block(self, hash, data, offset=0):
626 635
        """Update a known block and return the hash.
627
        
636

  
628 637
        Raises:
629 638
            IndexError: Offset or data outside block limits
630 639
        """

Also available in: Unified diff