Statistics
| Branch: | Tag: | Revision:

root / snf-astakos-app / astakos / api / quotas.py @ 8299ef23

History | View | Annotate | Download (9.8 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

    
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.register import get_resources
44
from astakos.im.quotas import get_user_quotas, service_get_quotas
45

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

    
49
from .util import (json_response, is_integer, are_integer,
50
                   user_from_token, component_from_token)
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)
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
    data = request.GET
92
    client_key = str(request.component_instance)
93

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

    
97

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

    
114

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

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

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

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

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

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

    
175
    return json_response(data, status_code=status_code)
176

    
177

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

    
188
    return serial
189

    
190

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

    
197

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

    
204

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

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

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

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

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

    
239
    return json_response(data)
240

    
241

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

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

    
260

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

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

    
277
    client_key = str(request.component_instance)
278

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

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

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

    
292
    return response