Statistics
| Branch: | Tag: | Revision:

root / snf-astakos-app / astakos / api / quotas.py @ 3a1bed03

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
from astakos.api.util import json_response, is_integer, are_integer
40

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

    
44
from astakos.im.api.user import user_from_token
45
from astakos.im.api.service import service_from_token
46

    
47
from astakos.im.resources import get_resources
48
from astakos.im.quotas import get_user_quotas, service_get_quotas
49

    
50
import astakos.quotaholder.exception as qh_exception
51
import astakos.quotaholder.callpoint as qh
52

    
53

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

    
60

    
61
@api.api_method(http_method='GET', token_required=True, user_required=False)
62
@service_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.service_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
    result = get_resources()
77
    return json_response(result)
78

    
79

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

    
90

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

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

    
100

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

    
117

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

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

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

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

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

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

    
178
    return json_response(data, status_code=status_code)
179

    
180

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

    
191
    return serial
192

    
193

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

    
200

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

    
207

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

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

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

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

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

    
242
    return json_response(data)
243

    
244

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

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

    
263

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

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

    
280
    client_key = str(request.service_instance)
281

    
282
    accept = 'accept' in input_data
283
    reject = 'reject' in input_data
284

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

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

    
295
    return response