Commit d5c9fa3b authored by Georgios D. Tsoukalas's avatar Georgios D. Tsoukalas
Browse files

fix+test for issue_commission release logic

issue_commission() with negative quantity is considered
to be a 'release' instead of an 'allocation'.
The limit checks for the conditions or the release were
somewhat incomplete and inconsistent.

The new logic also fixes the bug found by kpap where
an entity would be denied release of quantities (with NoCapacityError)
if the remaining quantity exceeds the quota limit.
This prevented an entity from releasing its overlimit quantities
in multiple steps (e.g. one by one), which resulted in a deadlock,
when a service (cyclades VM) tried to rectify the overlimit cases.
parent d5d358d7
......@@ -668,7 +668,7 @@ class QuotaholderDjangoDBCallpoint(Callpoint):
h = db_get_holding(entity=entity, resource=resource,
for_update=True)
except Holding.DoesNotExist:
m = ("There is not enough quantity "
m = ("There is no quantity "
"to allocate from in %s.%s" % (entity, resource))
raise NoQuantityError(m,
source=entity, target=target,
......@@ -677,25 +677,38 @@ class QuotaholderDjangoDBCallpoint(Callpoint):
hp = h.policy
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)
if not release:
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 + h.imported - h.releasing
unavailable = h.exporting - h.returned
available = limit - unavailable
if quantity > available:
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=unavailable, limit=limit)
else:
current = (+ h.importing + h.returning
- h.exported - h.returned)
limit = hp.capacity
if current - quantity > limit:
m = ("There is not enough capacity "
"to release to in %s.%s" % (entity, resource))
raise NoQuantityError(m,
source=entity, target=target,
resource=resource, requested=quantity,
current=current, limit=limit)
# Target limits checks
try:
......@@ -711,26 +724,38 @@ class QuotaholderDjangoDBCallpoint(Callpoint):
tp = th.policy
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 not release:
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)
current = (+ th.importing + th.returning
- th.exported - th.released)
if not release and (current + quantity > limit):
if current + quantity > tp.quantity + tp.capacity:
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)
else:
limit = tp.quantity + th.imported - th.releasing
unavailable = th.exporting - th.returned
available = limit - unavailable
if available + quantity < 0:
m = ("There is not enough quantity "
"to release from in %s.%s" % (target, resource))
raise NoCapacityError(m,
source=entity, target=target,
resource=resource, requested=quantity,
current=unavailable, limit=limit)
Provision.objects.create( serial = commission,
entity = e,
......
......@@ -338,7 +338,10 @@ class QHAPITest(QHTestCase):
q0, c0, il0, el0 = self.new_quota(e0, k0, resource)
q1, c1, il1, el1 = self.new_quota(e1, k1, resource)
most = max(0, min(c0, il0, q1, el1))
most = min(c0, il0, q1, el1)
if most < 0:
raise AssertionError("%s <= 0" % most)
r = self.qh.issue_commission(clientkey=self.client, target=e0, key=k0,
name='something',
provisions=[(e1, resource, most)])
......@@ -531,6 +534,67 @@ class QHAPITest(QHTestCase):
r = self.qh.get_holding(get_holding=[(e1, resource, k1)])
self.assertEqual(r, [(e1, resource, p) + counters + (f,)])
def test_015_release_nocapacity(self):
qh = self.qh
key = "key"
owner = "system"
owner_key = ""
source = "test_015_release_nocapacity_source"
resource = "resource"
target = "test_015_release_nocapacity_target"
flags = 0
source_create = [source, owner, key, owner_key]
source_limits = [source, 6, 0, 1000, 1000]
source_holding = [source, resource, key, source, flags]
target_create = [target, owner, key, owner_key]
target_limits = [target, 0, 5, 1000, 1000]
target_holding = [target, resource, key, target, flags]
failed = AssertionError("Quotaholder call failed")
if qh.create_entity(create_entity=[source_create, target_create]):
raise failed
if qh.set_limits(set_limits=[source_limits, target_limits]):
raise failed
if qh.set_holding(set_holding=[source_holding, target_holding]):
raise failed
serial = qh.issue_commission(clientkey=self.client, target=target, key=key,
name="something", provisions=[(source, resource, 5)])
qh.accept_commission(clientkey=self.client, serials=[serial])
holding = qh.get_holding(get_holding=[[source, resource, key]])
self.assertEqual(tuple(holding[0]), (source, resource, source, 0, 5, 0, 0, flags))
holding = qh.get_holding(get_holding=[[target, resource, key]])
self.assertEqual(tuple(holding[0]), (target, resource, target, 5, 0, 0, 0, flags))
if qh.reset_holding(reset_holding=[[target, resource, key, 10, 0, 0, 0]]):
raise failed
with self.assertRaises(NoCapacityError):
qh.issue_commission(clientkey=self.client, target=target, key=key,
name="something", provisions=[(source, resource, 1)])
with self.assertRaises(NoQuantityError):
qh.issue_commission(clientkey=self.client, target=target, key=key,
name="something", provisions=[(source, resource, -7)])
source_limits = [source, 6, 10, 1000, 1000]
if qh.set_limits(set_limits=[source_limits]):
raise failed
serial = qh.issue_commission(clientkey=self.client, target=target, key=key,
name="something", provisions=[(source, resource, -1)])
qh.accept_commission(clientkey=self.client, serials=[serial])
holding = qh.get_holding(get_holding=[[source, resource, key]])
self.assertEqual(tuple(holding[0]), (source, resource, source, 0, 5, 1, 0, flags))
holding = qh.get_holding(get_holding=[[target, resource, key]])
self.assertEqual(tuple(holding[0]), (target, resource, target, 10, 0, 0, 1, flags))
with self.assertRaises(NoCapacityError):
qh.issue_commission(clientkey=self.client, target=target, key=key,
name="something", provisions=[(source, resource, -10)])
if __name__ == "__main__":
import sys
......
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