quotas.py 9.78 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
# Copyright 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.

from django.utils import simplejson as json
from django.views.decorators.csrf import csrf_exempt
from django.http import HttpResponse

38
from snf_django.lib.db.transaction import commit_on_success_strict
39 40

from snf_django.lib import api
41
from snf_django.lib.api.faults import BadRequest, ItemNotFound
42

43
from astakos.im.resources import get_resources
44
from astakos.im.quotas import get_user_quotas, service_get_quotas
45

46 47
import astakos.quotaholder_app.exception as qh_exception
import astakos.quotaholder_app.callpoint as qh
48

49 50
from .util import (json_response, is_integer, are_integer,
                   user_from_token, service_from_token)
51

52 53
@api.api_method(http_method='GET', token_required=True, user_required=False)
@user_from_token
54 55 56 57 58
def quotas(request, user=None):
    result = get_user_quotas(user)
    return json_response(result)


59 60 61
@api.api_method(http_method='GET', token_required=True, user_required=False)
@service_from_token
def service_quotas(request):
62 63
    user = request.GET.get('user')
    users = [user] if user is not None else None
64
    result = service_get_quotas(request.service_instance, users=users)
65 66 67 68

    if user is not None and result == {}:
        raise ItemNotFound("No such user '%s'" % user)

69 70 71
    return json_response(result)


72
@api.api_method(http_method='GET', token_required=False, user_required=False)
Giorgos Korfiatis's avatar
Giorgos Korfiatis committed
73 74 75 76 77
def resources(request):
    result = get_resources()
    return json_response(result)


78 79 80 81 82 83 84 85 86 87 88
@csrf_exempt
def commissions(request):
    method = request.method
    if method == 'GET':
        return get_pending_commissions(request)
    elif method == 'POST':
        return issue_commission(request)
    else:
        raise BadRequest('Method not allowed.')


89 90
@api.api_method(http_method='GET', token_required=True, user_required=False)
@service_from_token
91 92
def get_pending_commissions(request):
    data = request.GET
93
    client_key = str(request.service_instance)
94 95 96 97 98

    result = qh.get_pending_commissions(clientkey=client_key)
    return json_response(result)


99 100 101 102 103 104 105 106 107 108
def _provisions_to_list(provisions):
    lst = []
    for provision in provisions:
        try:
            holder = provision['holder']
            source = provision['source']
            resource = provision['resource']
            quantity = provision['quantity']
            key = (holder, source, resource)
            lst.append((key, quantity))
109 110
            if not is_integer(quantity):
                raise ValueError()
111
        except (TypeError, KeyError, ValueError):
112
            raise BadRequest("Malformed provision %s" % str(provision))
113 114 115
    return lst


116
@csrf_exempt
117 118
@api.api_method(http_method='POST', token_required=True, user_required=False)
@service_from_token
119 120
def issue_commission(request):
    data = request.raw_post_data
121 122 123 124
    try:
        input_data = json.loads(data)
    except json.JSONDecodeError:
        raise BadRequest("POST data should be in json format.")
125

126
    client_key = str(request.service_instance)
127 128 129
    provisions = input_data.get('provisions')
    if provisions is None:
        raise BadRequest("Provisions are missing.")
130
    if not isinstance(provisions, list):
131 132
        raise BadRequest("Provisions should be a list.")

133
    provisions = _provisions_to_list(provisions)
134
    force = input_data.get('force', False)
135 136 137
    if not isinstance(force, bool):
        raise BadRequest('"force" option should be a boolean.')

138
    auto_accept = input_data.get('auto_accept', False)
139 140 141 142
    if not isinstance(auto_accept, bool):
        raise BadRequest('"auto_accept" option should be a boolean.')

    name = input_data.get('name', "")
143
    if not isinstance(name, basestring):
144
        raise BadRequest("Commission name should be a string.")
145 146 147 148

    try:
        result = _issue_commission(clientkey=client_key,
                                   provisions=provisions,
149
                                   name=name,
150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179
                                   force=force,
                                   accept=auto_accept)
        data = {"serial": result}
        status_code = 201
    except (qh_exception.NoCapacityError,
            qh_exception.NoQuantityError) as e:
        status_code = 413
        body = {"message": e.message,
                "code": status_code,
                "data": e.data,
                }
        data = {"overLimit": body}
    except qh_exception.NoHoldingError as e:
        status_code = 404
        body = {"message": e.message,
                "code": status_code,
                "data": e.data,
                }
        data = {"itemNotFound": body}
    except qh_exception.InvalidDataError as e:
        status_code = 400
        body = {"message": e.message,
                "code": status_code,
                }
        data = {"badRequest": body}

    return json_response(data, status_code=status_code)


@commit_on_success_strict()
180
def _issue_commission(clientkey, provisions, name, force, accept):
181 182
    serial = qh.issue_commission(clientkey=clientkey,
                                 provisions=provisions,
183
                                 name=name,
184 185
                                 force=force)
    if accept:
186 187
        done = qh.resolve_pending_commission(clientkey=clientkey,
                                             serial=serial)
188 189 190 191

    return serial


192 193 194 195 196 197 198 199 200 201 202 203
def notFoundCF(serial):
    body = {"code": 404,
            "message": "serial %s does not exist" % serial,
            }
    return {"itemNotFound": body}


def conflictingCF(serial):
    body = {"code": 400,
            "message": "cannot both accept and reject serial %s" % serial,
            }
    return {"badRequest": body}
204 205 206


@csrf_exempt
207 208
@api.api_method(http_method='POST', token_required=True, user_required=False)
@service_from_token
209 210 211
@commit_on_success_strict()
def resolve_pending_commissions(request):
    data = request.raw_post_data
212 213 214 215
    try:
        input_data = json.loads(data)
    except json.JSONDecodeError:
        raise BadRequest("POST data should be in json format.")
216

217
    client_key = str(request.service_instance)
218 219 220
    accept = input_data.get('accept', [])
    reject = input_data.get('reject', [])

221 222 223 224
    if not isinstance(accept, list) or not isinstance(reject, list):
        m = '"accept" and "reject" should reference lists of serials.'
        raise BadRequest(m)

225 226 227
    if not are_integer(accept) or not are_integer(reject):
        raise BadRequest("Serials should be integer.")

228 229 230
    result = qh.resolve_pending_commissions(clientkey=client_key,
                                            accept_set=accept,
                                            reject_set=reject)
231 232 233 234
    accepted, rejected, notFound, conflicting = result
    notFound = [(serial, notFoundCF(serial)) for serial in notFound]
    conflicting = [(serial, conflictingCF(serial)) for serial in conflicting]
    cloudfaults = notFound + conflicting
235 236 237 238 239 240 241 242
    data = {'accepted': accepted,
            'rejected': rejected,
            'failed': cloudfaults
            }

    return json_response(data)


243 244
@api.api_method(http_method='GET', token_required=True, user_required=False)
@service_from_token
245 246
def get_commission(request, serial):
    data = request.GET
247
    client_key = str(request.service_instance)
248 249 250 251
    try:
        serial = int(serial)
    except ValueError:
        raise BadRequest("Serial should be an integer.")
252 253 254 255 256 257 258 259 260 261 262

    try:
        data = qh.get_commission(clientkey=client_key,
                                 serial=serial)
        status_code = 200
        return json_response(data, status_code)
    except qh_exception.NoCommissionError as e:
        return HttpResponse(status=404)


@csrf_exempt
263 264
@api.api_method(http_method='POST', token_required=True, user_required=False)
@service_from_token
265 266 267
@commit_on_success_strict()
def serial_action(request, serial):
    data = request.raw_post_data
268 269 270 271 272
    try:
        input_data = json.loads(data)
    except json.JSONDecodeError:
        raise BadRequest("POST data should be in json format.")

273 274 275 276
    try:
        serial = int(serial)
    except ValueError:
        raise BadRequest("Serial should be an integer.")
277

278
    client_key = str(request.service_instance)
279 280 281 282 283 284 285

    accept = 'accept' in input_data
    reject = 'reject' in input_data

    if accept == reject:
        raise BadRequest('Specify either accept or reject action.')

286 287 288
    result = qh.resolve_pending_commission(clientkey=client_key,
                                           serial=serial,
                                           accept=accept)
289 290 291 292 293
    response = HttpResponse()
    if not result:
        response.status_code = 404

    return response