Statistics
| Branch: | Tag: | Revision:

root / snf-astakos-app / astakos / api / quotas.py @ 381a548c

History | View | Annotate | Download (9.7 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.register import get_resources
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
    result = get_user_quotas(request.user)
56
    return json_response(result)
57

    
58

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

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

    
69
    return json_response(result)
70

    
71

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

    
77

    
78
@csrf_exempt
79
def commissions(request):
80
    method = request.method
81
    if method == 'GET':
82
        return get_pending_commissions(request)
83
    elif method == 'POST':
84
        return issue_commission(request)
85
    return api.api_method_not_allowed(request, allowed_methods=['GET', 'POST'])
86

    
87

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

    
93
    result = qh.get_pending_commissions(clientkey=client_key)
94
    return json_response(result)
95

    
96

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

    
113

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

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

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

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

    
140
    name = input_data.get('name', "")
141
    if not isinstance(name, basestring):
142
        raise BadRequest("Commission name should be a string.")
143

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

    
174
    return json_response(data, status_code=status_code)
175

    
176

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

    
186
    return serial
187

    
188

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

    
195

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

    
202

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

    
214
    client_key = str(request.component_instance)
215
    accept = input_data.get('accept', [])
216
    reject = input_data.get('reject', [])
217

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

    
222
    if not are_integer(accept) or not are_integer(reject):
223
        raise BadRequest("Serials should be integer.")
224

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

    
237
    return json_response(data)
238

    
239

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

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

    
258

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

    
270
    try:
271
        serial = int(serial)
272
    except ValueError:
273
        raise BadRequest("Serial should be an integer.")
274

    
275
    client_key = str(request.component_instance)
276

    
277
    accept = 'accept' in input_data
278
    reject = 'reject' in input_data
279

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

    
283
    result = qh.resolve_pending_commission(clientkey=client_key,
284
                                           serial=serial,
285
                                           accept=accept)
286
    response = HttpResponse()
287
    if not result:
288
        response.status_code = 404
289

    
290
    return response