Commit 5b1f8225 authored by Giorgos Korfiatis's avatar Giorgos Korfiatis
Browse files

Cleanup obsolete quota code

parent 26416d10
# Copyright 2011, 2012, 2013 GRNET S.A. All rights reserved.
#
# Redistribution and use in source and binary forms, with or
# without modification, are permitted provided that the following
# conditions are met:
#
# 1. Redistributions of source code must retain the above
# copyright notice, this list of conditions and the following
# disclaimer.
#
# 2. Redistributions in binary form must reproduce the above
# copyright notice, this list of conditions and the following
# disclaimer in the documentation and/or other materials
# provided with the distribution.
#
# THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
# OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR
# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
# USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
#
# The views and conclusions contained in the software and
# documentation are those of the authors and should not be
# interpreted as representing official policies, either expressed
# or implied, of GRNET S.A.
import logging
import itertools
from functools import wraps
from collections import namedtuple
from django.utils.translation import ugettext as _
from astakos.im.settings import (
QUOTAHOLDER_URL, QUOTAHOLDER_TOKEN, LOGGING_LEVEL)
from astakos.quotaholder.callpoint import QuotaholderDjangoDBCallpoint
from synnefo.util.number import strbigdec
from astakos.im.settings import QUOTAHOLDER_POOLSIZE
from astakos.quotaholder.api import QH_PRACTICALLY_INFINITE
logger = logging.getLogger(__name__)
inf = float('inf')
clientkey = 'astakos'
_client = None
def get_client():
global _client
if _client:
return _client
_client = QuotaholderDjangoDBCallpoint()
return _client
def set_quota(payload):
c = get_client()
if not c:
return
if payload == []:
return []
result = c.set_quota(context={}, set_quota=payload)
logger.debug('set_quota: %s rejected: %s' % (payload, result))
return result
def quotas_per_user_from_get(lst):
limits = {}
usage = {}
for holder, resource, c, imp_min, imp_max, st_min, st_max, flags in lst:
userlimits = limits.get(holder, {})
userlimits[resource] = c
limits[holder] = userlimits
user_usage = usage.get(holder, {})
user_usage[resource] = imp_max
usage[holder] = user_usage
return limits, usage
def qh_get_quota(users, resources):
c = get_client()
if not c:
return
payload = []
append = payload.append
for user in users:
for resource in resources:
append((user.uuid, str(resource)),)
if payload == []:
return []
result = c.get_quota(context={}, get_quota=payload)
logger.debug('get_quota: %s rejected: %s' % (payload, result))
return result
def qh_get_quotas(users, resources):
result = qh_get_quota(users, resources)
return quotas_per_user_from_get(result)
SetQuotaPayload = namedtuple('SetQuotaPayload', ('holder',
'resource',
'capacity',
'flags'))
QuotaLimits = namedtuple('QuotaLimits', ('holder',
'resource',
'capacity',
))
def register_quotas(quotas):
if not quotas:
return
payload = []
append = payload.append
for uuid, userquotas in quotas.iteritems():
for resource, capacity in userquotas.iteritems():
append(SetQuotaPayload(
holder=uuid,
resource=str(resource),
capacity=capacity,
flags=0))
return set_quota(payload)
def send_quotas(userquotas):
if not userquotas:
return
payload = []
append = payload.append
for holder, quotas in userquotas.iteritems():
for resource, q in quotas.iteritems():
append(SetQuotaPayload(
holder=holder,
resource=str(resource),
capacity=q.capacity,
flags=0))
return set_quota(payload)
def initial_commission(resources):
c = get_client()
if not c:
return
for resource in resources:
s = c.issue_commission(
clientkey=clientkey,
target=str(resource.service),
name='initialization',
provisions=[(None, str(resource), QH_PRACTICALLY_INFINITE)])
c.accept_commission(clientkey=clientkey, serials=[s], reason='')
def register_resources(resources):
payload = list(SetQuotaPayload(
holder=str(resource.service),
resource=str(resource),
capacity=QH_PRACTICALLY_INFINITE,
flags=0) for resource in resources)
set_quota(payload)
initial_commission(resources)
def qh_add_quota(sub_list, add_list):
context = {}
c = get_client()
result = c.add_quota(context=context,
sub_quota=sub_list,
add_quota=add_list)
return result
from datetime import datetime
strptime = datetime.strptime
timefmt = '%Y-%m-%dT%H:%M:%S.%f'
SECOND_RESOLUTION = 1
def total_seconds(timedelta_object):
return timedelta_object.seconds + timedelta_object.days * 86400
def iter_timeline(timeline, before):
if not timeline:
return
for t in timeline:
yield t
t = dict(t)
t['issue_time'] = before
yield t
def _usage_units(timeline, after, before, details=0):
t_total = 0
uu_total = 0
t_after = strptime(after, timefmt)
t_before = strptime(before, timefmt)
t0 = t_after
u0 = 0
for point in iter_timeline(timeline, before):
issue_time = point['issue_time']
if issue_time <= after:
u0 = point['target_allocated_through']
continue
t = strptime(issue_time, timefmt) if issue_time <= before else t_before
t_diff = int(total_seconds(t - t0) * SECOND_RESOLUTION)
t_total += t_diff
uu_cost = u0 * t_diff
uu_total += uu_cost
t0 = t
u0 = point['target_allocated_through']
target = point['target']
if details:
yield (target,
point['resource'],
point['name'],
issue_time,
uu_cost,
uu_total)
if not t_total:
return
yield (target,
'total',
point['resource'],
issue_time,
uu_total / t_total,
uu_total)
def usage_units(timeline, after, before, details=0):
return list(_usage_units(timeline, after, before, details=details))
def traffic_units(timeline, after, before, details=0):
tu_total = 0
target = None
issue_time = None
for point in timeline:
issue_time = point['issue_time']
if issue_time <= after:
continue
if issue_time > before:
break
target = point['target']
tu = point['target_allocated_through']
tu_total += tu
if details:
yield (target,
point['resource'],
point['name'],
issue_time,
tu,
tu_total)
if not tu_total:
return
yield (target,
'total',
point['resource'],
issue_time,
tu_total // len(timeline),
tu_total)
def timeline_charge(entity, resource, after, before, details, charge_type):
if charge_type == 'charge_usage':
charge_units = usage_units
elif charge_type == 'charge_traffic':
charge_units = traffic_units
else:
m = 'charge type %s not supported' % charge_type
raise ValueError(m)
quotaholder = QuotaholderClient(QUOTAHOLDER_URL, token=QUOTAHOLDER_TOKEN,
poolsize=QUOTAHOLDER_POOLSIZE)
timeline = quotaholder.get_timeline(
context={},
after=after,
before=before,
get_timeline=[[entity, resource]])
cu = charge_units(timeline, after, before, details=details)
return cu
......@@ -78,8 +78,6 @@ from astakos.im.project_notif import (
application_submit_notify, application_approve_notify,
application_deny_notify,
project_termination_notify, project_suspension_notify)
from astakos.im.endpoints.qh import (
register_quotas, qh_get_quota, qh_add_quota)
import astakos.im.messages as astakos_messages
......@@ -376,8 +374,7 @@ class SendNotificationError(SendMailError):
def get_quota(users):
resources = get_resource_names()
return qh_get_quota(users, resources)
pass
### PROJECT VIEWS ###
......
......@@ -71,10 +71,6 @@ from astakos.im.settings import (
SITENAME, SERVICES, MODERATION_ENABLED, RESOURCES_PRESENTATION_DATA,
PROJECT_MEMBER_JOIN_POLICIES, PROJECT_MEMBER_LEAVE_POLICIES, PROJECT_ADMINS)
from astakos.im import settings as astakos_settings
from astakos.im.endpoints.qh import (
send_quotas, qh_get_quotas,
register_resources, qh_add_quota, QuotaLimits,
)
from astakos.im import auth_providers as auth
import astakos.im.messages as astakos_messages
......
......@@ -46,8 +46,6 @@ from astakos.quotaholder.commission import (
from astakos.quotaholder.utils.newname import newname
from astakos.quotaholder.api import QH_PRACTICALLY_INFINITE
from django.db.models import Q, Count
from django.db.models import Q
from .models import (Holding,
Commission, Provision, ProvisionLog,
now,
......@@ -57,117 +55,6 @@ from .models import (Holding,
class QuotaholderDjangoDBCallpoint(object):
def _init_holding(self,
holder, resource, limit,
imported_min, imported_max,
flags):
try:
h = db_get_holding(holder=holder, resource=resource,
for_update=True)
except Holding.DoesNotExist:
h = Holding(holder=holder, resource=resource)
h.limit = limit
h.flags = flags
h.imported_min = imported_min
h.imported_max = imported_max
h.save()
def init_holding(self, context=None, init_holding=[]):
rejected = []
append = rejected.append
for idx, sfh in enumerate(init_holding):
(holder, resource, limit,
imported_min, imported_max,
flags) = sfh
self._init_holding(holder, resource, limit,
imported_min, imported_max,
flags)
if rejected:
raise QuotaholderError(rejected)
return rejected
def reset_holding(self, context=None, reset_holding=[]):
rejected = []
append = rejected.append
for idx, tpl in enumerate(reset_holding):
(holder, source, resource,
imported_min, imported_max) = tpl
try:
h = db_get_holding(holder=holder,
source=source,
resource=resource,
for_update=True)
h.imported_min = imported_min
h.imported_max = imported_max
h.save()
except Holding.DoesNotExist:
append(idx)
continue
if rejected:
raise QuotaholderError(rejected)
return rejected
def _check_pending(self, holding):
ps = Provision.objects.filter(holding=holding)
return ps.count()
def release_holding(self, context=None, release_holding=[]):
rejected = []
append = rejected.append
for idx, (holder, source, resource) in enumerate(release_holding):
try:
h = db_get_holding(holder=holder,
source=source,
resource=resource,
for_update=True)
except Holding.DoesNotExist:
append(idx)
continue
if self._check_pending(h):
append(idx)
continue
if h.imported_max > 0:
append(idx)
continue
h.delete()
if rejected:
raise QuotaholderError(rejected)
return rejected
def list_resources(self, context=None, holder=None):
holdings = Holding.objects.filter(holder=holder)
resources = [h.resource for h in holdings]
return resources
def list_holdings(self, context=None, list_holdings=[]):
rejected = []
reject = rejected.append
holdings_list = []
append = holdings_list.append
for holder in list_holdings:
holdings = list(Holding.objects.filter(holder=holder))
if not holdings:
reject(holder)
continue
append([(holder, h.source, h.resource,
h.imported_min, h.imported_max)
for h in holdings])
return holdings_list, rejected
def get_holder_quota(self, holders=None, sources=None, resources=None):
holdings = Holding.objects.filter(holder__in=holders)
......@@ -185,28 +72,6 @@ class QuotaholderDjangoDBCallpoint(object):
return quotas
def get_quota(self, context=None, get_quota=[]):
quotas = []
append = quotas.append
holders = set(holder for holder, r in get_quota)
hs = Holding.objects.filter(holder__in=holders)
holdings = {}
for h in hs:
holdings[(h.holder, h.source, h.resource)] = h
for holder, source, resource in get_quota:
try:
h = holdings[(holder, source, resource)]
except:
continue
append((h.holder, h.source, h.resource, h.limit,
h.imported_min, h.imported_max,
h.flags))
return quotas
def set_holder_quota(self, quotas):
holders = quotas.keys()
hs = Holding.objects.filter(holder__in=holders).select_for_update()
......@@ -227,89 +92,6 @@ class QuotaholderDjangoDBCallpoint(object):
h.limit = limit
h.save()
def set_quota(self, context=None, set_quota=[]):
rejected = []
append = rejected.append
q_holdings = Q()
holders = []
for (holder, source, resource, _, _) in set_quota:
holders.append(holder)
hs = Holding.objects.filter(holder__in=holders).select_for_update()
holdings = {}
for h in hs:
holdings[(h.holder, h.source, h.resource)] = h
for (holder, source, resource,
limit,
flags) in set_quota:
try:
h = holdings[(holder, source, resource)]
h.flags = flags
except KeyError:
h = Holding(holder=holder,
source=source,
resource=resource,
flags=flags)
h.limit = limit
h.save()
holdings[(holder, source, resource)] = h
if rejected:
raise QuotaholderError(rejected)
return rejected
def add_quota(self,
context=None,
sub_quota=[], add_quota=[]):
rejected = []
append = rejected.append
sources = sub_quota + add_quota
q_holdings = Q()
holders = []
for (holder, resource, _) in sources:
holders.append(holder)
hs = Holding.objects.filter(holder__in=holders).select_for_update()
holdings = {}
for h in hs:
holdings[(h.holder, h.resource)] = h
for removing, source in [(True, sub_quota), (False, add_quota)]:
for (holder, resource,
limit,
) in source:
try:
h = holdings[(holder, resource)]
current_limit = h.limit
except KeyError:
if removing:
append((holder, resource))
continue
h = Holding(holder=holder, resource=resource, flags=0)
current_limit = 0
h.limit = (current_limit - limit if removing else
current_limit + limit)
if h.limit < 0:
append((holder, resource))
continue
h.save()
holdings[(holder, resource)] = h
if rejected:
raise QuotaholderError(rejected)
return rejected
def issue_commission(self,
context=None,
clientkey=None,
......@@ -524,76 +306,5 @@ class QuotaholderDjangoDBCallpoint(object):
failed = list(set(failed_ac + failed_re))
return accepted, rejected, failed
def get_timeline(self, context=None, after="", before="Z", get_timeline=[]):
holder_set = set()
e_add = holder_set.add
resource_set = set()
r_add = resource_set.add
for holder, resource in get_timeline:
if holder not in holder_set: