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