Revision eb5e92f4 commissioning/hlapi/api.py

b/commissioning/hlapi/api.py
12 12
from util import make_abs_user_name
13 13
from util import reparent_child_name_under
14 14
from util import check_abs_resource_name
15
from util import check_abs_group_name
15 16
from util import parent_abs_name_of
16 17
from util import relative_child_name_under
17 18
from util import make_rel_global_resource_name
......
21 22
from util import check_attribute_name
22 23
from util import level_of_node
23 24
from util import check_abs_name
24
from commissioning.hlapi.util import check_relative_name
25
from util import check_relative_name
26
from util import is_abs_resource_name
27
from util import is_abs_group_name
28
from util import is_abs_user_name
29

  
25 30

  
26 31
class Quota(object):
27 32
    def __init__(self):
......
36 41
        self.returned = None
37 42
        self.released = None
38 43
        self.flags = Quota.default_flags()
39

  
40
    def is_unknown(self):
41
        return \
42
            self.entity is None and \
43
            self.resource is None and \
44
            self.quantity is None and \
45
            self.capacity is None and \
46
            self.import_limit is None and \
47
            self.export_limit is None and \
48
            self.returned is None and \
49
            self.released is None
44
        self.current_amount = self.__current_amount()
45
        
46
    
47
    def __current_amount(self):
48
        def num(x):
49
            if x is None:
50
                return 0
51
            else:
52
                return x
53
        
54
        return  num(self.quantity) + \
55
                num(self.imported) - \
56
                num(self.exported) + \
57
                num(self.returned) - \
58
                num(self.released)
59

  
60
#    def is_unknown(self):
61
#        return \
62
#            self.entity is None and \
63
#            self.resource is None and \
64
#            self.quantity is None and \
65
#            self.capacity is None and \
66
#            self.import_limit is None and \
67
#            self.export_limit is None and \
68
#            self.returned is None and \
69
#            self.released is None and \
70
#            (self.flags is None or self.flags == 0)
50 71
            
51 72
    @classmethod
52 73
    def default_flags(cls):
......
65 86
        q.exported = quota[7]
66 87
        q.returned = quota[8]
67 88
        q.released = quota[9]
89
        q.current_amount = q.__current_amount()
68 90
        q.flags = quota[10]
69 91
        return q
70 92
    
......
81 103
        q.exported = d.get('exported') or None
82 104
        q.returned = d.get('returned') or None
83 105
        q.released = d.get('released') or None
106
        q.current_amount = q.__current_amount()
84 107
        q.flags = d.get('flags') or Quota.default_flags()
85 108
        return q
86 109
        
......
95 118
                'exported': self.exported,
96 119
                'returned': self.returned,
97 120
                'released': self.released,
121
                'current_amount': self.current_amount,
98 122
                'flags': self.flags
99 123
                }
100 124

  
101 125
    def to_dict(self):
102
        d={}
103
        def add(key, value):
104
            if value is not None:
105
                d[key] = value
106
        add('entity', self.entity)
107
        add('resource', self.resource)
108
        add('quantity', self.quantity)
109
        add('capacity', self.capacity)
110
        add('import_limit', self.import_limit)
111
        add('export_limit', self.export_limit)
112
        add('imported', self.imported)
113
        add('exported', self.exported)
114
        add('returned', self.returned)
115
        add('released', self.released)
116
        add('flags', self.flags)
117
        return d
118

  
119
        
126
#        d={}
127
#        def add(key, value):
128
#            if value is not None:
129
#                d[key] = value
130
#        add('entity', self.entity)
131
#        add('resource', self.resource)
132
#        add('quantity', self.quantity)
133
#        add('capacity', self.capacity)
134
#        add('import_limit', self.import_limit)
135
#        add('export_limit', self.export_limit)
136
#        add('imported', self.imported)
137
#        add('exported', self.exported)
138
#        add('returned', self.returned)
139
#        add('released', self.released)
140
#        add('current_amount', self.current_amount)
141
#        if (self.flags is not None) and (self.flags != 0):
142
#            d['flags'] = self.flags
143
#        return d
144
        return self.to_full_dict()
145

  
146
        
147
    def get_attribute_value(self):
148
        # We always use capacity as the value of an attribute
149
        return self.capacity
150
    
120 151
    def __str__(self):
121 152
        return str(self.to_dict())
122 153
        
123 154
    def __repr__(self):
124 155
        return self.__str__()
125
    
126

  
156
        
127 157
class HighLevelAPI(object):
128 158
    """
129 159
    High-level Quota Holder API that supports definitions of resources,
......
236 266
                
237 267
        abs_resource_name = make_abs_resource_name(resource_name)
238 268
                
239
        computed_attribute_name = '%s_%s' % (attribute_name,
240
                                             abs_resource_name)
269
        computed_attribute_name = self.make_full_attribute_name(
270
            attribute_name,
271
            node_name,
272
            abs_resource_name)
241 273
        
242 274
        return self.set_quota(
243 275
            entity=node_name,
......
299 331
                              capacity,
300 332
                              import_limit,
301 333
                              export_limit,
302
                              None, None, None, None,
334
                              0, 0, 0, 0,
303 335
                              flags))
304 336
        return q
305 337
        
306 338
    
307
    def qh_create_entity(self, entity, owner, key='', owner_key=''):
308
        key = key or self.__node_keys.get(entity) or ''
309
        owner_key = key or self.__node_keys.get(owner) or ''
339
    def qh_create_entity(self,
340
                         entity,
341
                         owner,
342
                         key='',
343
                         owner_key='',
344
                         entity_label='entity'):
345
        key = self.get_cached_node_key(entity, entity_label)
346
        owner_key = self.get_cached_node_key(owner)
347
        
310 348
        rejected = self.__qh.create_entity(context=self.__context,
311 349
                                    create_entity=[(entity,
312 350
                                                    owner,
313 351
                                                    key,
314 352
                                                    owner_key)])
315 353
        if len(rejected) > 0:
316
            raise Exception("Could not create entity '%s' under '%s'" % (
317
                    entity, owner))
354
            raise Exception("Could not create %s='%s' under '%s'. Probably it already exists?" % (
355
                    entity_label, entity, owner))
318 356
        return entity
319 357
        
358
        
320 359
    def qh_get_entity(self, entity, key=''):
321 360
        key = key or self.__node_keys.get(entity) or ''
322 361
        entity_owners = self.__qh.get_entity(context=self.__context,
......
371 410
    #+++##########################################
372 411

  
373 412
    def get_quota(self, entity, resource):
413
        check_abs_name(entity, 'entity')
374 414
        return self.qh_get_quota(entity,
375 415
                                 resource,
376 416
                                 self.get_cached_node_key(entity))
......
379 419
    def set_quota(self, entity, resource,
380 420
                     quantity, capacity,
381 421
                     import_limit, export_limit, flags):
422
        check_abs_name(entity)
382 423
        return self.qh_set_quota(entity=entity,
383 424
                                 resource=resource,
384 425
                                 key=self.get_cached_node_key(entity),
......
393 434
        check_abs_name(target_entity, 'target_entity')
394 435
        check_abs_name(source_entity, 'source_entity')
395 436
        
396
        return self.qh_issue_one_commission(
397
            target_entity=target_entity,
398
            target_entity_key=self.get_cached_node_key(target_entity),
399
            owner='', # We ignore the owner. Everything must exist
400
            owner_key='',
401
            source_entity=source_entity,
402
            resource=resource,
403
            quantity=quantity)
437
#        owner=parent_abs_name_of(target_entity, 'parent_target_entity')
438
#        owner_key = self.get_cached_node_key(owner, 'owner')
439
        owner = '' # Ignore the owner here. Everything must be set up OK
440
        owner_key = ''
441
        try:
442
            return self.qh_issue_one_commission(
443
                target_entity=target_entity,
444
                target_entity_key=self.get_cached_node_key(target_entity),
445
                owner=owner,
446
                owner_key=owner_key,
447
                source_entity=source_entity,
448
                resource=resource,
449
                quantity=quantity)
450
        except Exception, e:
451
            raise Exception(
452
                    "Could not transfer %s from '%s' to '%s' for resource '%s'. Original error is %s: %s" % (
453
                        quantity, source_entity, target_entity, resource,
454
                        type(e).__name__, str(e)))
455

  
404 456

  
405 457
    def get_cached_node_key(self, node_name, node_label='node_name'):
406 458
        check_abs_name(node_name, node_label)
......
436 488
        return self.get_node_children(NameOfSystemNode)
437 489
    
438 490
        
439
    def get_global_resources(self):
491
    def get_resources(self):
440 492
        self.ensure_resources_node()
441 493
        return self.get_node_children(NameOfResourcesNode)
442 494
    
......
452 504
    
453 505

  
454 506
    def ensure_node(self, abs_node_name, label='abs_node_name'):
455
        if not self.has_node(abs_node_name, label):
507
        if not self.node_exists(abs_node_name, label):
456 508
            return self.create_node(abs_node_name, label)
457 509
        else:
458 510
            return abs_node_name
......
485 537
        return self.ensure_node(NameOfUsersNode, 'NameOfGroupsNode')
486 538

  
487 539

  
488
    def has_node(self, abs_node_name, label='abs_node_name'):
540
    def node_exists(self, abs_node_name, label='abs_node_name'):
489 541
        """
490
        Checks if an entity with the absolute name ``abs_node_name`` exists.
542
        Checks if a node with the absolute name ``abs_node_name`` exists.
491 543
        
492 544
        Returns ``True``/``False`` accordingly.
493 545
        """
......
500 552
        return len(entity_owner_list) == 1 # TODO: any other check here?
501 553

  
502 554

  
503
    def has_group(self, group_name):
555
    def group_exists(self, group_name):
504 556
        abs_group_name = make_abs_group_name(group_name)
505
        return self.has_node(abs_group_name, 'abs_group_name')
557
        return self.node_exists(abs_group_name, 'abs_group_name')
506 558

  
507 559

  
508
    def has_global_resource(self, resource_name):
560
    def resource_exists(self, resource_name):
509 561
        abs_resource_name = make_abs_resource_name(resource_name)
510
        return self.has_node(abs_resource_name, 'abs_resource_name')
562
        return self.node_exists(abs_resource_name, 'abs_resource_name')
511 563

  
512 564

  
513
    def has_user(self, user_name):
565
    def user_exists(self, user_name):
514 566
        abs_user_name = make_abs_user_name(user_name)
515
        return self.has_node(abs_user_name, 'abs_user_name')
567
        return self.node_exists(abs_user_name, 'abs_user_name')
516 568
    
517 569
    
518 570
    def create_node(self, node_name, node_label):
......
526 578
        
527 579
        The implementation maps a node to a Quota Holder entity.
528 580
        """
529
        print "Creating %s=%s" % (node_label, node_name)
581
#        print "Creating %s=%s" % (node_label, node_name)
530 582
        check_abs_name(node_name, node_label)
531 583
        
532 584
        if is_system_node(node_name):
......
540 592

  
541 593
        node_key = self.get_cached_node_key(node_name)
542 594
        parent_node_key = self.get_cached_node_key(parent_node_name)
595
        
596
        return self.qh_create_entity(entity=node_name,
597
                                     owner=parent_node_name,
598
                                     key=node_key,
599
                                     owner_key=parent_node_key,
600
                                     entity_label=node_label)
543 601

  
544
        rejected = self.__qh.create_entity(
545
            context=self.__context,
546
            create_entity=[
547
                (
548
                    node_name,
549
                    parent_node_name,
550
                    node_key,
551
                    parent_node_key
552
                    )
553
            ]
554
        )
555
        if len(rejected) > 0:
556
            raise Exception("Could not create %s='%s'. Maybe it already exists? Is 'system' entity defined?" % (node_label, node_name,))
602

  
603
    def make_full_attribute_name(self, attribute_name, parent_node, for_node=''):
604
        if (for_node == '') or (for_node is None):
605
            for_node = parent_node
606
        check_attribute_name(attribute_name)
607
        check_abs_name(parent_node, 'parent_node')        
608
        check_abs_name(for_node, 'attribute_for_node')
609
        
610
        if parent_node == for_node:
611
            return "%s::%s" % (attribute_name, parent_node)
557 612
        else:
558
            return node_name
613
            return "%s::%s::%s" % (attribute_name, parent_node, for_node)
614

  
615

  
616
    def split_full_attribute_name(self, name):
617
        data = name.split("::")
618
        if len(data) == 2:
619
            return (data[0], data[1], data[1])
620
        elif len(data) == 3:
621
            return (data[0], data[1], data[2])
622
        else:
623
            raise Exception("Bad form of attribute name: '%s'" % (name))
624

  
625

  
626
    def get_resource_info(self, name):
627
        if not self.resource_exists(name):
628
            raise Exception("Unknown resource '%s'" % (name))
629
        
630
        abs_resource_name = make_abs_resource_name(name)
631
        
632
        
633
        resource_quota = self.get_quota(entity=abs_resource_name,
634
                                        resource=abs_resource_name)
635
        
636
        rdef_user_quota_name = self.make_full_attribute_name(
637
            'user', abs_resource_name)
638
        rdef_user_quota = self.get_quota(
639
            entity=abs_resource_name, resource=rdef_user_quota_name)
559 640

  
641
        rdef_user_max_quota_name = self.make_full_attribute_name(
642
            'user_max', abs_resource_name)
643
        rdef_user_max_quota = self.get_quota(
644
            entity=abs_resource_name, resource=rdef_user_max_quota_name)
560 645

  
646
        rdef_group_max_quota_name = self.make_full_attribute_name(
647
            'group_max', abs_resource_name)
648
        rdef_group_max_quota = self.get_quota(
649
            entity=abs_resource_name, resource=rdef_group_max_quota_name)
650
        
651
        return {'resource_quota': resource_quota,
652
                'user_quota': rdef_user_quota,
653
                'user_max_quota': rdef_user_max_quota,
654
                'group_max_quota': rdef_group_max_quota}
655
        
561 656
    def define_resource(self,
562 657
                        name,
563 658
                        total_quota,
......
574 669
        (so this is equivalent to a node in the high-level API but an extra
575 670
        check is made to ensure the resource is under 'system/resources').
576 671
        """
672
        ## TODO: Check total_quota is greater than the other quotas.
673
        ## TODO: Check user_max_quota >= user_quota
674

  
577 675
        # If the name is not in absolute form, make it so.
578 676
        # E.g. if it is 'pithos+' make it 'system/resources/pithos+'
579 677
        abs_resource_name = make_abs_resource_name(name)
......
586 684
        # absolute name
587 685
        resource_quota = self.set_quota(entity=abs_resource_name,
588 686
            resource=abs_resource_name,
589
            quantity=0,
590
            capacity=total_quota,
687
            quantity=total_quota,
688
            capacity=None, # Grow to infinity
591 689
            import_limit=None,
592 690
            export_limit=None,
593 691
            flags=0)
......
620 718
            flags=0)
621 719
        
622 720

  
623
        return [resource_quota,
624
                rdef_user_quota,
625
                rdef_user_max_quota,
626
                rdef_group_max_quota] 
721
        return {'resource_quota': resource_quota,
722
                'user_quota': rdef_user_quota,
723
                'user_max_quota': rdef_user_max_quota,
724
                'group_max_quota': rdef_group_max_quota
725
                }
627 726
        
628 727
    
629 728
    def define_attribute_of_resource(self,
......
647 746
        the node (Quota Holder entity) named ``global_resource_name``. The
648 747
        respective value is defined via Quota Holder quotas. 
649 748
        """
650
        check_attribute_name(attribute_name)
651
        
652 749
        return self.__create_attribute_of_node_for_resource(
653 750
            node_name=abs_resource_name,
654 751
            node_label='abs_resource_name',
......
657 754
            # The attribute name goes next
658 755
            # The absolute resource name goes next
659 756
            #    (will be added by the called method)
660
            attribute_name='rdef_%s' % (attribute_name),
757
            attribute_name=attribute_name,
661 758
            quantity=quantity,
662 759
            capacity=capacity,
663 760
            import_limit=import_limit,
......
666 763

  
667 764
            
668 765
    
669
    def define_group(self, group_name, group_node_key=''):
766
    def define_group(self, group_name):
670 767
        """
671 768
        Creates a new group under 'system/groups'.
672 769

  
......
680 777
        most authoritative value instead of the one passed as a parameter.
681 778

  
682 779
        No further resource assignment is done, you must use
683
        ``define_group_resource``.
780
        ``define_resource_of_group``.
684 781

  
685 782
        The implementation maps a group to a Quota Holder entity.
686 783
        """
687
        check_node_key(group_node_key)
688

  
689 784
        self.ensure_groups_node()
690 785
        # get name in absolute form
691 786
        abs_group_name = make_abs_group_name(group_name)
692

  
787
        
693 788
        # Create hierarchy on demand
694 789
        self.create_node(abs_group_name, 'abs_group_name')
695
        self.set_cached_node_key(abs_group_name, group_node_key)
696 790
        return abs_group_name
697 791
    
698 792

  
699 793
    
700 794
    def define_attribute_of_group_for_resource(self,
701
                                               group_name,
795
                                               abs_group_name,
796
                                               abs_resource_name,
702 797
                                               attribute_name,
703
                                               resource_name,
704 798
                                               quantity,
705 799
                                               capacity,
706 800
                                               import_limit,
707 801
                                               export_limit,
708 802
                                               flags):
803
        check_abs_group_name(abs_group_name)
804
        check_abs_resource_name(abs_resource_name)
709 805
        
710 806
        return self.__create_attribute_of_node_for_resource(
711
            node_name=group_name,
712
            intended_parent_node_name=NameOfGroupsNode,
713
            resource_name=resource_name,
714
            # g means we define for G
715
            # The attribute name goes next
716
            # The relative resource name goes next
717
            #    (will be added by the called method)
718
            attribute_name='g_%s' % (attribute_name),
807
            node_name=abs_group_name,
808
            node_label='abs_group_name',
809
            resource_name=abs_resource_name,
810
            attribute_name=attribute_name,
719 811
            quantity=quantity,
720 812
            capacity=capacity,
721 813
            import_limit=import_limit,
722 814
            export_limit=export_limit,
723 815
            flags=flags)
816
        
724 817

  
725

  
726
    def define_group_resource(self,
727
                              group_name,
728
                              resource_name,
729
                              group_quota,
730
                              group_import_limit,
731
                              group_export_limit,
732
                              group_flags,
733
                              per_user_quota,
734
                              operational_quantity,
735
                              operational_capacity,
736
                              operational_import_limit,
737
                              operational_export_limit,
738
                              operational_flags):
818
    def define_resource_of_group(self,
819
                                 group_name,
820
                                 resource_name,
821
                                 # How much the group gets in total
822
                                 resource_amount,
823
                                 # How much the group gives to the user
824
                                 user_quota,
825
                                 resource_amount_is_ondemand = False):
739 826
        """
740 827
        Defines a resource that a group provides to its users.
741 828
        """
742 829
        abs_group_name = make_abs_group_name(group_name)
743 830
        abs_resource_name = make_abs_resource_name(resource_name)
744 831
        
745
        if not self.has_global_resource(abs_resource_name):
832
        # Not implemented yet
833
        if resource_amount_is_ondemand:
834
            raise Exception(
835
                    "On demand group capacity is not supported for group %s" % (
836
                        group_name))
837
        
838
        if not self.resource_exists(abs_resource_name):
746 839
            raise Exception(
747 840
                "Cannot define resource '%s' for group '%s' because the global resource '%s' does not exist" %(
748 841
                    resource_name,
749 842
                    group_name,
750 843
                    abs_resource_name))
751 844
            
752
        if not self.has_node(abs_group_name):
845
        if not self.node_exists(abs_group_name):
753 846
            raise Exception(
754 847
                "Cannot define resource '%s' for group '%s' because the group does not exist" %(
755 848
                    resource_name,
756 849
                    group_name))
757 850

  
758 851
        # Define the resource quotas for the group (initially empty)
759
        # and do the quota transfer from the global resource
760 852
        group_resource_quota = self.set_quota(
761 853
            entity=abs_group_name,
762 854
            resource=abs_resource_name,
763 855
            quantity=0,
764
            capacity=0,
765
            import_limit=group_import_limit,
766
            export_limit=group_export_limit,
767
            flags=group_flags)
768

  
856
            capacity=resource_amount,
857
            import_limit=None,
858
            export_limit=None,
859
            flags=0)
860
        # ... and do the quota transfer from the resource node
769 861
        self.issue_one_commission(
770 862
            target_entity=abs_group_name,
771 863
            source_entity=abs_resource_name,
772 864
            resource=abs_resource_name,
773
            quantity=group_quota)
774
        
775
        return self.get_quota(entity=abs_group_name,
776
                              resource=abs_resource_name)
865
            quantity=resource_amount)
866
        
867
        # Other definitions
868
        user_quota = self.define_attribute_of_group_for_resource(
869
                        abs_group_name=abs_group_name,
870
                        abs_resource_name=abs_resource_name,
871
                        attribute_name='user',
872
                        quantity=0,
873
                        capacity=user_quota,
874
                        import_limit=0,
875
                        export_limit=0,
876
                        flags=0)
877
        
878
        return {'group_resource_quota': group_resource_quota,
879
                'user_quota': user_quota}
777 880

  
778 881

  
779 882
    #+++##########################################

Also available in: Unified diff