Revision d2b32360
b/snf-astakos-app/astakos/im/endpoints/qh.py | ||
---|---|---|
118 | 118 |
'import_limit', |
119 | 119 |
'export_limit')) |
120 | 120 |
|
121 |
def qh_add_quota(serial, quotalimits_list):
|
|
121 |
def qh_add_quota(serial, sub_list, add_list):
|
|
122 | 122 |
if not QUOTAHOLDER_URL: |
123 | 123 |
return () |
124 | 124 |
|
125 | 125 |
context = {} |
126 | 126 |
c = get_client() |
127 | 127 |
|
128 |
data = [] |
|
129 |
append = data.append |
|
130 |
for ql in quotalimits_list: |
|
128 |
sub_quota = [] |
|
129 |
sub_append = sub_quota.append |
|
130 |
add_quota = [] |
|
131 |
add_append = add_quota.append |
|
132 |
|
|
133 |
for ql in sub_quota: |
|
131 | 134 |
args = (ql.holder, ql.resource, ENTITY_KEY, |
132 | 135 |
0, ql.capacity, ql.import_limit, ql.export_limit) |
133 |
append(args) |
|
136 |
sub_append(args) |
|
137 |
|
|
138 |
for ql in add_quota: |
|
139 |
args = (ql.holder, ql.resource, ENTITY_KEY, |
|
140 |
0, ql.capacity, ql.import_limit, ql.export_limit) |
|
141 |
add_append(args) |
|
134 | 142 |
|
135 | 143 |
result = c.add_quota(context=context, |
136 | 144 |
clientkey=clientkey, |
137 | 145 |
serial=serial, |
138 |
add_quota=data) |
|
146 |
sub_quota=sub_quota, |
|
147 |
add_quota=add_quota) |
|
139 | 148 |
|
140 | 149 |
return result |
141 | 150 |
|
b/snf-astakos-app/astakos/im/models.py | ||
---|---|---|
1501 | 1501 |
self.state = APPROVED |
1502 | 1502 |
self.save() |
1503 | 1503 |
|
1504 |
transaction.commit() |
|
1505 |
trigger_sync() |
|
1506 |
|
|
1507 | 1504 |
|
1508 | 1505 |
class ProjectResourceGrant(models.Model): |
1509 | 1506 |
|
... | ... | |
1682 | 1679 |
# logger.error(e.messages) |
1683 | 1680 |
|
1684 | 1681 |
|
1685 |
|
|
1686 |
class ExclusiveOrRaise(object): |
|
1687 |
"""Context Manager to exclusively execute a critical code section. |
|
1688 |
The exclusion must be global. |
|
1689 |
(IPC semaphores will not protect across OS, |
|
1690 |
DB locks will if it's the same DB) |
|
1691 |
""" |
|
1692 |
|
|
1693 |
class Busy(Exception): |
|
1694 |
pass |
|
1695 |
|
|
1696 |
def __init__(self, locked=False): |
|
1697 |
init = 0 if locked else 1 |
|
1698 |
from multiprocessing import Semaphore |
|
1699 |
self._sema = Semaphore(init) |
|
1700 |
|
|
1701 |
def enter(self): |
|
1702 |
acquired = self._sema.acquire(False) |
|
1703 |
if not acquired: |
|
1704 |
raise self.Busy() |
|
1705 |
|
|
1706 |
def leave(self): |
|
1707 |
self._sema.release() |
|
1708 |
|
|
1709 |
def __enter__(self): |
|
1710 |
self.enter() |
|
1711 |
return self |
|
1712 |
|
|
1713 |
def __exit__(self, exc_type, exc_value, exc_traceback): |
|
1714 |
self.leave() |
|
1715 |
|
|
1716 |
|
|
1717 |
exclusive_or_raise = ExclusiveOrRaise(locked=False) |
|
1718 |
|
|
1719 |
|
|
1720 | 1682 |
class ProjectMembership(models.Model): |
1721 | 1683 |
|
1722 | 1684 |
person = models.ForeignKey(AstakosUser) |
... | ... | |
1783 | 1745 |
self._set_history_item(reason='ACCEPT', date=now) |
1784 | 1746 |
self.state = self.PENDING |
1785 | 1747 |
self.save() |
1786 |
trigger_sync() |
|
1787 | 1748 |
|
1788 | 1749 |
def remove(self): |
1789 | 1750 |
if state != self.ACCEPTED: |
... | ... | |
1793 | 1754 |
self._set_history_item(reason='REMOVE') |
1794 | 1755 |
self.state = self.REMOVING |
1795 | 1756 |
self.save() |
1796 |
trigger_sync() |
|
1797 | 1757 |
|
1798 | 1758 |
def reject(self): |
1799 | 1759 |
if state != self.REQUESTED: |
... | ... | |
1805 | 1765 |
self._set_history_item(reason='REJECT') |
1806 | 1766 |
self.delete() |
1807 | 1767 |
|
1808 |
def get_diff_quotas(self, limits_list=None, remove=False): |
|
1809 |
if limits_list is None: |
|
1810 |
limits_list = [] |
|
1768 |
def get_diff_quotas(self, sub_list=None, add_list=None, remove=False): |
|
1769 |
if sub_list is None: |
|
1770 |
sub_list = [] |
|
1771 |
|
|
1772 |
if add_list is None: |
|
1773 |
add_list = [] |
|
1811 | 1774 |
|
1812 |
append = limits_list.append |
|
1775 |
sub_append = sub_list.append |
|
1776 |
add_append = add_list.append |
|
1813 | 1777 |
holder = self.person.username |
1814 |
key = "1" |
|
1815 | 1778 |
|
1816 |
tmp_grants = {} |
|
1817 | 1779 |
synced_application = self.application |
1818 | 1780 |
if synced_application is not None: |
1819 | 1781 |
# first, inverse all current limits, and index them by resource name |
1820 | 1782 |
cur_grants = synced_application.resource_grants.all() |
1821 |
f = -1 |
|
1822 | 1783 |
for grant in cur_grants: |
1823 |
name = grant.resource.name |
|
1824 |
tmp_grants[name] = QuotaLimits( |
|
1825 |
holder = holder, |
|
1826 |
resource = name, |
|
1827 |
capacity = f * grant.member_capacity, |
|
1828 |
import_limit = f * grant.member_import_limit, |
|
1829 |
export_limit = f * grant.member_export_limit) |
|
1784 |
sub_append(QuotaLimits( |
|
1785 |
holder = holder, |
|
1786 |
resource = grant.resource.name, |
|
1787 |
capacity = grant.member_capacity, |
|
1788 |
import_limit = grant.member_import_limit, |
|
1789 |
export_limit = grant.member_export_limit)) |
|
1830 | 1790 |
|
1831 | 1791 |
if not remove: |
1832 | 1792 |
# second, add each new limit to its inverted current |
1833 | 1793 |
new_grants = self.pending_application.projectresourcegrant_set.all() |
1834 | 1794 |
for new_grant in new_grants: |
1835 |
name = new_grant.resource.name |
|
1836 |
cur_grant = tmp_grants.pop(name, None) |
|
1837 |
if cur_grant is None: |
|
1838 |
# if limits on a new resource, set 0 current values |
|
1839 |
capacity = 0 |
|
1840 |
import_limit = 0 |
|
1841 |
export_limit = 0 |
|
1842 |
else: |
|
1843 |
capacity = cur_grant.capacity |
|
1844 |
import_limit = cur_grant.import_limit |
|
1845 |
export_limit = cur_grant.export_limit |
|
1846 |
|
|
1847 |
capacity += new_grant.member_capacity |
|
1848 |
import_limit += new_grant.member_import_limit |
|
1849 |
export_limit += new_grant.member_export_limit |
|
1795 |
add_append(QuotaLimits( |
|
1796 |
holder = holder, |
|
1797 |
resource = new_grant.resource.name, |
|
1798 |
capacity = new_grant.capacity, |
|
1799 |
import_limit = new_grant.import_limit, |
|
1800 |
export_limit = new_grant.export_limit)) |
|
1850 | 1801 |
|
1851 |
append(QuotaLimits(holder = holder, |
|
1852 |
key = key, |
|
1853 |
resource = name, |
|
1854 |
capacity = capacity, |
|
1855 |
import_limit = import_limit, |
|
1856 |
export_limit = export_limit)) |
|
1857 |
|
|
1858 |
# third, append all the inverted current limits for removed resources |
|
1859 |
limits_list.extend(tmp_grants.itervalues()) |
|
1860 |
return limits_list |
|
1802 |
return (sub_list, add_list) |
|
1861 | 1803 |
|
1862 | 1804 |
def set_sync(self): |
1863 | 1805 |
state = self.state |
... | ... | |
1914 | 1856 |
REMOVING = ProjectMembership.REMOVING |
1915 | 1857 |
objects = ProjectMembership.objects.select_for_update() |
1916 | 1858 |
|
1917 |
quotas = []
|
|
1859 |
sub_quota, add_quota = [], []
|
|
1918 | 1860 |
|
1919 | 1861 |
serial = new_serial() |
1920 | 1862 |
|
... | ... | |
1932 | 1874 |
|
1933 | 1875 |
membership.pending_application = membership.project.application |
1934 | 1876 |
membership.pending_serial = serial |
1935 |
membership.get_diff_quotas(quotas)
|
|
1877 |
membership.get_diff_quotas(sub_quota, add_quota)
|
|
1936 | 1878 |
membership.save() |
1937 | 1879 |
|
1938 | 1880 |
removing = objects.filter(state=REMOVING) |
... | ... | |
1948 | 1890 |
raise AssertionError(m) |
1949 | 1891 |
|
1950 | 1892 |
membership.pending_serial = serial |
1951 |
membership.get_diff_quotas(quotas, remove=True)
|
|
1893 |
membership.get_diff_quotas(sub_quota, add_quota, remove=True)
|
|
1952 | 1894 |
membership.save() |
1953 | 1895 |
|
1954 | 1896 |
transaction.commit() |
... | ... | |
1957 | 1899 |
# which has been scheduled to sync with the old project.application |
1958 | 1900 |
# Need to check in ProjectMembership.set_sync() |
1959 | 1901 |
|
1960 |
qh_add_quota(serial, quotas)
|
|
1902 |
qh_add_quota(serial, sub_quota, add_quota)
|
|
1961 | 1903 |
sync_finish_serials() |
1962 | 1904 |
|
1963 | 1905 |
|
... | ... | |
2136 | 2078 |
instance.renew_token() |
2137 | 2079 |
pre_save.connect(renew_token, sender=AstakosUser) |
2138 | 2080 |
pre_save.connect(renew_token, sender=Service) |
2081 |
|
b/snf-common/synnefo/lib/quotaholder/api/quotaholder.py | ||
---|---|---|
239 | 239 |
context = Context, |
240 | 240 |
clientkey = ClientKey, |
241 | 241 |
serial = Serial, |
242 |
sub_quota = ListOf( Entity, Resource, Key, |
|
243 |
QuantityDelta, CapacityDelta, |
|
244 |
ImportLimitDelta, ExportLimitDelta ), |
|
242 | 245 |
add_quota = ListOf( Entity, Resource, Key, |
243 | 246 |
QuantityDelta, CapacityDelta, |
244 | 247 |
ImportLimitDelta, ExportLimitDelta ) |
b/snf-quotaholder-app/quotaholder_django/quotaholder_app/callpoint.py | ||
---|---|---|
506 | 506 |
raise ReturnButFail(rejected) |
507 | 507 |
return rejected |
508 | 508 |
|
509 |
def add_quota(self, context={}, clientkey=None, serial=None, add_quota=()): |
|
509 |
def add_quota(self, context={}, clientkey=None, serial=None, |
|
510 |
sub_quota=(), add_quota=()): |
|
510 | 511 |
rejected = [] |
511 | 512 |
append = rejected.append |
512 |
all_pairs = [(q[0], q[1]) for q in add_quota] |
|
513 | 513 |
|
514 | 514 |
if serial is not None: |
515 | 515 |
if clientkey is None: |
516 |
all_pairs = [(q[0], q[1]) for q in sub_quota + add_quota] |
|
516 | 517 |
raise ReturnButFail(all_pairs) |
517 | 518 |
try: |
518 | 519 |
cs = CallSerial.objects.get(serial=serial, clientkey=clientkey) |
520 |
all_pairs = [(q[0], q[1]) for q in sub_quota + add_quota] |
|
519 | 521 |
raise ReturnButFail(all_pairs) |
520 | 522 |
except CallSerial.DoesNotExist: |
521 | 523 |
pass |
522 | 524 |
|
523 |
for ( entity, resource, key, |
|
524 |
quantity, capacity, |
|
525 |
import_limit, export_limit ) in add_quota: |
|
526 |
|
|
527 |
try: |
|
528 |
e = Entity.objects.get(entity=entity, key=key) |
|
529 |
except Entity.DoesNotExist: |
|
530 |
append((entity, resource)) |
|
531 |
continue |
|
525 |
for removing, source in [(True, sub_quota), (False, add_quota)]: |
|
526 |
for ( entity, resource, key, |
|
527 |
quantity, capacity, |
|
528 |
import_limit, export_limit ) in source: |
|
532 | 529 |
|
533 |
try: |
|
534 |
h = db_get_holding(entity=entity, resource=resource, |
|
535 |
for_update=True) |
|
536 |
p = h.policy |
|
537 |
except Holding.DoesNotExist: |
|
538 |
h = Holding(entity=e, resource=resource, flags=0) |
|
539 |
p = None |
|
530 |
try: |
|
531 |
e = Entity.objects.get(entity=entity, key=key) |
|
532 |
except Entity.DoesNotExist: |
|
533 |
append((entity, resource)) |
|
534 |
continue |
|
540 | 535 |
|
541 |
policy = newname('policy_') |
|
542 |
newp = Policy(policy=policy) |
|
543 |
|
|
544 |
newp.quantity = _add(p.quantity if p else 0, quantity) |
|
545 |
newp.capacity = _add(p.capacity if p else 0, capacity) |
|
546 |
newp.import_limit = _add(p.import_limit if p else 0, |
|
547 |
import_limit) |
|
548 |
newp.export_limit = _add(p.export_limit if p else 0, |
|
549 |
export_limit) |
|
550 |
|
|
551 |
new_values = [newp.capacity, |
|
552 |
newp.import_limit, newp.export_limit] |
|
553 |
if any(map(_isneg, new_values)): |
|
554 |
append((entity, resource)) |
|
555 |
continue |
|
536 |
try: |
|
537 |
h = db_get_holding(entity=entity, resource=resource, |
|
538 |
for_update=True) |
|
539 |
p = h.policy |
|
540 |
except Holding.DoesNotExist: |
|
541 |
if removing: |
|
542 |
append((entity, resource)) |
|
543 |
continue |
|
544 |
h = Holding(entity=e, resource=resource, flags=0) |
|
545 |
p = None |
|
546 |
|
|
547 |
policy = newname('policy_') |
|
548 |
newp = Policy(policy=policy) |
|
549 |
|
|
550 |
newp.quantity = _add(p.quantity if p else 0, quantity, |
|
551 |
invert=removing) |
|
552 |
newp.capacity = _add(p.capacity if p else 0, capacity, |
|
553 |
invert=removing) |
|
554 |
newp.import_limit = _add(p.import_limit if p else 0, |
|
555 |
import_limit, invert=removing) |
|
556 |
newp.export_limit = _add(p.export_limit if p else 0, |
|
557 |
export_limit, invert=removing) |
|
558 |
|
|
559 |
new_values = [newp.capacity, |
|
560 |
newp.import_limit, newp.export_limit] |
|
561 |
if any(map(_isneg, new_values)): |
|
562 |
append((entity, resource)) |
|
563 |
continue |
|
556 | 564 |
|
557 |
h.policy = newp |
|
565 |
h.policy = newp
|
|
558 | 566 |
|
559 |
# the order is intentionally reversed so that it |
|
560 |
# would break if we are not within a transaction. |
|
561 |
# Has helped before. |
|
562 |
h.save() |
|
563 |
newp.save() |
|
567 |
# the order is intentionally reversed so that it
|
|
568 |
# would break if we are not within a transaction.
|
|
569 |
# Has helped before.
|
|
570 |
h.save()
|
|
571 |
newp.save()
|
|
564 | 572 |
|
565 |
if p is not None and p.holding_set.count() == 0: |
|
566 |
p.delete() |
|
573 |
if p is not None and p.holding_set.count() == 0:
|
|
574 |
p.delete()
|
|
567 | 575 |
|
568 | 576 |
if rejected: |
569 | 577 |
raise ReturnButFail(rejected) |
... | ... | |
964 | 972 |
|
965 | 973 |
return timeline |
966 | 974 |
|
967 |
def _add(x, y): |
|
975 |
def _add(x, y, invert=False): |
|
976 |
if invert and y is None: |
|
977 |
return 0 |
|
968 | 978 |
if x is None or y is None: |
969 | 979 |
return None |
970 |
return x + y |
|
980 |
return x + y if not invert else x - y
|
|
971 | 981 |
|
972 | 982 |
def _update(dest, source, attr, delta): |
973 | 983 |
dest_attr = getattr(dest, attr) |
b/snf-quotaholder-app/quotaholder_django/test/simpletests.py | ||
---|---|---|
230 | 230 |
resource1 = self.rand_resource() |
231 | 231 |
|
232 | 232 |
r = self.qh.set_quota( |
233 |
set_quota=[(e0, resource0, k0) + (5, 5, 5, 5) + (0,),
|
|
233 |
set_quota=[(e0, resource0, k0) + (5, None, 5, 6) + (0,),
|
|
234 | 234 |
(e1, resource0, k1) + (5, 5, 5, 5) + (0,)]) |
235 | 235 |
self.assertEqual(r, []) |
236 | 236 |
|
237 | 237 |
r = self.qh.add_quota(clientkey=self.client, |
238 | 238 |
serial=1, |
239 |
add_quota=[(e0, resource0, k0, 0, (-2), None, 0), |
|
239 |
sub_quota=[(e0, resource0, k0, 0, None, 1, 1)], |
|
240 |
add_quota=[(e0, resource0, k0, 0, 3, None, 0), |
|
241 |
# new holding |
|
240 | 242 |
(e0, resource1, k0, 0, None, 5, 5)]) |
241 | 243 |
self.assertEqual(r, []) |
242 | 244 |
|
243 | 245 |
r = self.qh.get_quota(get_quota=[(e0, resource0, k0), |
244 | 246 |
(e0, resource1, k0)]) |
245 |
self.assertEqual(r, [(e0, resource0, 5, 5 - 2, None, 5)
|
|
247 |
self.assertEqual(r, [(e0, resource0, 5, 3, None, 5)
|
|
246 | 248 |
+ DEFAULT_HOLDING + (0,), |
247 | 249 |
(e0, resource1, 0, None, 5, 5) |
248 | 250 |
+ DEFAULT_HOLDING + (0,)]) |
... | ... | |
250 | 252 |
# repeated serial |
251 | 253 |
r = self.qh.add_quota(clientkey=self.client, |
252 | 254 |
serial=1, |
253 |
add_quota=[(e0, resource0, k0, 0, 2, None, 0),
|
|
254 |
(e0, resource1, k0, 0, None, (-5), 0)])
|
|
255 |
self.assertEqual(r, [(e0, resource0), (e0, resource1)])
|
|
255 |
sub_quota=[(e0, resource1, k0, 0, None, (-5), 0)],
|
|
256 |
add_quota=[(e0, resource0, k0, 0, 2, None, 0)])
|
|
257 |
self.assertEqual(r, [(e0, resource1), (e0, resource0)])
|
|
256 | 258 |
|
257 | 259 |
r = self.qh.query_serials(clientkey=self.client, serials=[1, 2]) |
258 | 260 |
self.assertEqual(r, [1]) |
Also available in: Unified diff