Commit da5d8c5a authored by Olga Brani's avatar Olga Brani
Browse files

Merge branch 'latest-quota' of https://code.grnet.gr/git/synnefo into latest-quota

parents 0a4da18d 5dcad10f
......@@ -31,7 +31,7 @@
# interpreted as representing official policies, either expressed
# or implied, of GRNET S.A.
from .quotaholder import QuotaholderAPI
from .quotaholder import QuotaholderAPI, QH_PRACTICALLY_INFINITE
from .exception import ( InvalidKeyError, NoEntityError,
NoQuantityError, NoCapacityError,
ExportLimitError, ImportLimitError,
......
......@@ -59,6 +59,8 @@ class Positive(Integer):
def init(self):
self.opts.update({'minimum': 1})
QH_PRACTICALLY_INFINITE = 10**32
Serial = Positive(classname='Serial')
ClientKey = Name(classname='ClientKey')
......@@ -72,14 +74,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')
......
......@@ -33,6 +33,7 @@
from synnefo.lib.quotaholder.api import (
QuotaholderAPI,
QH_PRACTICALLY_INFINITE,
InvalidKeyError, NoEntityError,
NoQuantityError, NoCapacityError,
ExportLimitError, ImportLimitError,
......@@ -363,7 +364,10 @@ class QuotaholderDjangoDBCallpoint(Callpoint):
except Holding.DoesNotExist:
h = Holding(entity=entity, resource=resource)
p = Policy.objects.create(policy=self._new_policy_name(),
quantity=0)
quantity=0,
capacity=QH_PRACTICALLY_INFINITE,
import_limit=QH_PRACTICALLY_INFINITE,
export_limit=QH_PRACTICALLY_INFINITE)
h.policy = p
h.imported += amount
h.save()
......@@ -673,27 +677,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 +711,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 +998,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 +1005,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
......
......@@ -34,11 +34,37 @@
from synnefo.lib.commissioning import CorruptedError
from django.core import exceptions
from django.db.models import (Model, BigIntegerField, CharField,
ForeignKey, AutoField)
IntegerField, SubfieldBase,
ForeignKey, AutoField, DecimalField)
from django.db import transaction
from .managers import ForUpdateManager
class IntDecimalField(IntegerField):
__metaclass__ = SubfieldBase
def __init__(self, max_digits=None, **kwargs):
self.max_digits, self.decimal_places = max_digits, 0
IntegerField.__init__(self, **kwargs)
def get_internal_type(self):
return "DecimalField"
def to_python(self, value):
if value is None:
return value
try:
return long(value)
except (ValueError, TypeError):
raise exceptions.ValidationError(self.error_messages['invalid'])
DECIMAL_DIGITS = 38
def intDecimalField(**kwargs):
return IntDecimalField(max_digits=DECIMAL_DIGITS, **kwargs)
class Holder(Model):
attribute = CharField(max_length=4096, primary_key=True)
......@@ -59,10 +85,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 = intDecimalField()
capacity = intDecimalField()
import_limit = intDecimalField()
export_limit = intDecimalField()
objects = ForUpdateManager()
......@@ -74,14 +100,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 = intDecimalField(default=0)
importing = intDecimalField(default=0)
exported = intDecimalField(default=0)
exporting = intDecimalField(default=0)
returned = intDecimalField(default=0)
returning = intDecimalField(default=0)
released = intDecimalField(default=0)
releasing = intDecimalField(default=0)
objects = ForUpdateManager()
......@@ -113,7 +139,7 @@ class Provision(Model):
entity = ForeignKey(Entity, to_field='entity')
resource = CharField(max_length=4096, null=False)
quantity = BigIntegerField(null=False)
quantity = intDecimalField()
objects = ForUpdateManager()
......@@ -126,23 +152,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 = intDecimalField()
source_capacity = intDecimalField()
source_import_limit = intDecimalField()
source_export_limit = intDecimalField()
source_imported = intDecimalField()
source_exported = intDecimalField()
source_returned = intDecimalField()
source_released = intDecimalField()
target_quantity = intDecimalField()
target_capacity = intDecimalField()
target_import_limit = intDecimalField()
target_export_limit = intDecimalField()
target_imported = intDecimalField()
target_exported = intDecimalField()
target_returned = intDecimalField()
target_released = intDecimalField()
delta_quantity = intDecimalField()
reason = CharField(max_length=4096)
objects = ForUpdateManager()
......
......@@ -38,6 +38,7 @@ from config import printf
from synnefo.lib.commissioning import CallError
from synnefo.lib.quotaholder.api import (
QH_PRACTICALLY_INFINITE,
InvalidDataError,
InvalidKeyError, NoEntityError,
NoQuantityError, NoCapacityError,
......@@ -236,30 +237,35 @@ 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_PRACTICALLY_INFINITE, 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_PRACTICALLY_INFINITE, 1, 1)],
add_quota=[(e0, resource0, k0,
0, 3, QH_PRACTICALLY_INFINITE, 0),
# new holding
(e0, resource1, k0, 0, None, 5, 5)])
(e0, resource1, k0,
0, QH_PRACTICALLY_INFINITE, 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_PRACTICALLY_INFINITE+4, 5)
+ DEFAULT_HOLDING + (0,),
(e0, resource1, 0, None, 5, 5)
(e0, resource1, 0, QH_PRACTICALLY_INFINITE, 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_PRACTICALLY_INFINITE, (-5), 0)],
add_quota=[(e0, resource0, k0,
0, 2, QH_PRACTICALLY_INFINITE, 0)])
self.assertEqual(r, [(e0, resource1), (e0, resource0)])
r = self.qh.query_serials(clientkey=self.client, serials=[1, 2])
......@@ -282,13 +288,15 @@ 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_PRACTICALLY_INFINITE, 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_PRACTICALLY_INFINITE, 0),
(e0, resource1, k0, 1, 0, 0, 0)])
self.assertEqual(r, [(e1, resource0)])
......@@ -296,9 +304,33 @@ 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_PRACTICALLY_INFINITE, 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_PRACTICALLY_INFINITE, 5, 6) + (0,)])
self.assertEqual(r, [])
r = self.qh.add_quota(clientkey=self.client,
serial=3,
add_quota=[(e0, resource0, k0,
0, QH_PRACTICALLY_INFINITE, 0, 0)])
self.assertEqual(r, [])
r = self.qh.get_quota(get_quota=[(e0, resource0, k0)])
self.assertEqual(r, [(e0, resource0, 5, 2*QH_PRACTICALLY_INFINITE, 5, 6)
+ DEFAULT_HOLDING + (0,)])
def test_0090_commissions(self):
e0, k0 = self.new_entity()
e1, k1 = self.new_entity()
......@@ -398,9 +430,15 @@ class QHAPITest(QHTestCase):
if r:
raise AssertionError("cannot create entities")
self.qh.set_quota(set_quota=[(sys, resource, '', 10, 0, None, None, 0),
(e0, resource, k0, 0, 10, None, None, 0),
(e1, resource, k1, 0, 10, None, None, 0)])
self.qh.set_quota(set_quota=[(sys, resource, '', 10, 0,
QH_PRACTICALLY_INFINITE,
QH_PRACTICALLY_INFINITE, 0),
(e0, resource, k0, 0, 10,
QH_PRACTICALLY_INFINITE,
QH_PRACTICALLY_INFINITE, 0),
(e1, resource, k1, 0, 10,
QH_PRACTICALLY_INFINITE,
QH_PRACTICALLY_INFINITE, 0)])
s0 = self.qh.issue_commission(clientkey=self.client, target=e0, key=k0,
name='a commission',
......
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