Revision 16bcc6e8

/dev/null
1
from .api import *
2
from .http import QuotaholderClient
3

  
4
QH_PRACTICALLY_INFINITE = 10 ** 32
/dev/null
1
# Copyright 2012 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 .quotaholder import QuotaholderAPI, QH_PRACTICALLY_INFINITE
35
from .exception import (CallError,
36
                        InvalidKeyError, NoEntityError,
37
                        NoQuantityError, NoCapacityError,
38
                        ExportLimitError, ImportLimitError,
39
                        CorruptedError, InvalidDataError,
40
                        DuplicateError)
41

  
42
API_Spec = QuotaholderAPI
/dev/null
1
# Copyright 2012 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 synnefo.lib.commissioning import (CallError, register_exception,
35
                                       InvalidDataError, CorruptedError)
36

  
37
@register_exception
38
class CommissionException(CallError):
39
    pass
40

  
41
@register_exception
42
class InvalidKeyError(CommissionException):
43
    pass
44

  
45
@register_exception
46
class NoEntityError(CommissionException):
47
    pass
48

  
49
@register_exception
50
class CommissionValueException(CommissionException):
51
    def __init__(self, *args, **kw):
52
        super(CommissionValueException, self).__init__(*args, **kw)
53
        kwargs = self.kwargs
54

  
55
        self.source    = kwargs['source']
56
        self.target    = kwargs['target']
57
        self.resource  = kwargs['resource']
58
        self.requested = kwargs['requested']
59
        self.current   = kwargs['current']
60
        self.limit     = kwargs['limit']
61

  
62
@register_exception
63
class NoQuantityError(CommissionValueException):
64
    pass
65

  
66
@register_exception
67
class NoCapacityError(CommissionValueException):
68
    pass
69

  
70
@register_exception
71
class ExportLimitError(CommissionValueException):
72
    pass
73

  
74
@register_exception
75
class ImportLimitError(CommissionValueException):
76
    pass
77

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

  
36

  
37
from synnefo.lib.commissioning import (CanonifyException, SpecifyException,
38
                                       Specificator, Null, Integer, Text,
39
                                       Tuple, ListOf, Dict, Args)
40
from random import choice, randint
41

  
42
Context             =   Dict(classname='Context', null=True, show=False)
43

  
44
class Name(Text):
45
    def init(self):
46
        self.opts.update({'regex': "[\w.:@+/-]+", 'maxlen':512})
47
        Text.init(self)
48

  
49
    def _random_choice(self, kw):
50
        alphabet = u'abcdef_1233490.:@/-αβγδεζ'
51
        length = randint(1, 48)
52
        return ''.join(choice(alphabet) for _ in xrange(length))
53

  
54
class Nonnegative(Integer):
55
    def init(self):
56
        self.opts.update({'minimum': 0})
57

  
58
class Positive(Integer):
59
    def init(self):
60
        self.opts.update({'minimum': 1})
61

  
62
QH_PRACTICALLY_INFINITE =   10**32
63

  
64
Serial              =   Positive(classname='Serial')
65

  
66
ClientKey           =   Name(classname='ClientKey')
67
Nothing             =   Null(classname='Nothing')
68

  
69
Entity              =   Name(classname='Entity')
70
Owner               =   Name(classname='Owner')
71
Key                 =   Text(classname='Key')
72
NewKey              =   Text(classname='Newkey')
73
OwnerKey            =   Text(classname='OwnerKey')
74
Resource            =   Name(classname='Resource')
75
Policy              =   Name(classname='Policy')
76

  
77
Quantity            =   Integer(classname='Quantity')
78
Capacity            =   Nonnegative(classname='Capacity')
79
ImportLimit         =   Nonnegative(classname='ImportLimit')
80
ExportLimit         =   Nonnegative(classname='ExportLimit')
81
QuantityDelta       =   Integer(classname='QuantityDelta')
82
CapacityDelta       =   Integer(classname='CapacityDelta')
83
ImportLimitDelta    =   Integer(classname='ImportLimitDelta')
84
ExportLimitDelta    =   Integer(classname='ExportLimitDelta')
85
Imported            =   Nonnegative(classname='Imported')
86
Exported            =   Nonnegative(classname='Exported')
87
Returned            =   Nonnegative(classname='Returned')
88
Released            =   Nonnegative(classname='Released')
89
Flags               =   Nonnegative(classname='Flags')
90
Index               =   Nonnegative(classname='Index')
91

  
92
Timepoint           =   Text(classname='Timepoint', maxlen=24)
93
Reason              =   Text(   classname   =   'Reason',
94
                                regex       =   '(ACCEPT|REJECT):.*',
95
                                maxlen      =   128         )
96

  
97
class QuotaholderAPI(Specificator):
98

  
99
    def create_entity   (
100
                self,
101
                context         =   Context,
102
                create_entity   =   ListOf(Entity, Owner, Key, OwnerKey, nonempty=1)
103
        ):
104
        rejected = ListOf(Index)
105
        return rejected
106

  
107
    def set_entity_key  (
108
                self,
109
                context         =   Context,
110
                set_entity_key  =   ListOf(Entity, Key, NewKey)
111
        ):
112
        rejected = ListOf(Entity)
113
        return rejected
114

  
115
    def list_entities   (
116
                self,
117
                context         =   Context,
118
                entity          =   Entity,
119
                key             =   Key
120
        ):
121
        entities = ListOf(Entity)
122
        return entities
123

  
124
    def get_entity  (
125
                self,
126
                context     =   Context,
127
                get_entity  =   ListOf(Entity, Key, nonempty=1)
128
        ):
129
        entities = ListOf(Entity, Owner)
130
        return entities
131

  
132
    def get_limits  (
133
                self,
134
                context     =   Context,
135
                get_limits  =   ListOf(Policy, nonempty=1)
136
        ):
137
        limits = ListOf(Policy, Quantity, Capacity,
138
                        ImportLimit, ExportLimit)
139
        return limits
140

  
141
    def set_limits  (
142
                self,
143
                context     =   Context,
144
                set_limits  =   ListOf( Policy, Quantity, Capacity,
145
                                        ImportLimit, ExportLimit,
146
                                        nonempty=1 )
147
        ):
148
        rejected = ListOf(Policy)
149
        return rejected
150

  
151
    def get_holding (
152
                self,
153
                context     =   Context,
154
                get_holding =   ListOf(Entity, Resource, Key)
155
        ):
156
        holdings = ListOf(  Entity, Resource, Policy,
157
                            Imported, Exported, Returned, Released, Flags   )
158
        return holdings
159

  
160
    def set_holding (
161
                self,
162
                context     =   Context,
163
                set_holding =   ListOf(Entity, Resource, Key, Policy, Flags)
164
        ):
165
        rejected = ListOf(Entity, Resource, Policy)
166
        return rejected
167

  
168
    def init_holding (
169
                self,
170
                context      =   Context,
171
                init_holding =   ListOf(Entity, Resource, Key, Policy,
172
                                        Imported, Exported, Returned, Released,
173
                                        Flags)
174
        ):
175
        rejected = ListOf(Index)
176
        return rejected
177

  
178
    def reset_holding (
179
                self,
180
                context       =   Context,
181
                reset_holding =   ListOf(Entity, Resource, Key,
182
                                        Imported, Exported, Returned, Released)
183
        ):
184
        rejected = ListOf(Index)
185
        return rejected
186

  
187
    def release_holding (
188
                self,
189
                context         =   Context,
190
                release_holding =   ListOf(Entity, Resource, Key)
191
        ):
192
        rejected = ListOf(Index)
193
        return rejected
194

  
195
    def list_resources  (
196
                self,
197
                context     =   Context,
198
                entity      =   Entity,
199
                key         =   Key
200
        ):
201
        resources = ListOf(Resource)
202
        return resources
203

  
204
    def list_holdings   (
205
                self,
206
                context         =   Context,
207
                list_holdings   =   ListOf(Entity, Key)
208
        ):
209

  
210
        rejected = ListOf(Entity)
211
        holdings_list = ListOf(ListOf(Entity, Resource,
212
                                      Imported, Exported,
213
                                      Returned, Released))
214
        return Tuple(holdings_list, rejected)
215

  
216
    def get_quota   (
217
                self,
218
                context     =   Context,
219
                get_quota   =   ListOf(Entity, Resource, Key)
220
        ):
221
        quotas = ListOf(Entity, Resource,
222
                        Quantity, Capacity,
223
                        ImportLimit, ExportLimit,
224
                        Imported, Exported,
225
                        Returned, Released,
226
                        Flags)
227
        return quotas
228

  
229
    def set_quota   (
230
                self,
231
                context     =   Context,
232
                set_quota   =   ListOf( Entity, Resource, Key,
233
                                        Quantity, Capacity,
234
                                        ImportLimit, ExportLimit, Flags )
235
        ):
236
        rejected = ListOf(Entity, Resource)
237
        return rejected
238

  
239
    def add_quota   (
240
                self,
241
                context     =   Context,
242
                clientkey   =   ClientKey,
243
                serial      =   Serial,
244
                sub_quota   =   ListOf( Entity, Resource, Key,
245
                                        QuantityDelta, CapacityDelta,
246
                                        ImportLimitDelta, ExportLimitDelta ),
247
                add_quota   =   ListOf( Entity, Resource, Key,
248
                                        QuantityDelta, CapacityDelta,
249
                                        ImportLimitDelta, ExportLimitDelta )
250
        ):
251
        rejected = ListOf(Entity, Resource)
252
        return rejected
253

  
254
    def query_serials   (
255
                self,
256
                context     =   Context,
257
                clientkey   =   ClientKey,
258
                serials     =   ListOf(Serial)
259
        ):
260
        return ListOf(Serial)
261

  
262
    def ack_serials (
263
                self,
264
                context     =   Context,
265
                clientkey   =   ClientKey,
266
                serials     =   ListOf(Serial)
267
        ):
268
        return Nothing
269

  
270
    def issue_commission    (
271
                self,
272
                context     =   Context,
273
                target      =   Entity,
274
                key         =   Key,
275
                clientkey   =   ClientKey,
276
                name        =   Text(default=''),
277
                provisions  =   ListOf(Entity, Resource, Quantity)
278
        ):
279
        return Serial
280

  
281
    def accept_commission   (
282
                self,
283
                context     =   Context,
284
                clientkey   =   ClientKey,
285
                serials     =   ListOf(Serial),
286
                reason      =   Text(default='ACCEPT')
287
        ):
288
        return Nothing
289

  
290
    def reject_commission   (
291
                self,
292
                context     =   Context,
293
                clientkey   =   ClientKey,
294
                serials     =   ListOf(Serial),
295
                reason      =   Text(default='REJECT')
296
        ):
297
        return Nothing
298

  
299
    def get_pending_commissions (
300
                    self,
301
                    context     =   Context,
302
                    clientkey   =   ClientKey
303
        ):
304
        pending = ListOf(Serial)
305
        return pending
306

  
307
    def resolve_pending_commissions (
308
                    self,
309
                    context     =   Context,
310
                    clientkey   =   ClientKey,
311
                    max_serial  =   Serial,
312
                    accept_set  =   ListOf(Serial)
313
        ):
314
        return Nothing
315

  
316
    def release_entity  (
317
                self,
318
                context         =   Context,
319
                release_entity  =   ListOf(Entity, Key, nonempty=1)
320
        ):
321
        rejected = ListOf(Entity)
322
        return rejected
323

  
324
    def get_timeline    (
325
                self,
326
                context         =   Context,
327
                after           =   Timepoint,
328
                before          =   Timepoint,
329
                get_timeline    =   ListOf(Entity, Resource, Key)
330
        ):
331
        timeline = ListOf(Dict(
332
                            serial                      =   Serial,
333
                            source                      =   Entity,
334
                            target                      =   Entity,
335
                            resource                    =   Resource,
336
                            name                        =   Name(),
337
                            quantity                    =   Quantity,
338
                            source_allocated            =   Quantity,
339
                            source_allocated_through    =   Quantity,
340
                            source_inbound              =   Quantity,
341
                            source_inbound_through      =   Quantity,
342
                            source_outbound             =   Quantity,
343
                            source_outbound_through     =   Quantity,
344
                            target_allocated            =   Quantity,
345
                            target_allocated_through    =   Quantity,
346
                            target_inbound              =   Quantity,
347
                            target_inbound_through      =   Quantity,
348
                            target_outbound             =   Quantity,
349
                            target_outbound_through     =   Quantity,
350
                            issue_time                  =   Timepoint,
351
                            log_time                    =   Timepoint,
352
                            reason                      =   Reason,
353

  
354
                            strict  =   True))
355
        return timeline
356

  
/dev/null
1
# Copyright 2012 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 synnefo.lib.commissioning import Callpoint, CallError
35
from objpool.http import PooledHTTPConnection
36
from .api import QuotaholderAPI
37

  
38
from json import loads as json_loads, dumps as json_dumps
39
import logging
40
from urlparse import urlparse
41

  
42
logger = logging.getLogger(__name__)
43

  
44

  
45
class QuotaholderClient(Callpoint):
46

  
47
    api_spec = QuotaholderAPI()
48

  
49
    def __init__(self, base_url, token='', poolsize=1000):
50
        super(QuotaholderClient, self).__init__()
51
        self._url = base_url
52
        parsed = urlparse(base_url)
53
        self._netloc = parsed.netloc
54
        self._scheme = parsed.scheme
55
        basepath = parsed.path
56
        if not basepath.endswith('/'):
57
            basepath += '/'
58
        self._basepath = basepath
59
        self._token = token
60
        self._poolsize = poolsize
61

  
62
    def do_make_call(self, api_call, data):
63

  
64
        gettable = ['list', 'get', 'read']
65
        method = ('GET' if any(api_call.startswith(x) for x in gettable)
66
                  else 'POST')
67

  
68
        path = self._basepath + api_call
69
        json_data = json_dumps(data)
70

  
71
        logger.debug("%s %s\n%s\n<<<\n", method, path, json_data[:128])
72
        headers = {'X-Auth-Token': self._token}
73
        with PooledHTTPConnection(scheme=self._scheme,
74
                                  netloc=self._netloc,
75
                                  size=self._poolsize) as conn:
76
            conn.request(method, path, body=json_data, headers=headers)
77
            resp = conn.getresponse()
78
            body = resp.read()
79

  
80
        logger.debug(">>>\nStatus: %s", resp.status)
81
        logger.debug("\n%s\n<<<\n", body[:128] if body else None)
82

  
83
        status = int(resp.status)
84
        if status == 200:
85
            return json_loads(body)
86
        else:
87
            try:
88
                error = json_loads(body)
89
            except ValueError:
90
                exc = CallError(body, call_error='ValueError')
91
            else:
92
                exc = CallError.from_dict(error)
93
            raise exc

Also available in: Unified diff