Statistics
| Branch: | Tag: | Revision:

root / snf-astakos-app / astakos / api / quotas.py @ 85ae5a4c

History | View | Annotate | Download (9.9 kB)

1
# Copyright 2013 GRNET S.A. All rights reserved.
2
#
3
# Redistribution and use in source and binary forms, with or
4
# without modification, are permitted provided that the following
5
# conditions are met:
6
#
7
#   1. Redistributions of source code must retain the above
8
#      copyright notice, this list of conditions and the following
9
#      disclaimer.
10
#
11
#   2. Redistributions in binary form must reproduce the above
12
#      copyright notice, this list of conditions and the following
13
#      disclaimer in the documentation and/or other materials
14
#      provided with the distribution.
15
#
16
# THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
17
# OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19
# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR
20
# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
23
# USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
24
# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
26
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27
# POSSIBILITY OF SUCH DAMAGE.
28
#
29
# The views and conclusions contained in the software and
30
# documentation are those of the authors and should not be
31
# interpreted as representing official policies, either expressed
32
# or implied, of GRNET S.A.
33

    
34
from django.utils import simplejson as json
35
from django.views.decorators.csrf import csrf_exempt
36
from django.http import HttpResponse
37
from django.db import transaction
38

    
39
from snf_django.lib import api
40
from snf_django.lib.api.faults import BadRequest, ItemNotFound
41

    
42
from astakos.im import register
43
from astakos.im.quotas import get_user_quotas, service_get_quotas
44

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

    
48
from .util import (json_response, is_integer, are_integer,
49
                   user_from_token, component_from_token)
50

    
51

    
52
@api.api_method(http_method='GET', token_required=True, user_required=False)
53
@user_from_token
54
def quotas(request):
55
    visible_resources = register.get_api_visible_resources()
56
    resource_names = [r.name for r in visible_resources]
57
    result = get_user_quotas(request.user, resources=resource_names)
58
    return json_response(result)
59

    
60

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

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

    
71
    return json_response(result)
72

    
73

    
74
@api.api_method(http_method='GET', token_required=False, user_required=False)
75
def resources(request):
76
    resources = register.get_api_visible_resources()
77
    result = register.resources_to_dict(resources)
78
    return json_response(result)
79

    
80

    
81
@csrf_exempt
82
def commissions(request):
83
    method = request.method
84
    if method == 'GET':
85
        return get_pending_commissions(request)
86
    elif method == 'POST':
87
        return issue_commission(request)
88
    return api.api_method_not_allowed(request)
89

    
90

    
91
@api.api_method(http_method='GET', token_required=True, user_required=False)
92
@component_from_token
93
def get_pending_commissions(request):
94
    client_key = str(request.component_instance)
95

    
96
    result = qh.get_pending_commissions(clientkey=client_key)
97
    return json_response(result)
98

    
99

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

    
116

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

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

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

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

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

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

    
177
    return json_response(data, status_code=status_code)
178

    
179

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

    
189
    return serial
190

    
191

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

    
198

    
199
def conflictingCF(serial):
200
    body = {"code": 400,
201
            "message": "cannot both accept and reject serial %s" % serial,
202
            }
203
    return {"badRequest": body}
204

    
205

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

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

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

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

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

    
240
    return json_response(data)
241

    
242

    
243
@api.api_method(http_method='GET', token_required=True, user_required=False)
244
@component_from_token
245
def get_commission(request, serial):
246
    data = request.GET
247
    client_key = str(request.component_instance)
248
    try:
249
        serial = int(serial)
250
    except ValueError:
251
        raise BadRequest("Serial should be an integer.")
252

    
253
    try:
254
        data = qh.get_commission(clientkey=client_key,
255
                                 serial=serial)
256
        status_code = 200
257
        return json_response(data, status_code)
258
    except qh_exception.NoCommissionError:
259
        return HttpResponse(status=404)
260

    
261

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

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

    
278
    client_key = str(request.component_instance)
279

    
280
    accept = 'accept' in input_data
281
    reject = 'reject' in input_data
282

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

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

    
293
    return response