Revision 08ce0e6f

b/snf-common/synnefo/lib/quotaholder/api/quotaholder.py
72 72
Resource            =   Name(classname='Resource')
73 73
Policy              =   Name(classname='Policy')
74 74

  
75
Quantity            =   Integer(classname='Quantity', null=True)
76
Capacity            =   Nonnegative(classname='Capacity', null=True)
77
ImportLimit         =   Nonnegative(classname='ImportLimit', null=True)
78
ExportLimit         =   Nonnegative(classname='ExportLimit', null=True)
79
QuantityDelta       =   Integer(classname='QuantityDelta', null=True)
80
CapacityDelta       =   Integer(classname='CapacityDelta', null=True)
81
ImportLimitDelta    =   Integer(classname='ImportLimitDelta', null=True)
82
ExportLimitDelta    =   Integer(classname='ExportLimitDelta', null=True)
75
Quantity            =   Integer(classname='Quantity')
76
Capacity            =   Nonnegative(classname='Capacity')
77
ImportLimit         =   Nonnegative(classname='ImportLimit')
78
ExportLimit         =   Nonnegative(classname='ExportLimit')
79
QuantityDelta       =   Integer(classname='QuantityDelta')
80
CapacityDelta       =   Integer(classname='CapacityDelta')
81
ImportLimitDelta    =   Integer(classname='ImportLimitDelta')
82
ExportLimitDelta    =   Integer(classname='ExportLimitDelta')
83 83
Imported            =   Nonnegative(classname='Imported')
84 84
Exported            =   Nonnegative(classname='Exported')
85 85
Returned            =   Nonnegative(classname='Returned')
b/snf-quotaholder-app/quotaholder_django/quotaholder_app/callpoint.py
673 673

  
674 674
            hp = h.policy
675 675

  
676
            if hp.export_limit is not None:
677
                current = h.exporting
678
                limit = hp.export_limit
679
                if current + quantity > limit:
680
                    m = ("Export limit reached for %s.%s" % (entity, resource))
681
                    raise ExportLimitError(m,
682
                                           source=entity, target=target,
683
                                           resource=resource, requested=quantity,
684
                                           current=current, limit=limit)
685

  
686
            if hp.quantity is not None:
687
                limit = hp.quantity
688
                current = (+ h.exporting + h.releasing
689
                           - h.imported - h.returned)
690
                if current + quantity > limit:
691
                    m = ("There is not enough quantity "
692
                         "to allocate from in %s.%s" % (entity, resource))
693
                    raise NoQuantityError(m,
694
                                          source=entity, target=target,
695
                                          resource=resource, requested=quantity,
696
                                          current=current, limit=limit)
676
            current = h.exporting
677
            limit = hp.export_limit
678
            if current + quantity > limit:
679
                m = ("Export limit reached for %s.%s" % (entity, resource))
680
                raise ExportLimitError(m,
681
                                       source=entity, target=target,
682
                                       resource=resource, requested=quantity,
683
                                       current=current, limit=limit)
684

  
685
            limit = hp.quantity
686
            current = (+ h.exporting + h.releasing
687
                       - h.imported - h.returned)
688
            if current + quantity > limit:
689
                m = ("There is not enough quantity "
690
                     "to allocate from in %s.%s" % (entity, resource))
691
                raise NoQuantityError(m,
692
                                      source=entity, target=target,
693
                                      resource=resource, requested=quantity,
694
                                      current=current, limit=limit)
697 695

  
698 696
            # Target limits checks
699 697
            try:
......
709 707

  
710 708
            tp = th.policy
711 709

  
712
            if tp.import_limit is not None:
713
                limit = tp.import_limit
714
                current = th.importing
715
                if current + quantity > limit:
716
                    m = ("Import limit reached for %s.%s" % (target, resource))
717
                    raise ImportLimitError(m,
718
                                           source=entity, target=target,
719
                                           resource=resource, requested=quantity,
720
                                           current=current, limit=limit)
721

  
722
            if tp.capacity is not None:
723
                limit = tp.capacity
724
                current = (+ th.importing + th.returning
725
                           - th.exported - th.released)
726

  
727
                if current + quantity > limit:
728
                        m = ("There is not enough capacity "
729
                             "to allocate into in %s.%s" % (target, resource))
730
                        raise NoCapacityError(m,
731
                                              source=entity, target=target,
732
                                              resource=resource, requested=quantity,
733
                                              current=current, limit=limit)
710
            limit = tp.import_limit
711
            current = th.importing
712
            if current + quantity > limit:
713
                m = ("Import limit reached for %s.%s" % (target, resource))
714
                raise ImportLimitError(m,
715
                                       source=entity, target=target,
716
                                       resource=resource, requested=quantity,
717
                                       current=current, limit=limit)
718

  
719
            limit = tp.capacity
720
            current = (+ th.importing + th.returning
721
                       - th.exported - th.released)
722

  
723
            if current + quantity > limit:
724
                    m = ("There is not enough capacity "
725
                         "to allocate into in %s.%s" % (target, resource))
726
                    raise NoCapacityError(m,
727
                                          source=entity, target=target,
728
                                          resource=resource, requested=quantity,
729
                                          current=current, limit=limit)
734 730

  
735 731
            Provision.objects.create(   serial      =   commission,
736 732
                                        entity      =   e,
......
998 994
        return timeline
999 995

  
1000 996
def _add(x, y, invert=False):
1001
    if invert and y is None:
1002
        return 0
1003
    if x is None or y is None:
1004
        return None
1005 997
    return x + y if not invert else x - y
1006 998

  
1007 999
def _update(dest, source, attr, delta):
......
1009 1001
    dest_attr = _add(getattr(source, attr, 0), delta)
1010 1002

  
1011 1003
def _isneg(x):
1012
    if x is None:
1013
        return False
1014 1004
    return x < 0
1015 1005

  
1016 1006
API_Callpoint = QuotaholderDjangoDBCallpoint
b/snf-quotaholder-app/quotaholder_django/quotaholder_app/models.py
35 35
from synnefo.lib.commissioning import CorruptedError
36 36

  
37 37
from django.db.models import (Model, BigIntegerField, CharField,
38
                              ForeignKey, AutoField)
38
                              ForeignKey, AutoField, DecimalField)
39 39
from django.db import transaction
40 40
from .managers import ForUpdateManager
41 41

  
42
DECIMAL_DIGITS  =   38
43
QH_MAX_INT      =   10**32
44

  
45
def decimalField(**kwargs):
46
    return DecimalField(max_digits=DECIMAL_DIGITS, decimal_places=0, **kwargs)
47

  
42 48
class Holder(Model):
43 49

  
44 50
    attribute   =   CharField(max_length=4096, primary_key=True)
......
59 65
class Policy(Model):
60 66

  
61 67
    policy          =   CharField(max_length=4096, primary_key=True)
62
    quantity        =   BigIntegerField(null=True, default=None)
63
    capacity        =   BigIntegerField(null=True,  default=None)
64
    import_limit    =   BigIntegerField(null=True,  default=None)
65
    export_limit    =   BigIntegerField(null=True,  default=None)
68
    quantity        =   decimalField()
69
    capacity        =   decimalField()
70
    import_limit    =   decimalField()
71
    export_limit    =   decimalField()
66 72

  
67 73
    objects     =   ForUpdateManager()
68 74

  
......
74 80
    policy      =   ForeignKey(Policy, to_field='policy')
75 81
    flags       =   BigIntegerField(null=False, default=0)
76 82

  
77
    imported    =   BigIntegerField(null=False, default=0)
78
    importing   =   BigIntegerField(null=False, default=0)
79
    exported    =   BigIntegerField(null=False, default=0)
80
    exporting   =   BigIntegerField(null=False, default=0)
81
    returned    =   BigIntegerField(null=False, default=0)
82
    returning   =   BigIntegerField(null=False, default=0)
83
    released    =   BigIntegerField(null=False, default=0)
84
    releasing   =   BigIntegerField(null=False, default=0)
83
    imported    =   decimalField(default=0)
84
    importing   =   decimalField(default=0)
85
    exported    =   decimalField(default=0)
86
    exporting   =   decimalField(default=0)
87
    returned    =   decimalField(default=0)
88
    returning   =   decimalField(default=0)
89
    released    =   decimalField(default=0)
90
    releasing   =   decimalField(default=0)
85 91

  
86 92
    objects     =   ForUpdateManager()
87 93

  
......
113 119

  
114 120
    entity      =   ForeignKey(Entity, to_field='entity')
115 121
    resource    =   CharField(max_length=4096, null=False)
116
    quantity    =   BigIntegerField(null=False)
122
    quantity    =   decimalField()
117 123

  
118 124
    objects     =   ForUpdateManager()
119 125

  
......
126 132
    issue_time          =   CharField(max_length=4096)
127 133
    log_time            =   CharField(max_length=4096)
128 134
    resource            =   CharField(max_length=4096)
129
    source_quantity     =   BigIntegerField(null=True)
130
    source_capacity     =   BigIntegerField(null=True)
131
    source_import_limit =   BigIntegerField(null=True)
132
    source_export_limit =   BigIntegerField(null=True)
133
    source_imported     =   BigIntegerField(null=False)
134
    source_exported     =   BigIntegerField(null=False)
135
    source_returned     =   BigIntegerField(null=False)
136
    source_released     =   BigIntegerField(null=False)
137
    target_quantity     =   BigIntegerField(null=True)
138
    target_capacity     =   BigIntegerField(null=True)
139
    target_import_limit =   BigIntegerField(null=True)
140
    target_export_limit =   BigIntegerField(null=True)
141
    target_imported     =   BigIntegerField(null=False)
142
    target_exported     =   BigIntegerField(null=False)
143
    target_returned     =   BigIntegerField(null=False)
144
    target_released     =   BigIntegerField(null=False)
145
    delta_quantity      =   BigIntegerField(null=False)
135
    source_quantity     =   decimalField()
136
    source_capacity     =   decimalField()
137
    source_import_limit =   decimalField()
138
    source_export_limit =   decimalField()
139
    source_imported     =   decimalField()
140
    source_exported     =   decimalField()
141
    source_returned     =   decimalField()
142
    source_released     =   decimalField()
143
    target_quantity     =   decimalField()
144
    target_capacity     =   decimalField()
145
    target_import_limit =   decimalField()
146
    target_export_limit =   decimalField()
147
    target_imported     =   decimalField()
148
    target_exported     =   decimalField()
149
    target_returned     =   decimalField()
150
    target_released     =   decimalField()
151
    delta_quantity      =   decimalField()
146 152
    reason              =   CharField(max_length=4096)
147 153

  
148 154
    objects     =   ForUpdateManager()
b/snf-quotaholder-app/quotaholder_django/test/simpletests.py
47 47
    Name, Key, Quantity, Capacity, ImportLimit, ExportLimit, Resource, Flags,
48 48
    Imported, Exported, Returned, Released)
49 49

  
50
QH_MAX_INT = 10**32
51

  
50 52
DEFAULT_HOLDING = (0, 0, 0, 0)
51 53

  
52 54
class QHAPITest(QHTestCase):
......
236 238
        resource1 = self.rand_resource()
237 239

  
238 240
        r = self.qh.set_quota(
239
            set_quota=[(e0, resource0, k0) + (5, None, 5, 6) + (0,),
241
            set_quota=[(e0, resource0, k0) + (5, QH_MAX_INT, 5, 6) + (0,),
240 242
                       (e1, resource0, k1) + (5, 5, 5, 5) + (0,)])
241 243
        self.assertEqual(r, [])
242 244

  
243 245
        r = self.qh.add_quota(clientkey=self.client,
244 246
                              serial=1,
245
                              sub_quota=[(e0, resource0, k0, 0, None, 1, 1)],
246
                              add_quota=[(e0, resource0, k0, 0, 3, None, 0),
247
                              sub_quota=[(e0, resource0, k0, 0, QH_MAX_INT, 1, 1)],
248
                              add_quota=[(e0, resource0, k0, 0, 3, QH_MAX_INT, 0),
247 249
                                         # new holding
248
                                         (e0, resource1, k0, 0, None, 5, 5)])
250
                                         (e0, resource1, k0, 0, QH_MAX_INT, 5, 5)])
249 251
        self.assertEqual(r, [])
250 252

  
251 253
        r = self.qh.get_quota(get_quota=[(e0, resource0, k0),
252 254
                                         (e0, resource1, k0)])
253
        self.assertEqual(r, [(e0, resource0, 5, 3, None, 5)
255
        self.assertEqual(r, [(e0, resource0, 5, 3, QH_MAX_INT, 5)
254 256
                             + DEFAULT_HOLDING + (0,),
255
                             (e0, resource1, 0, None, 5, 5)
257
                             (e0, resource1, 0, QH_MAX_INT, 5, 5)
256 258
                             + DEFAULT_HOLDING + (0,)])
257 259

  
258 260
        # repeated serial
259 261
        r = self.qh.add_quota(clientkey=self.client,
260 262
                              serial=1,
261
                              sub_quota=[(e0, resource1, k0, 0, None, (-5), 0)],
262
                              add_quota=[(e0, resource0, k0, 0, 2, None, 0)])
263
                              sub_quota=[(e0, resource1, k0, 0, QH_MAX_INT, (-5), 0)],
264
                              add_quota=[(e0, resource0, k0, 0, 2, QH_MAX_INT, 0)])
263 265
        self.assertEqual(r, [(e0, resource1), (e0, resource0)])
264 266

  
265 267
        r = self.qh.query_serials(clientkey=self.client, serials=[1, 2])
......
282 284
        # serial has been deleted
283 285
        r = self.qh.add_quota(clientkey=self.client,
284 286
                              serial=1,
285
                              add_quota=[(e0, resource0, k0, 0, 2, None, 0)])
287
                              add_quota=[(e0, resource0, k0, 0, 2, QH_MAX_INT, 0)])
286 288
        self.assertEqual(r, [])
287 289

  
288 290
        # none is committed
289 291
        r = self.qh.add_quota(clientkey=self.client,
290 292
                              serial=2,
291
                              add_quota=[(e1, resource0, k1, 0, (-10), None, 0),
293
                              add_quota=[(e1, resource0, k1, 0, (-10), QH_MAX_INT, 0),
292 294
                                         (e0, resource1, k0, 1, 0, 0, 0)])
293 295
        self.assertEqual(r, [(e1, resource0)])
294 296

  
......
296 298
                                         (e0, resource1, k0)])
297 299
        self.assertEqual(r, [(e1, resource0, 5, 5 , 5, 5)
298 300
                             + DEFAULT_HOLDING + (0,),
299
                             (e0, resource1, 0, None, 5, 5)
301
                             (e0, resource1, 0, QH_MAX_INT, 5, 5)
300 302
                             + DEFAULT_HOLDING + (0,)])
301 303

  
304
    def test_0082_max_quota(self):
305
        e0, k0 = self.new_entity()
306
        e1, k1 = self.new_entity()
307
        resource0 = self.rand_resource()
308
        resource1 = self.rand_resource()
309

  
310
        r = self.qh.set_quota(
311
            set_quota=[(e0, resource0, k0) + (5, QH_MAX_INT, 5, 6) + (0,)])
312
        self.assertEqual(r, [])
313

  
314
        r = self.qh.add_quota(clientkey=self.client,
315
                              serial=3,
316
                              add_quota=[(e0, resource0, k0, 0, QH_MAX_INT, 0, 0)])
317

  
318
        self.assertEqual(r, [])
319

  
320
        r = self.qh.get_quota(get_quota=[(e0, resource0, k0)])
321
        self.assertEqual(r, [(e0, resource0, 5, 2*QH_MAX_INT, 5, 6)
322
                             + DEFAULT_HOLDING + (0,)])
323

  
324

  
325

  
302 326
    def test_0090_commissions(self):
303 327
        e0, k0 = self.new_entity()
304 328
        e1, k1 = self.new_entity()

Also available in: Unified diff