Commit cd250991 authored by Giorgos Korfiatis's avatar Giorgos Korfiatis
Browse files

wip Use decimals to store quota values; infinite quotas no more supported

parent 6f4f69d8
......@@ -72,14 +72,14 @@ OwnerKey = Text(classname='OwnerKey')
Resource = Name(classname='Resource')
Policy = Name(classname='Policy')
Quantity = Integer(classname='Quantity', null=True)
Capacity = Nonnegative(classname='Capacity', null=True)
ImportLimit = Nonnegative(classname='ImportLimit', null=True)
ExportLimit = Nonnegative(classname='ExportLimit', null=True)
QuantityDelta = Integer(classname='QuantityDelta', null=True)
CapacityDelta = Integer(classname='CapacityDelta', null=True)
ImportLimitDelta = Integer(classname='ImportLimitDelta', null=True)
ExportLimitDelta = Integer(classname='ExportLimitDelta', null=True)
Quantity = Integer(classname='Quantity')
Capacity = Nonnegative(classname='Capacity')
ImportLimit = Nonnegative(classname='ImportLimit')
ExportLimit = Nonnegative(classname='ExportLimit')
QuantityDelta = Integer(classname='QuantityDelta')
CapacityDelta = Integer(classname='CapacityDelta')
ImportLimitDelta = Integer(classname='ImportLimitDelta')
ExportLimitDelta = Integer(classname='ExportLimitDelta')
Imported = Nonnegative(classname='Imported')
Exported = Nonnegative(classname='Exported')
Returned = Nonnegative(classname='Returned')
......
......@@ -673,27 +673,25 @@ class QuotaholderDjangoDBCallpoint(Callpoint):
hp = h.policy
if hp.export_limit is not None:
current = h.exporting
limit = hp.export_limit
if current + quantity > limit:
m = ("Export limit reached for %s.%s" % (entity, resource))
raise ExportLimitError(m,
source=entity, target=target,
resource=resource, requested=quantity,
current=current, limit=limit)
if hp.quantity is not None:
limit = hp.quantity
current = (+ h.exporting + h.releasing
- h.imported - h.returned)
if current + quantity > limit:
m = ("There is not enough quantity "
"to allocate from in %s.%s" % (entity, resource))
raise NoQuantityError(m,
source=entity, target=target,
resource=resource, requested=quantity,
current=current, limit=limit)
current = h.exporting
limit = hp.export_limit
if current + quantity > limit:
m = ("Export limit reached for %s.%s" % (entity, resource))
raise ExportLimitError(m,
source=entity, target=target,
resource=resource, requested=quantity,
current=current, limit=limit)
limit = hp.quantity
current = (+ h.exporting + h.releasing
- h.imported - h.returned)
if current + quantity > limit:
m = ("There is not enough quantity "
"to allocate from in %s.%s" % (entity, resource))
raise NoQuantityError(m,
source=entity, target=target,
resource=resource, requested=quantity,
current=current, limit=limit)
# Target limits checks
try:
......@@ -709,28 +707,26 @@ class QuotaholderDjangoDBCallpoint(Callpoint):
tp = th.policy
if tp.import_limit is not None:
limit = tp.import_limit
current = th.importing
if current + quantity > limit:
m = ("Import limit reached for %s.%s" % (target, resource))
raise ImportLimitError(m,
source=entity, target=target,
resource=resource, requested=quantity,
current=current, limit=limit)
if tp.capacity is not None:
limit = tp.capacity
current = (+ th.importing + th.returning
- th.exported - th.released)
if current + quantity > limit:
m = ("There is not enough capacity "
"to allocate into in %s.%s" % (target, resource))
raise NoCapacityError(m,
source=entity, target=target,
resource=resource, requested=quantity,
current=current, limit=limit)
limit = tp.import_limit
current = th.importing
if current + quantity > limit:
m = ("Import limit reached for %s.%s" % (target, resource))
raise ImportLimitError(m,
source=entity, target=target,
resource=resource, requested=quantity,
current=current, limit=limit)
limit = tp.capacity
current = (+ th.importing + th.returning
- th.exported - th.released)
if current + quantity > limit:
m = ("There is not enough capacity "
"to allocate into in %s.%s" % (target, resource))
raise NoCapacityError(m,
source=entity, target=target,
resource=resource, requested=quantity,
current=current, limit=limit)
Provision.objects.create( serial = commission,
entity = e,
......@@ -998,10 +994,6 @@ class QuotaholderDjangoDBCallpoint(Callpoint):
return timeline
def _add(x, y, invert=False):
if invert and y is None:
return 0
if x is None or y is None:
return None
return x + y if not invert else x - y
def _update(dest, source, attr, delta):
......@@ -1009,8 +1001,6 @@ def _update(dest, source, attr, delta):
dest_attr = _add(getattr(source, attr, 0), delta)
def _isneg(x):
if x is None:
return False
return x < 0
API_Callpoint = QuotaholderDjangoDBCallpoint
......
......@@ -35,10 +35,16 @@
from synnefo.lib.commissioning import CorruptedError
from django.db.models import (Model, BigIntegerField, CharField,
ForeignKey, AutoField)
ForeignKey, AutoField, DecimalField)
from django.db import transaction
from .managers import ForUpdateManager
DECIMAL_DIGITS = 38
QH_MAX_INT = 10**32
def decimalField(**kwargs):
return DecimalField(max_digits=DECIMAL_DIGITS, decimal_places=0, **kwargs)
class Holder(Model):
attribute = CharField(max_length=4096, primary_key=True)
......@@ -59,10 +65,10 @@ class Entity(Model):
class Policy(Model):
policy = CharField(max_length=4096, primary_key=True)
quantity = BigIntegerField(null=True, default=None)
capacity = BigIntegerField(null=True, default=None)
import_limit = BigIntegerField(null=True, default=None)
export_limit = BigIntegerField(null=True, default=None)
quantity = decimalField()
capacity = decimalField()
import_limit = decimalField()
export_limit = decimalField()
objects = ForUpdateManager()
......@@ -74,14 +80,14 @@ class Holding(Model):
policy = ForeignKey(Policy, to_field='policy')
flags = BigIntegerField(null=False, default=0)
imported = BigIntegerField(null=False, default=0)
importing = BigIntegerField(null=False, default=0)
exported = BigIntegerField(null=False, default=0)
exporting = BigIntegerField(null=False, default=0)
returned = BigIntegerField(null=False, default=0)
returning = BigIntegerField(null=False, default=0)
released = BigIntegerField(null=False, default=0)
releasing = BigIntegerField(null=False, default=0)
imported = decimalField(default=0)
importing = decimalField(default=0)
exported = decimalField(default=0)
exporting = decimalField(default=0)
returned = decimalField(default=0)
returning = decimalField(default=0)
released = decimalField(default=0)
releasing = decimalField(default=0)
objects = ForUpdateManager()
......@@ -113,7 +119,7 @@ class Provision(Model):
entity = ForeignKey(Entity, to_field='entity')
resource = CharField(max_length=4096, null=False)
quantity = BigIntegerField(null=False)
quantity = decimalField()
objects = ForUpdateManager()
......@@ -126,23 +132,23 @@ class ProvisionLog(Model):
issue_time = CharField(max_length=4096)
log_time = CharField(max_length=4096)
resource = CharField(max_length=4096)
source_quantity = BigIntegerField(null=True)
source_capacity = BigIntegerField(null=True)
source_import_limit = BigIntegerField(null=True)
source_export_limit = BigIntegerField(null=True)
source_imported = BigIntegerField(null=False)
source_exported = BigIntegerField(null=False)
source_returned = BigIntegerField(null=False)
source_released = BigIntegerField(null=False)
target_quantity = BigIntegerField(null=True)
target_capacity = BigIntegerField(null=True)
target_import_limit = BigIntegerField(null=True)
target_export_limit = BigIntegerField(null=True)
target_imported = BigIntegerField(null=False)
target_exported = BigIntegerField(null=False)
target_returned = BigIntegerField(null=False)
target_released = BigIntegerField(null=False)
delta_quantity = BigIntegerField(null=False)
source_quantity = decimalField()
source_capacity = decimalField()
source_import_limit = decimalField()
source_export_limit = decimalField()
source_imported = decimalField()
source_exported = decimalField()
source_returned = decimalField()
source_released = decimalField()
target_quantity = decimalField()
target_capacity = decimalField()
target_import_limit = decimalField()
target_export_limit = decimalField()
target_imported = decimalField()
target_exported = decimalField()
target_returned = decimalField()
target_released = decimalField()
delta_quantity = decimalField()
reason = CharField(max_length=4096)
objects = ForUpdateManager()
......
......@@ -47,6 +47,8 @@ from synnefo.lib.quotaholder.api.quotaholder import (
Name, Key, Quantity, Capacity, ImportLimit, ExportLimit, Resource, Flags,
Imported, Exported, Returned, Released)
QH_MAX_INT = 10**32
DEFAULT_HOLDING = (0, 0, 0, 0)
class QHAPITest(QHTestCase):
......@@ -236,30 +238,30 @@ class QHAPITest(QHTestCase):
resource1 = self.rand_resource()
r = self.qh.set_quota(
set_quota=[(e0, resource0, k0) + (5, None, 5, 6) + (0,),
set_quota=[(e0, resource0, k0) + (5, QH_MAX_INT, 5, 6) + (0,),
(e1, resource0, k1) + (5, 5, 5, 5) + (0,)])
self.assertEqual(r, [])
r = self.qh.add_quota(clientkey=self.client,
serial=1,
sub_quota=[(e0, resource0, k0, 0, None, 1, 1)],
add_quota=[(e0, resource0, k0, 0, 3, None, 0),
sub_quota=[(e0, resource0, k0, 0, QH_MAX_INT, 1, 1)],
add_quota=[(e0, resource0, k0, 0, 3, QH_MAX_INT, 0),
# new holding
(e0, resource1, k0, 0, None, 5, 5)])
(e0, resource1, k0, 0, QH_MAX_INT, 5, 5)])
self.assertEqual(r, [])
r = self.qh.get_quota(get_quota=[(e0, resource0, k0),
(e0, resource1, k0)])
self.assertEqual(r, [(e0, resource0, 5, 3, None, 5)
self.assertEqual(r, [(e0, resource0, 5, 3, QH_MAX_INT, 5)
+ DEFAULT_HOLDING + (0,),
(e0, resource1, 0, None, 5, 5)
(e0, resource1, 0, QH_MAX_INT, 5, 5)
+ DEFAULT_HOLDING + (0,)])
# repeated serial
r = self.qh.add_quota(clientkey=self.client,
serial=1,
sub_quota=[(e0, resource1, k0, 0, None, (-5), 0)],
add_quota=[(e0, resource0, k0, 0, 2, None, 0)])
sub_quota=[(e0, resource1, k0, 0, QH_MAX_INT, (-5), 0)],
add_quota=[(e0, resource0, k0, 0, 2, QH_MAX_INT, 0)])
self.assertEqual(r, [(e0, resource1), (e0, resource0)])
r = self.qh.query_serials(clientkey=self.client, serials=[1, 2])
......@@ -282,13 +284,13 @@ class QHAPITest(QHTestCase):
# serial has been deleted
r = self.qh.add_quota(clientkey=self.client,
serial=1,
add_quota=[(e0, resource0, k0, 0, 2, None, 0)])
add_quota=[(e0, resource0, k0, 0, 2, QH_MAX_INT, 0)])
self.assertEqual(r, [])
# none is committed
r = self.qh.add_quota(clientkey=self.client,
serial=2,
add_quota=[(e1, resource0, k1, 0, (-10), None, 0),
add_quota=[(e1, resource0, k1, 0, (-10), QH_MAX_INT, 0),
(e0, resource1, k0, 1, 0, 0, 0)])
self.assertEqual(r, [(e1, resource0)])
......@@ -296,9 +298,31 @@ class QHAPITest(QHTestCase):
(e0, resource1, k0)])
self.assertEqual(r, [(e1, resource0, 5, 5 , 5, 5)
+ DEFAULT_HOLDING + (0,),
(e0, resource1, 0, None, 5, 5)
(e0, resource1, 0, QH_MAX_INT, 5, 5)
+ DEFAULT_HOLDING + (0,)])
def test_0082_max_quota(self):
e0, k0 = self.new_entity()
e1, k1 = self.new_entity()
resource0 = self.rand_resource()
resource1 = self.rand_resource()
r = self.qh.set_quota(
set_quota=[(e0, resource0, k0) + (5, QH_MAX_INT, 5, 6) + (0,)])
self.assertEqual(r, [])
r = self.qh.add_quota(clientkey=self.client,
serial=3,
add_quota=[(e0, resource0, k0, 0, QH_MAX_INT, 0, 0)])
self.assertEqual(r, [])
r = self.qh.get_quota(get_quota=[(e0, resource0, k0)])
self.assertEqual(r, [(e0, resource0, 5, 2*QH_MAX_INT, 5, 6)
+ DEFAULT_HOLDING + (0,)])
def test_0090_commissions(self):
e0, k0 = self.new_entity()
e1, k1 = self.new_entity()
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment