Revision 29148653 snf-pithos-backend/pithos/backends/modular.py

b/snf-pithos-backend/pithos/backends/modular.py
97 97
DEFAULT_BLOCK_SIZE = 4 * 1024 * 1024  # 4MB
98 98
DEFAULT_HASH_ALGORITHM = 'sha256'
99 99
#DEFAULT_QUEUE_MODULE = 'pithos.backends.lib.rabbitmq'
100
DEFAULT_BLOCK_PARAMS = { 'mappool': None, 'blockpool': None }
100
DEFAULT_BLOCK_PARAMS = {'mappool': None, 'blockpool': None}
101 101
#DEFAULT_QUEUE_HOSTS = '[amqp://guest:guest@localhost:5672]'
102 102
#DEFAULT_QUEUE_EXCHANGE = 'pithos'
103 103
DEFAULT_PUBLIC_URL_ALPHABET = ('0123456789'
......
162 162
        #queue_exchange = queue_exchange or DEFAULT_QUEUE_EXCHANGE
163 163

  
164 164
        self.public_url_security = (public_url_security or
165
            DEFAULT_PUBLIC_URL_SECURITY)
165
                                    DEFAULT_PUBLIC_URL_SECURITY)
166 166
        self.public_url_alphabet = (public_url_alphabet or
167
            DEFAULT_PUBLIC_URL_ALPHABET)
167
                                    DEFAULT_PUBLIC_URL_ALPHABET)
168 168

  
169 169
        self.hash_algorithm = hash_algorithm
170 170
        self.block_size = block_size
......
183 183
        for x in ['READ', 'WRITE']:
184 184
            setattr(self, x, getattr(self.db_module, x))
185 185
        self.node = self.db_module.Node(**params)
186
        for x in ['ROOTNODE', 'SERIAL', 'HASH', 'SIZE', 'TYPE', 'MTIME', 'MUSER', 'UUID', 'CHECKSUM', 'CLUSTER', 'MATCH_PREFIX', 'MATCH_EXACT']:
186
        for x in ['ROOTNODE', 'SERIAL', 'HASH', 'SIZE', 'TYPE', 'MTIME',
187
                  'MUSER', 'UUID', 'CHECKSUM', 'CLUSTER', 'MATCH_PREFIX',
188
                  'MATCH_EXACT']:
187 189
            setattr(self, x, getattr(self.db_module, x))
188 190

  
189 191
        self.block_module = load_module(block_module)
......
251 253
                self.wrapper.execute()
252 254

  
253 255
                r = self.astakosclient.resolve_commissions(
254
                            token=self.service_token,
255
                            accept_serials=self.serials,
256
                            reject_serials=[])
256
                    token=self.service_token,
257
                    accept_serials=self.serials,
258
                    reject_serials=[])
257 259
                self.commission_serials.delete_many(
258 260
                    r['accepted'])
259 261

  
......
291 293
            "get_account_meta: %s %s %s %s", user, account, domain, until)
292 294
        path, node = self._lookup_account(account, user == account)
293 295
        if user != account:
294
            if until or node is None or account not in self._allowed_accounts(user):
296
            if until or (node is None) or (account not
297
                                           in self._allowed_accounts(user)):
295 298
                raise NotAllowedError
296 299
        try:
297 300
            props = self._get_properties(node, until)
......
336 339
                           update_statistics_ancestors_depth=-1)
337 340

  
338 341
    def get_account_groups(self, user, account):
339
        """Return a dictionary with the user groups defined for this account."""
342
        """Return a dictionary with the user groups defined for the account."""
340 343

  
341 344
        logger.debug("get_account_groups: %s %s", user, account)
342 345
        if user != account:
......
419 422
            raise AccountNotEmpty('Account is not empty')
420 423
        self.permissions.group_destroy(account)
421 424

  
422
    def list_containers(self, user, account, marker=None, limit=10000, shared=False, until=None, public=False):
425
    def list_containers(self, user, account, marker=None, limit=10000,
426
                        shared=False, until=None, public=False):
423 427
        """Return a list of containers existing under an account."""
424 428

  
425 429
        logger.debug("list_containers: %s %s %s %s %s %s %s", user,
......
433 437
        if shared or public:
434 438
            allowed = set()
435 439
            if shared:
436
                allowed.update([x.split('/', 2)[1] for x in self.permissions.access_list_shared(account)])
440
                allowed.update([x.split('/', 2)[1] for x in
441
                               self.permissions.access_list_shared(account)])
437 442
            if public:
438
                allowed.update([x[0].split('/', 2)[1] for x in self.permissions.public_list(account)])
443
                allowed.update([x[0].split('/', 2)[1] for x in
444
                               self.permissions.public_list(account)])
439 445
            allowed = sorted(allowed)
440 446
            start, limit = self._list_limits(allowed, marker, limit)
441 447
            return allowed[start:start + limit]
......
446 452
            [x[0] for x in containers], marker, limit)
447 453
        return containers[start:start + limit]
448 454

  
449
    def list_container_meta(self, user, account, container, domain, until=None):
450
        """Return a list with all the container's object meta keys for the domain."""
455
    def list_container_meta(self, user, account, container, domain,
456
                            until=None):
457
        """Return a list of the container's object meta keys for a domain."""
451 458

  
452 459
        logger.debug("list_container_meta: %s %s %s %s %s", user,
453 460
                     account, container, domain, until)
......
462 469
        path, node = self._lookup_container(account, container)
463 470
        before = until if until is not None else inf
464 471
        allowed = self._get_formatted_paths(allowed)
465
        return self.node.latest_attribute_keys(node, domain, before, CLUSTER_DELETED, allowed)
472
        return self.node.latest_attribute_keys(node, domain, before,
473
                                               CLUSTER_DELETED, allowed)
466 474

  
467
    def get_container_meta(self, user, account, container, domain, until=None, include_user_defined=True):
475
    def get_container_meta(self, user, account, container, domain, until=None,
476
                           include_user_defined=True):
468 477
        """Return a dictionary with the container metadata for the domain."""
469 478

  
470 479
        logger.debug("get_container_meta: %s %s %s %s %s", user,
471 480
                     account, container, domain, until)
472 481
        if user != account:
473
            if until or container not in self._allowed_containers(user, account):
482
            if until or container not in self._allowed_containers(user,
483
                                                                  account):
474 484
                raise NotAllowedError
475 485
        path, node = self._lookup_container(account, container)
476 486
        props = self._get_properties(node, until)
......
497 507
        meta.update({'modified': modified})
498 508
        return meta
499 509

  
500
    def update_container_meta(self, user, account, container, domain, meta, replace=False):
510
    def update_container_meta(self, user, account, container, domain, meta,
511
                              replace=False):
501 512
        """Update the metadata associated with the container for the domain."""
502 513

  
503 514
        logger.debug("update_container_meta: %s %s %s %s %s %s",
......
527 538
        path, node = self._lookup_container(account, container)
528 539
        return self._get_policy(node, is_account_policy=False)
529 540

  
530
    def update_container_policy(self, user, account, container, policy, replace=False):
541
    def update_container_policy(self, user, account, container, policy,
542
                                replace=False):
531 543
        """Update the policy associated with the container."""
532 544

  
533 545
        logger.debug("update_container_policy: %s %s %s %s %s",
......
560 572
            update_statistics_ancestors_depth=-1)
561 573
        self._put_policy(node, policy, True, is_account_policy=False)
562 574

  
563
    def delete_container(self, user, account, container, until=None, prefix='', delimiter=None):
575
    def delete_container(self, user, account, container, until=None, prefix='',
576
                         delimiter=None):
564 577
        """Delete/purge the container with the given name."""
565 578

  
566 579
        logger.debug("delete_container: %s %s %s %s %s %s", user,
......
580 593
            if not self.free_versioning:
581 594
                self._report_size_change(
582 595
                    user, account, -size, {
583
                        'action':'container purge',
596
                        'action': 'container purge',
584 597
                        'path': path,
585 598
                        'versions': ','.join(str(i) for i in serials)
586 599
                    }
......
601 614
            if not self.free_versioning:
602 615
                self._report_size_change(
603 616
                    user, account, -size, {
604
                        'action':'container purge',
617
                        'action': 'container purge',
605 618
                        'path': path,
606 619
                        'versions': ','.join(str(i) for i in serials)
607 620
                    }
608 621
                )
609 622
        else:
610 623
            # remove only contents
611
            src_names = self._list_objects_no_limit(user, account, container, prefix='', delimiter=None, virtual=False, domain=None, keys=[], shared=False, until=None, size_range=None, all_props=True, public=False)
624
            src_names = self._list_objects_no_limit(
625
                user, account, container, prefix='', delimiter=None,
626
                virtual=False, domain=None, keys=[], shared=False, until=None,
627
                size_range=None, all_props=True, public=False)
612 628
            paths = []
613 629
            for t in src_names:
614 630
                path = '/'.join((account, container, t[0]))
......
621 637
                    account, container, src_version_id,
622 638
                    update_statistics_ancestors_depth=1)
623 639
                self._report_size_change(
624
                        user, account, -del_size, {
625
                                'action': 'object delete',
626
                                'path': path,
627
                        'versions': ','.join([str(dest_version_id)])
628
                     }
629
                )
640
                    user, account, -del_size, {
641
                        'action': 'object delete',
642
                        'path': path,
643
                        'versions': ','.join([str(dest_version_id)])})
630 644
                self._report_object_change(
631 645
                    user, account, path, details={'action': 'object delete'})
632 646
                paths.append(path)
633 647
            self.permissions.access_clear_bulk(paths)
634 648

  
635
    def _list_objects(self, user, account, container, prefix, delimiter, marker, limit, virtual, domain, keys, shared, until, size_range, all_props, public):
649
    def _list_objects(self, user, account, container, prefix, delimiter,
650
                      marker, limit, virtual, domain, keys, shared, until,
651
                      size_range, all_props, public):
636 652
        if user != account and until:
637 653
            raise NotAllowedError
638 654
        if shared and public:
......
643 659
            if shared_paths:
644 660
                path, node = self._lookup_container(account, container)
645 661
                shared_paths = self._get_formatted_paths(shared_paths)
646
                objects |= set(self._list_object_properties(node, path, prefix, delimiter, marker, limit, virtual, domain, keys, until, size_range, shared_paths, all_props))
662
                objects |= set(self._list_object_properties(
663
                    node, path, prefix, delimiter, marker, limit, virtual,
664
                    domain, keys, until, size_range, shared_paths, all_props))
647 665

  
648 666
            # get public
649 667
            objects |= set(self._list_public_object_properties(
......
667 685
            return []
668 686
        path, node = self._lookup_container(account, container)
669 687
        allowed = self._get_formatted_paths(allowed)
670
        objects = self._list_object_properties(node, path, prefix, delimiter, marker, limit, virtual, domain, keys, until, size_range, allowed, all_props)
688
        objects = self._list_object_properties(
689
            node, path, prefix, delimiter, marker, limit, virtual, domain,
690
            keys, until, size_range, allowed, all_props)
671 691
        start, limit = self._list_limits(
672 692
            [x[0] for x in objects], marker, limit)
673 693
        return objects[start:start + limit]
674 694

  
675
    def _list_public_object_properties(self, user, account, container, prefix, all_props):
695
    def _list_public_object_properties(self, user, account, container, prefix,
696
                                       all_props):
676 697
        public = self._list_object_permissions(
677 698
            user, account, container, prefix, shared=False, public=True)
678 699
        paths, nodes = self._lookup_objects(public)
679 700
        path = '/'.join((account, container))
680 701
        cont_prefix = path + '/'
681 702
        paths = [x[len(cont_prefix):] for x in paths]
682
        props = self.node.version_lookup_bulk(nodes, all_props=all_props)
683
        objects = [(path,) + props for path, props in zip(paths, props)]
703
        objects = [(p,) + props for p, props in
704
                   zip(paths, self.node.version_lookup_bulk(
705
                       nodes, all_props=all_props))]
684 706
        return objects
685 707

  
686
    def _list_objects_no_limit(self, user, account, container, prefix, delimiter, virtual, domain, keys, shared, until, size_range, all_props, public):
708
    def _list_objects_no_limit(self, user, account, container, prefix,
709
                               delimiter, virtual, domain, keys, shared, until,
710
                               size_range, all_props, public):
687 711
        objects = []
688 712
        while True:
689 713
            marker = objects[-1] if objects else None
690 714
            limit = 10000
691
            l = self._list_objects(user, account, container, prefix, delimiter, marker, limit, virtual, domain, keys, shared, until, size_range, all_props, public)
715
            l = self._list_objects(
716
                user, account, container, prefix, delimiter, marker, limit,
717
                virtual, domain, keys, shared, until, size_range, all_props,
718
                public)
692 719
            objects.extend(l)
693 720
            if not l or len(l) < limit:
694 721
                break
695 722
        return objects
696 723

  
697
    def _list_object_permissions(self, user, account, container, prefix, shared, public):
724
    def _list_object_permissions(self, user, account, container, prefix,
725
                                 shared, public):
698 726
        allowed = []
699 727
        path = '/'.join((account, container, prefix)).rstrip('/')
700 728
        if user != account:
......
713 741
                return []
714 742
        return allowed
715 743

  
716
    def list_objects(self, user, account, container, prefix='', delimiter=None, marker=None, limit=10000, virtual=True, domain=None, keys=None, shared=False, until=None, size_range=None, public=False):
717
        """Return a list of object (name, version_id) tuples existing under a container."""
744
    def list_objects(self, user, account, container, prefix='', delimiter=None,
745
                     marker=None, limit=10000, virtual=True, domain=None,
746
                     keys=None, shared=False, until=None, size_range=None,
747
                     public=False):
748
        """List (object name, object version_id) under a container."""
718 749

  
719
        logger.debug("list_objects: %s %s %s %s %s %s %s %s %s %s %s %s %s %s", user, account, container, prefix, delimiter, marker, limit, virtual, domain, keys, shared, until, size_range, public)
750
        logger.debug("list_objects: %s %s %s %s %s %s %s %s %s %s %s %s %s %s",
751
                     user, account, container, prefix, delimiter, marker,
752
                     limit, virtual, domain, keys, shared, until, size_range,
753
                     public)
720 754
        keys = keys or []
721
        return self._list_objects(user, account, container, prefix, delimiter, marker, limit, virtual, domain, keys, shared, until, size_range, False, public)
755
        return self._list_objects(
756
            user, account, container, prefix, delimiter, marker, limit,
757
            virtual, domain, keys, shared, until, size_range, False, public)
722 758

  
723
    def list_object_meta(self, user, account, container, prefix='', delimiter=None, marker=None, limit=10000, virtual=True, domain=None, keys=None, shared=False, until=None, size_range=None, public=False):
724
        """Return a list of object metadata dicts existing under a container."""
759
    def list_object_meta(self, user, account, container, prefix='',
760
                         delimiter=None, marker=None, limit=10000,
761
                         virtual=True, domain=None, keys=None, shared=False,
762
                         until=None, size_range=None, public=False):
763
        """Return a list of metadata dicts of objects under a container."""
725 764

  
726
        logger.debug("list_object_meta: %s %s %s %s %s %s %s %s %s %s %s %s %s %s", user, account, container, prefix, delimiter, marker, limit, virtual, domain, keys, shared, until, size_range, public)
765
        logger.debug(
766
            "list_object_meta: %s %s %s %s %s %s %s %s %s %s %s %s %s %s",
767
            user, account, container, prefix, delimiter, marker, limit,
768
            virtual, domain, keys, shared, until, size_range, public)
727 769
        keys = keys or []
728
        props = self._list_objects(user, account, container, prefix, delimiter, marker, limit, virtual, domain, keys, shared, until, size_range, True, public)
770
        props = self._list_objects(
771
            user, account, container, prefix, delimiter, marker, limit,
772
            virtual, domain, keys, shared, until, size_range, True, public)
729 773
        objects = []
730 774
        for p in props:
731 775
            if len(p) == 2:
732 776
                objects.append({'subdir': p[0]})
733 777
            else:
734
                objects.append({'name': p[0],
735
                                'bytes': p[self.SIZE + 1],
736
                                'type': p[self.TYPE + 1],
737
                                'hash': p[self.HASH + 1],
738
                                'version': p[self.SERIAL + 1],
739
                                'version_timestamp': p[self.MTIME + 1],
740
                                'modified': p[self.MTIME + 1] if until is None else None,
741
                                'modified_by': p[self.MUSER + 1],
742
                                'uuid': p[self.UUID + 1],
743
                                'checksum': p[self.CHECKSUM + 1]})
778
                objects.append({
779
                    'name': p[0],
780
                    'bytes': p[self.SIZE + 1],
781
                    'type': p[self.TYPE + 1],
782
                    'hash': p[self.HASH + 1],
783
                    'version': p[self.SERIAL + 1],
784
                    'version_timestamp': p[self.MTIME + 1],
785
                    'modified': p[self.MTIME + 1] if until is None else None,
786
                    'modified_by': p[self.MUSER + 1],
787
                    'uuid': p[self.UUID + 1],
788
                    'checksum': p[self.CHECKSUM + 1]})
744 789
        return objects
745 790

  
746 791
    def list_object_permissions(self, user, account, container, prefix=''):
747
        """Return a list of paths that enforce permissions under a container."""
792
        """Return a list of paths enforce permissions under a container."""
748 793

  
749 794
        logger.debug("list_object_permissions: %s %s %s %s", user,
750 795
                     account, container, prefix)
751
        return self._list_object_permissions(user, account, container, prefix, True, False)
796
        return self._list_object_permissions(user, account, container, prefix,
797
                                             True, False)
752 798

  
753 799
    def list_object_public(self, user, account, container, prefix=''):
754
        """Return a dict mapping paths to public ids for objects that are public under a container."""
800
        """Return a mapping of object paths to public ids under a container."""
755 801

  
756 802
        logger.debug("list_object_public: %s %s %s %s", user,
757 803
                     account, container, prefix)
758 804
        public = {}
759
        for path, p in self.permissions.public_list('/'.join((account, container, prefix))):
805
        for path, p in self.permissions.public_list('/'.join((account,
806
                                                              container,
807
                                                              prefix))):
760 808
            public[path] = p
761 809
        return public
762 810

  
763
    def get_object_meta(self, user, account, container, name, domain, version=None, include_user_defined=True):
811
    def get_object_meta(self, user, account, container, name, domain,
812
                        version=None, include_user_defined=True):
764 813
        """Return a dictionary with the object metadata for the domain."""
765 814

  
766 815
        logger.debug("get_object_meta: %s %s %s %s %s %s", user,
......
797 846
                     'checksum': props[self.CHECKSUM]})
798 847
        return meta
799 848

  
800
    def update_object_meta(self, user, account, container, name, domain, meta, replace=False):
801
        """Update the metadata associated with the object for the domain and return the new version."""
849
    def update_object_meta(self, user, account, container, name, domain, meta,
850
                           replace=False):
851
        """Update object metadata for a domain and return the new version."""
802 852

  
803 853
        logger.debug("update_object_meta: %s %s %s %s %s %s %s",
804 854
                     user, account, container, name, domain, meta, replace)
......
821 871
        allowed = 'write'
822 872
        permissions_path = self._get_permissions_path(account, container, name)
823 873
        if user != account:
824
            if self.permissions.access_check(permissions_path, self.WRITE, user):
874
            if self.permissions.access_check(permissions_path, self.WRITE,
875
                                             user):
825 876
                allowed = 'write'
826
            elif self.permissions.access_check(permissions_path, self.READ, user):
877
            elif self.permissions.access_check(permissions_path, self.READ,
878
                                               user):
827 879
                allowed = 'read'
828 880
            else:
829 881
                raise NotAllowedError
830 882
        self._lookup_object(account, container, name)
831
        return (allowed, permissions_path, self.permissions.access_get(permissions_path))
883
        return (allowed,
884
                permissions_path,
885
                self.permissions.access_get(permissions_path))
832 886

  
833
    def update_object_permissions(self, user, account, container, name, permissions):
887
    def update_object_permissions(self, user, account, container, name,
888
                                  permissions):
834 889
        """Update the permissions associated with the object."""
835 890

  
836 891
        logger.debug("update_object_permissions: %s %s %s %s %s",
......
878 933
        hashmap = self.store.map_get(binascii.unhexlify(props[self.HASH]))
879 934
        return props[self.SIZE], [binascii.hexlify(x) for x in hashmap]
880 935

  
881
    def _update_object_hash(self, user, account, container, name, size, type, hash, checksum, domain, meta, replace_meta, permissions, src_node=None, src_version_id=None, is_copy=False):
936
    def _update_object_hash(self, user, account, container, name, size, type,
937
                            hash, checksum, domain, meta, replace_meta,
938
                            permissions, src_node=None, src_version_id=None,
939
                            is_copy=False):
882 940
        if permissions is not None and user != account:
883 941
            raise NotAllowedError
884 942
        self._can_write(user, account, container, name)
......
909 967
        if size_delta > 0:
910 968
            # Check account quota.
911 969
            if not self.using_external_quotaholder:
912
                account_quota = long(
913
                    self._get_policy(account_node, is_account_policy=True
914
                    )['quota']
915
                )
916
                account_usage = self._get_statistics(account_node, compute=True)[1]
970
                account_quota = long(self._get_policy(
971
                    account_node, is_account_policy=True)['quota'])
972
                account_usage = self._get_statistics(account_node,
973
                                                     compute=True)[1]
917 974
                if (account_quota > 0 and account_usage > account_quota):
918 975
                    raise QuotaError(
919 976
                        'Account quota exceeded: limit: %s, usage: %s' % (
920
                            account_quota, account_usage
921
                        )
922
                    )
977
                            account_quota, account_usage))
923 978

  
924 979
            # Check container quota.
925
            container_quota = long(
926
                self._get_policy(container_node, is_account_policy=False
927
                )['quota']
928
            )
980
            container_quota = long(self._get_policy(
981
                container_node, is_account_policy=False)['quota'])
929 982
            container_usage = self._get_statistics(container_node)[1]
930 983
            if (container_quota > 0 and container_usage > container_quota):
931 984
                # This must be executed in a transaction, so the version is
......
936 989
                    )
937 990
                )
938 991

  
939
        self._report_size_change(user, account, size_delta,
940
                                 {'action': 'object update', 'path': path,
941
                                  'versions': ','.join([str(dest_version_id)])})
992
        self._report_size_change(
993
            user, account, size_delta,
994
            {'action': 'object update', 'path': path,
995
             'versions': ','.join([str(dest_version_id)])})
942 996
        if permissions is not None:
943 997
            self.permissions.access_set(path, permissions)
944
            self._report_sharing_change(user, account, path, {'members': self.permissions.access_members(path)})
998
            self._report_sharing_change(
999
                user, account, path,
1000
                {'members': self.permissions.access_members(path)})
945 1001

  
946
        self._report_object_change(user, account, path, details={'version': dest_version_id, 'action': 'object update'})
1002
        self._report_object_change(
1003
            user, account, path,
1004
            details={'version': dest_version_id, 'action': 'object update'})
947 1005
        return dest_version_id
948 1006

  
949
    def update_object_hashmap(self, user, account, container, name, size, type, hashmap, checksum, domain, meta=None, replace_meta=False, permissions=None):
950
        """Create/update an object with the specified size and partial hashes."""
1007
    def update_object_hashmap(self, user, account, container, name, size, type,
1008
                              hashmap, checksum, domain, meta=None,
1009
                              replace_meta=False, permissions=None):
1010
        """Create/update an object's hashmap and return the new version."""
951 1011

  
952 1012
        logger.debug("update_object_hashmap: %s %s %s %s %s %s %s %s", user,
953 1013
                     account, container, name, size, type, hashmap, checksum)
......
964 1024

  
965 1025
        hash = map.hash()
966 1026
        hexlified = binascii.hexlify(hash)
967
        dest_version_id = self._update_object_hash(user, account, container, name, size, type, hexlified, checksum, domain, meta, replace_meta, permissions)
1027
        dest_version_id = self._update_object_hash(
1028
            user, account, container, name, size, type, hexlified, checksum,
1029
            domain, meta, replace_meta, permissions)
968 1030
        self.store.map_put(hash, map)
969 1031
        return dest_version_id, hexlified
970 1032

  
971
    def update_object_checksum(self, user, account, container, name, version, checksum):
1033
    def update_object_checksum(self, user, account, container, name, version,
1034
                               checksum):
972 1035
        """Update an object's checksum."""
973 1036

  
974 1037
        logger.debug("update_object_checksum: %s %s %s %s %s %s",
975 1038
                     user, account, container, name, version, checksum)
976
        # Update objects with greater version and same hashmap and size (fix metadata updates).
1039
        # Update objects with greater version and same hashmap
1040
        # and size (fix metadata updates).
977 1041
        self._can_write(user, account, container, name)
978 1042
        path, node = self._lookup_object(account, container, name)
979 1043
        props = self._get_version(node, version)
980 1044
        versions = self.node.node_get_versions(node)
981 1045
        for x in versions:
982
            if x[self.SERIAL] >= int(version) and x[self.HASH] == props[self.HASH] and x[self.SIZE] == props[self.SIZE]:
1046
            if (x[self.SERIAL] >= int(version) and
1047
                x[self.HASH] == props[self.HASH] and
1048
                    x[self.SIZE] == props[self.SIZE]):
983 1049
                self.node.version_put_property(
984 1050
                    x[self.SERIAL], 'checksum', checksum)
985 1051

  
986
    def _copy_object(self, user, src_account, src_container, src_name, dest_account, dest_container, dest_name, type, dest_domain=None, dest_meta=None, replace_meta=False, permissions=None, src_version=None, is_move=False, delimiter=None):
1052
    def _copy_object(self, user, src_account, src_container, src_name,
1053
                     dest_account, dest_container, dest_name, type,
1054
                     dest_domain=None, dest_meta=None, replace_meta=False,
1055
                     permissions=None, src_version=None, is_move=False,
1056
                     delimiter=None):
987 1057
        dest_meta = dest_meta or {}
988 1058
        dest_version_ids = []
989 1059
        self._can_read(user, src_account, src_container, src_name)
......
996 1066
        size = props[self.SIZE]
997 1067
        is_copy = not is_move and (src_account, src_container, src_name) != (
998 1068
            dest_account, dest_container, dest_name)  # New uuid.
999
        dest_version_ids.append(self._update_object_hash(user, dest_account, dest_container, dest_name, size, type, hash, None, dest_domain, dest_meta, replace_meta, permissions, src_node=node, src_version_id=src_version_id, is_copy=is_copy))
1000
        if is_move and (src_account, src_container, src_name) != (dest_account, dest_container, dest_name):
1069
        dest_version_ids.append(self._update_object_hash(
1070
            user, dest_account, dest_container, dest_name, size, type, hash,
1071
            None, dest_domain, dest_meta, replace_meta, permissions,
1072
            src_node=node, src_version_id=src_version_id, is_copy=is_copy))
1073
        if is_move and ((src_account, src_container, src_name) !=
1074
                        (dest_account, dest_container, dest_name)):
1001 1075
            self._delete_object(user, src_account, src_container, src_name)
1002 1076

  
1003 1077
        if delimiter:
1004
            prefix = src_name + \
1005
                delimiter if not src_name.endswith(delimiter) else src_name
1006
            src_names = self._list_objects_no_limit(user, src_account, src_container, prefix, delimiter=None, virtual=False, domain=None, keys=[], shared=False, until=None, size_range=None, all_props=True, public=False)
1078
            prefix = (src_name + delimiter if not
1079
                      src_name.endswith(delimiter) else src_name)
1080
            src_names = self._list_objects_no_limit(
1081
                user, src_account, src_container, prefix, delimiter=None,
1082
                virtual=False, domain=None, keys=[], shared=False, until=None,
1083
                size_range=None, all_props=True, public=False)
1007 1084
            src_names.sort(key=lambda x: x[2])  # order by nodes
1008 1085
            paths = [elem[0] for elem in src_names]
1009 1086
            nodes = [elem[2] for elem in src_names]
1010
            # TODO: Will do another fetch of the properties in duplicate version...
1087
            # TODO: Will do another fetch of the properties
1088
            # in duplicate version...
1011 1089
            props = self._get_versions(nodes)  # Check to see if source exists.
1012 1090

  
1013 1091
            for prop, path, node in zip(props, paths, nodes):
......
1018 1096
                dest_prefix = dest_name + delimiter if not dest_name.endswith(
1019 1097
                    delimiter) else dest_name
1020 1098
                vdest_name = path.replace(prefix, dest_prefix, 1)
1021
                dest_version_ids.append(self._update_object_hash(user, dest_account, dest_container, vdest_name, size, vtype, hash, None, dest_domain, meta={}, replace_meta=False, permissions=None, src_node=node, src_version_id=src_version_id, is_copy=is_copy))
1022
                if is_move and (src_account, src_container, src_name) != (dest_account, dest_container, dest_name):
1099
                dest_version_ids.append(self._update_object_hash(
1100
                    user, dest_account, dest_container, vdest_name, size,
1101
                    vtype, hash, None, dest_domain, meta={},
1102
                    replace_meta=False, permissions=None, src_node=node,
1103
                    src_version_id=src_version_id, is_copy=is_copy))
1104
                if is_move and ((src_account, src_container, src_name) !=
1105
                                (dest_account, dest_container, dest_name)):
1023 1106
                    self._delete_object(user, src_account, src_container, path)
1024
        return dest_version_ids[0] if len(dest_version_ids) == 1 else dest_version_ids
1107
        return (dest_version_ids[0] if len(dest_version_ids) == 1 else
1108
                dest_version_ids)
1025 1109

  
1026
    def copy_object(self, user, src_account, src_container, src_name, dest_account, dest_container, dest_name, type, domain, meta=None, replace_meta=False, permissions=None, src_version=None, delimiter=None):
1110
    def copy_object(self, user, src_account, src_container, src_name,
1111
                    dest_account, dest_container, dest_name, type, domain,
1112
                    meta=None, replace_meta=False, permissions=None,
1113
                    src_version=None, delimiter=None):
1027 1114
        """Copy an object's data and metadata."""
1028 1115

  
1029
        logger.debug("copy_object: %s %s %s %s %s %s %s %s %s %s %s %s %s %s", user, src_account, src_container, src_name, dest_account, dest_container, dest_name, type, domain, meta, replace_meta, permissions, src_version, delimiter)
1116
        logger.debug("copy_object: %s %s %s %s %s %s %s %s %s %s %s %s %s %s",
1117
                     user, src_account, src_container, src_name, dest_account,
1118
                     dest_container, dest_name, type, domain, meta,
1119
                     replace_meta, permissions, src_version, delimiter)
1030 1120
        meta = meta or {}
1031
        dest_version_id = self._copy_object(user, src_account, src_container, src_name, dest_account, dest_container, dest_name, type, domain, meta, replace_meta, permissions, src_version, False, delimiter)
1121
        dest_version_id = self._copy_object(
1122
            user, src_account, src_container, src_name, dest_account,
1123
            dest_container, dest_name, type, domain, meta, replace_meta,
1124
            permissions, src_version, False, delimiter)
1032 1125
        return dest_version_id
1033 1126

  
1034
    def move_object(self, user, src_account, src_container, src_name, dest_account, dest_container, dest_name, type, domain, meta=None, replace_meta=False, permissions=None, delimiter=None):
1127
    def move_object(self, user, src_account, src_container, src_name,
1128
                    dest_account, dest_container, dest_name, type, domain,
1129
                    meta=None, replace_meta=False, permissions=None,
1130
                    delimiter=None):
1035 1131
        """Move an object's data and metadata."""
1036 1132

  
1037
        logger.debug("move_object: %s %s %s %s %s %s %s %s %s %s %s %s %s", user, src_account, src_container, src_name, dest_account, dest_container, dest_name, type, domain, meta, replace_meta, permissions, delimiter)
1133
        logger.debug("move_object: %s %s %s %s %s %s %s %s %s %s %s %s %s",
1134
                     user, src_account, src_container, src_name, dest_account,
1135
                     dest_container, dest_name, type, domain, meta,
1136
                     replace_meta, permissions, delimiter)
1038 1137
        meta = meta or {}
1039 1138
        if user != src_account:
1040 1139
            raise NotAllowedError
1041
        dest_version_id = self._copy_object(user, src_account, src_container, src_name, dest_account, dest_container, dest_name, type, domain, meta, replace_meta, permissions, None, True, delimiter)
1140
        dest_version_id = self._copy_object(
1141
            user, src_account, src_container, src_name, dest_account,
1142
            dest_container, dest_name, type, domain, meta, replace_meta,
1143
            permissions, None, True, delimiter)
1042 1144
        return dest_version_id
1043 1145

  
1044
    def _delete_object(self, user, account, container, name, until=None, delimiter=None):
1146
    def _delete_object(self, user, account, container, name, until=None,
1147
                       delimiter=None):
1045 1148
        if user != account:
1046 1149
            raise NotAllowedError
1047 1150

  
......
1069 1172
            self.node.node_purge(node, until, CLUSTER_DELETED,
1070 1173
                                 update_statistics_ancestors_depth=1)
1071 1174
            try:
1072
                props = self._get_version(node)
1175
                self._get_version(node)
1073 1176
            except NameError:
1074 1177
                self.permissions.access_clear(path)
1075 1178
            self._report_size_change(
......
1087 1190
            cluster=CLUSTER_DELETED, update_statistics_ancestors_depth=1)
1088 1191
        del_size = self._apply_versioning(account, container, src_version_id,
1089 1192
                                          update_statistics_ancestors_depth=1)
1090
        self._report_size_change(user, account, -del_size,
1091
                                 {'action': 'object delete', 'path': path,
1092
                                  'versions': ','.join([str(dest_version_id)])})
1193
        self._report_size_change(
1194
            user, account, -del_size,
1195
            {'action': 'object delete',
1196
             'path': path,
1197
             'versions': ','.join([str(dest_version_id)])})
1093 1198
        self._report_object_change(
1094 1199
            user, account, path, details={'action': 'object delete'})
1095 1200
        self.permissions.access_clear(path)
1096 1201

  
1097 1202
        if delimiter:
1098 1203
            prefix = name + delimiter if not name.endswith(delimiter) else name
1099
            src_names = self._list_objects_no_limit(user, account, container, prefix, delimiter=None, virtual=False, domain=None, keys=[], shared=False, until=None, size_range=None, all_props=True, public=False)
1204
            src_names = self._list_objects_no_limit(
1205
                user, account, container, prefix, delimiter=None,
1206
                virtual=False, domain=None, keys=[], shared=False, until=None,
1207
                size_range=None, all_props=True, public=False)
1100 1208
            paths = []
1101 1209
            for t in src_names:
1102 1210
                path = '/'.join((account, container, t[0]))
......
1108 1216
                del_size = self._apply_versioning(
1109 1217
                    account, container, src_version_id,
1110 1218
                    update_statistics_ancestors_depth=1)
1111
                self._report_size_change(user, account, -del_size,
1112
                                         {'action': 'object delete',
1113
                                          'path': path,
1114
                                          'versions': ','.join([str(dest_version_id)])})
1219
                self._report_size_change(
1220
                    user, account, -del_size,
1221
                    {'action': 'object delete',
1222
                     'path': path,
1223
                     'versions': ','.join([str(dest_version_id)])})
1115 1224
                self._report_object_change(
1116 1225
                    user, account, path, details={'action': 'object delete'})
1117 1226
                paths.append(path)
1118 1227
            self.permissions.access_clear_bulk(paths)
1119 1228

  
1120
    def delete_object(self, user, account, container, name, until=None, prefix='', delimiter=None):
1229
    def delete_object(self, user, account, container, name, until=None,
1230
                      prefix='', delimiter=None):
1121 1231
        """Delete/purge an object."""
1122 1232

  
1123 1233
        logger.debug("delete_object: %s %s %s %s %s %s %s", user,
......
1125 1235
        self._delete_object(user, account, container, name, until, delimiter)
1126 1236

  
1127 1237
    def list_versions(self, user, account, container, name):
1128
        """Return a list of all (version, version_timestamp) tuples for an object."""
1238
        """Return a list of all object (version, version_timestamp) tuples."""
1129 1239

  
1130 1240
        logger.debug(
1131 1241
            "list_versions: %s %s %s %s", user, account, container, name)
1132 1242
        self._can_read(user, account, container, name)
1133 1243
        path, node = self._lookup_object(account, container, name)
1134 1244
        versions = self.node.node_get_versions(node)
1135
        return [[x[self.SERIAL], x[self.MTIME]] for x in versions if x[self.CLUSTER] != CLUSTER_DELETED]
1245
        return [[x[self.SERIAL], x[self.MTIME]] for x in versions if
1246
                x[self.CLUSTER] != CLUSTER_DELETED]
1136 1247

  
1137 1248
    def get_uuid(self, user, uuid):
1138 1249
        """Return the (account, container, name) for the UUID given."""
......
1241 1352
        return props
1242 1353

  
1243 1354
    def _get_statistics(self, node, until=None, compute=False):
1244
        """Return count, sum of size and latest timestamp of everything under node."""
1355
        """Return (count, sum of size, timestamp) of everything under node."""
1245 1356

  
1246 1357
        if until is not None:
1247 1358
            stats = self.node.statistics_latest(node, until, CLUSTER_DELETED)
1248 1359
        elif compute:
1249
            stats = self.node.statistics_latest(node, except_cluster=CLUSTER_DELETED)
1360
            stats = self.node.statistics_latest(node,
1361
                                                except_cluster=CLUSTER_DELETED)
1250 1362
        else:
1251 1363
            stats = self.node.statistics_get(node, CLUSTER_NORMAL)
1252 1364
        if stats is None:
......
1292 1404
            src_type = ''
1293 1405
            src_checksum = ''
1294 1406
        if size is None:  # Set metadata.
1295
            hash = src_hash  # This way hash can be set to None (account or container).
1407
            hash = src_hash  # This way hash can be set to None
1408
                             # (account or container).
1296 1409
            size = src_size
1297 1410
        if type is None:
1298 1411
            type = src_type
......
1340 1453

  
1341 1454
        src_version_id, dest_version_id = self._put_version_duplicate(
1342 1455
            user, node,
1343
            update_statistics_ancestors_depth=update_statistics_ancestors_depth)
1456
            update_statistics_ancestors_depth=
1457
            update_statistics_ancestors_depth)
1344 1458
        self._put_metadata_duplicate(
1345 1459
            src_version_id, dest_version_id, domain, node, meta, replace)
1346 1460
        return src_version_id, dest_version_id
......
1356 1470
            limit = 10000
1357 1471
        return start, limit
1358 1472

  
1359
    def _list_object_properties(self, parent, path, prefix='', delimiter=None, marker=None, limit=10000, virtual=True, domain=None, keys=None, until=None, size_range=None, allowed=None, all_props=False):
1473
    def _list_object_properties(self, parent, path, prefix='', delimiter=None,
1474
                                marker=None, limit=10000, virtual=True,
1475
                                domain=None, keys=None, until=None,
1476
                                size_range=None, allowed=None,
1477
                                all_props=False):
1360 1478
        keys = keys or []
1361 1479
        allowed = allowed or []
1362 1480
        cont_prefix = path + '/'
......
1366 1484
        filterq = keys if domain else []
1367 1485
        sizeq = size_range
1368 1486

  
1369
        objects, prefixes = self.node.latest_version_list(parent, prefix, delimiter, start, limit, before, CLUSTER_DELETED, allowed, domain, filterq, sizeq, all_props)
1487
        objects, prefixes = self.node.latest_version_list(
1488
            parent, prefix, delimiter, start, limit, before, CLUSTER_DELETED,
1489
            allowed, domain, filterq, sizeq, all_props)
1370 1490
        objects.extend([(p, None) for p in prefixes] if virtual else [])
1371 1491
        objects.sort(key=lambda x: x[0])
1372 1492
        objects = [(x[0][len(cont_prefix):],) + x[1:] for x in objects]
......
1385 1505
        details.update({'user': user, 'total': total})
1386 1506
        logger.debug(
1387 1507
            "_report_size_change: %s %s %s %s", user, account, size, details)
1388
        self.messages.append((QUEUE_MESSAGE_KEY_PREFIX % ('resource.diskspace',),
1389
                              account, QUEUE_INSTANCE_ID, 'diskspace',
1390
                              float(size), details))
1508
        self.messages.append(
1509
            (QUEUE_MESSAGE_KEY_PREFIX % ('resource.diskspace',),
1510
             account, QUEUE_INSTANCE_ID, 'diskspace', float(size), details))
1391 1511

  
1392 1512
        if not self.using_external_quotaholder:
1393 1513
            return
......
1399 1519
                holder=account,
1400 1520
                source=DEFAULT_SOURCE,
1401 1521
                provisions={'pithos.diskspace': size},
1402
                name=name
1403
                )
1522
                name=name)
1404 1523
        except BaseException, e:
1405 1524
            raise QuotaError(e)
1406 1525
        else:
......
1412 1531
        logger.debug("_report_object_change: %s %s %s %s", user,
1413 1532
                     account, path, details)
1414 1533
        self.messages.append((QUEUE_MESSAGE_KEY_PREFIX % ('object',),
1415
                              account, QUEUE_INSTANCE_ID, 'object', path, details))
1534
                              account, QUEUE_INSTANCE_ID, 'object', path,
1535
                              details))
1416 1536

  
1417 1537
    def _report_sharing_change(self, user, account, path, details=None):
1418 1538
        logger.debug("_report_permissions_change: %s %s %s %s",
......
1420 1540
        details = details or {}
1421 1541
        details.update({'user': user})
1422 1542
        self.messages.append((QUEUE_MESSAGE_KEY_PREFIX % ('sharing',),
1423
                              account, QUEUE_INSTANCE_ID, 'sharing', path, details))
1543
                              account, QUEUE_INSTANCE_ID, 'sharing', path,
1544
                              details))
1424 1545

  
1425 1546
    # Policy functions.
1426 1547

  
......
1496 1617
            if node is not None:
1497 1618
                props = self.node.version_lookup(node, inf, CLUSTER_NORMAL)
1498 1619
            if props is not None:
1499
                if props[self.TYPE].split(';', 1)[0].strip() in ('application/directory', 'application/folder'):
1620
                if props[self.TYPE].split(';', 1)[0].strip() in (
1621
                        'application/directory', 'application/folder'):
1500 1622
                    formatted.append((p.rstrip('/') + '/', self.MATCH_PREFIX))
1501 1623
                formatted.append((p, self.MATCH_EXACT))
1502 1624
        return formatted
......
1517 1639
                if node is not None:
1518 1640
                    props = self.node.version_lookup(node, inf, CLUSTER_NORMAL)
1519 1641
                if props is not None:
1520
                    if props[self.TYPE].split(';', 1)[0].strip() in ('application/directory', 'application/folder'):
1642
                    if props[self.TYPE].split(';', 1)[0].strip() in (
1643
                            'application/directory', 'application/folder'):
1521 1644
                        return p
1522 1645
        return None
1523 1646

  
......
1530 1653
        path = self._get_permissions_path(account, container, name)
1531 1654
        if not path:
1532 1655
            raise NotAllowedError
1533
        if not self.permissions.access_check(path, self.READ, user) and not self.permissions.access_check(path, self.WRITE, user):
1656
        if (not self.permissions.access_check(path, self.READ, user) and not
1657
                self.permissions.access_check(path, self.WRITE, user)):
1534 1658
            raise NotAllowedError
1535 1659

  
1536 1660
    def _can_write(self, user, account, container, name):
......
1581 1705
                'modified_by': props[self.MUSER],
1582 1706
                'uuid': props[self.UUID],
1583 1707
                'checksum': props[self.CHECKSUM]}
1584
        if include_user_defined and user_defined != None:
1708
        if include_user_defined and user_defined is not None:
1585 1709
            meta.update(user_defined)
1586 1710
        return meta
1587 1711

  

Also available in: Unified diff