Revision d2b32360 snf-astakos-app/astakos/im/models.py

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

  

Also available in: Unified diff