Statistics
| Branch: | Tag: | Revision:

root / commissioning / servers / quotaholder / django_backend / models.py @ 9619cc64

History | View | Annotate | Download (15.6 kB)

1

    
2
from commissioning.quotaholder \
3
import (QuotaholderCallpoint,  QuotaholderException,
4
        QuotaholderController, QuotaholderPhysical,
5
        QuotaholderCorrupted,  QuotaholderInvalidData,
6
        QuotaholderInvalidKey, QuotaholderNoEntity,
7
        QuotaholderNoQuantity, QuotaholderNoCapacity)
8

    
9

    
10
from django.db.models import Model, BigIntegerField, CharField, ForeignKey
11
from django.db import transaction
12

    
13

    
14
class Holder(Model):
15

    
16
    attribute   =   CharField(max_length=72, primary_key=True)
17
    intval      =   BigIntegerField()
18
    strval      =   CharField(max_length=72)
19

    
20

    
21
def alloc_serial(nr=1):
22
    if nr < 0:
23
        m = "Can only receive a positive argument, not %d" % (nr,)
24
        raise QuotaholderCorrupted(m)
25

    
26
    try:
27
        holder = Holder.objects.get(attribute='serial')
28
    except Holder.DoesNotExist:
29
        holder = Holder(attribute='serial', inval=1)
30

    
31
    serial = holder.serial
32
    holder.serial += nr
33
    holder.save()
34

    
35
    return serial
36

    
37

    
38
class Entity(Model):
39

    
40
    entity      =   CharField(max_length=72, primary_key=True)
41
    owner       =   ForeignKey('self', to_field='entity',
42
                               related_name='entities')
43
    key         =   CharField(max_length=72, null=False)
44

    
45

    
46
class Policy(Model):
47

    
48
    policy          =   CharField(max_length=72, primary_key=True)
49
    quantity        =   BigIntegerField(null=False, default=0)
50
    capacity        =   BigIntegerField(null=False, default=0)
51
    import_limit    =   BigIntegerField(null=False, default=0)
52
    export_limit    =   BigIntegerField(null=False, default=0)
53

    
54

    
55
class Holding(Model):
56

    
57
    entity      =   ForeignKey(Entity, to_field='entity')
58
    resource    =   CharField(max_length=72, null=False)
59

    
60
    policy      =   ForeignKey(Policy, to_field='policy')
61
    flags       =   BigIntegerField(null=False, default=0)
62

    
63
    imported    =   BigIntegerField(null=False, default=0)
64
    importing   =   BigIntegerField(null=False, default=0)
65
    exported    =   BigIntegerField(null=False, default=0)
66
    exporting   =   BigIntegerField(null=False, default=0)
67

    
68
    class Meta:
69
        unique_together = (('entity', 'resource'),)
70

    
71

    
72
from datetime import datetime
73

    
74
def now():
75
    return datetime.now().strftime('%Y-%m-%dT:%H:%M:%S.%f')[:24]
76

    
77

    
78
class Commission(Model):
79

    
80
    serial      =   BigIntegerField(primary_key=True, default=alloc_serial)
81
    entity      =   ForeignKey(Entity, to_field='entity')
82
    clientkey   =   CharField(max_length=72, null=False)
83
    issue_time  =   CharField(max_length=24, default=now)
84

    
85

    
86
class Provision(Model):
87

    
88
    serial      =   ForeignKey( Commission,
89
                                to_field='serial',
90
                                related_name='provisions'   )
91

    
92
    entity      =   ForeignKey(Entity, to_field='entity')
93
    resource    =   CharField(max_length=72, null=False)
94
    quantity    =   BigIntegerField(null=False)
95

    
96

    
97

    
98
class QuotaholderDjangoDBCallpoint(QuotaholderCallpoint):
99

    
100
    http_exc_lookup = {
101
        QuotaholderCorrupted:   550,
102
        QuotaholderInvalidData: 400,
103
        QuotaholderInvalidKey:  401,
104
        QuotaholderNoEntity:    404,
105
        QuotaholderNoQuantity:  413,
106
        QuotaholderNoCapacity:  413,
107
    }
108

    
109
    def init_connection(self, connection):
110
        if connection is not None:
111
            raise ValueError("Cannot specify connection args with %s" %
112
                             type(self).__name__)
113
        pass
114

    
115
    def commit(self):
116
        transaction.commit()
117

    
118
    def rollback(self):
119
        transaction.rollback()
120

    
121
    def do_make_call(self, call_name, data):
122
        call_fn = getattr(self, call_name, None)
123
        if not call_fn:
124
            m = "cannot find call '%s'" % (call_name,)
125
            raise QuotaholderCorrupted(m)
126

    
127
        return call_fn(**data)
128

    
129
    @classmethod
130
    def http_exception(cls, exc):
131
        if not isinstance(exc, QuotaholderException):
132
            raise exc
133

    
134
        body = str(exc.args)
135
        status = cls.http_exc_lookup.get(type(exc), 400)
136
        return status, body
137

    
138
    def create_entity(self, context={}, create_entity=()):
139
        rejected = []
140
        append = rejected.append
141

    
142
        for entity, owner, key, ownerkey in create_entity:
143
            try:
144
                owner = Entity.objects.get(entity=owner, key=ownerkey)
145
            except Entity.DoesNotExist:
146
                append(entity)
147

    
148
            Entity.objects.create(entity=entity, owner=owner, key=key)
149

    
150
        return rejected
151

    
152
    def list_entities(self, context={}, entity=None, key=None):
153
        try:
154
            e = Entity.objects.get(entity=entity, key=key)
155
        except Entity.DoesNotExist:
156
            m = "Entity '%s' does not exist" % (entity,)
157
            raise QuotaholderNoEntity(m)
158

    
159
        children = e.entities.all()
160
        entities = [e.entity for e in children]
161
        return entities
162

    
163
    def get_entity(self, context={}, get_entity=()):
164
        entities = []
165
        append = entities.append
166

    
167
        for entity, key in get_entity:
168
            try:
169
                Entity.objects.get(entity=entity, key=key)
170
            except Entity.DoesNotExist:
171
                continue
172

    
173
            append((entity, key))
174

    
175
        return entities
176

    
177
    def get_limits(self, context={}, get_limits=()):
178
        limits = []
179
        append = limits.append
180

    
181
        for entity, resource, key in get_limits:
182
            try:
183
                h = Holding.objects.get(entity=entity, resource=resource)
184
            except Policy.DoesNotExist:
185
                continue
186

    
187
            if h.entity.key != key:
188
                continue
189
            p = h.policy
190
            append((h.entity, h.resource, p.quantity, p.capacity,
191
                    p.import_limit, p.export_limit, h.flags))
192

    
193
        return limits
194

    
195
    def set_limits(self, context={}, set_limits=()):
196

    
197
        for (   policy, quantity, capacity,
198
                import_limit, export_limit  ) in set_limits:
199

    
200
                #XXX: create or replace?
201
                Policy.objects.create(  policy=policy,
202
                                        quantity=quantity,
203
                                        capacity=capacity,
204
                                        import_limit=import_limit,
205
                                        export_limit=export_limit   )
206

    
207
        return ()
208

    
209
    def get_holding(self, context={}, get_holding=()):
210
        holdings = []
211
        append = holdings.append
212

    
213
        for entity, resource, key in get_holding:
214
            try:
215
                h = Holding.objects.get(entity=entity, resource=resource)
216
            except Holding.DoesNotExist:
217
                continue
218

    
219
            if h.entity.key != key:
220
                continue
221

    
222
            append((h.entity, h.resource, h.policy,
223
                    h.imported, h.exported, h.flags))
224

    
225
        return holdings
226

    
227
    def set_holding(self, context={}, set_holding=()):
228
        rejected = []
229
        append = rejected.append
230

    
231
        for entity, resource, key, policy, flags in set_holding:
232
            try:
233
                e = Entity.objects.get(entity=entity, key=key)
234
            except Entity.DoesNotExist:
235
                append((entity, resource, policy))
236
                continue
237

    
238
            if e.key != key:
239
                append((entity, resource, policy))
240
                continue
241

    
242
            try:
243
                p = Policy.objects.get(policy=policy)
244
            except Policy.DoesNotExist:
245
                append((entity, resource, policy))
246
                continue
247

    
248
            try:
249
                h = Holding.objects.get(entity=entity, resource=resource)
250
                h.policy = p
251
                h.flags = flags
252
                h.save()
253
            except Holding.DoesNotExist:
254
                h = Holding.objects.create( entity=entity, resource=resource,
255
                                            policy=policy, flags=flags      )
256

    
257
        return rejected
258

    
259
    def list_resources(self, context={}, entity=None, key=None):
260
        try:
261
            e = Entity.objects.get()
262
        except Entity.DoesNotExist:
263
            m = "No such entity '%s'" % (entity,)
264
            raise QuotaholderNoEntity(m)
265

    
266
        if e.key != key:
267
            m = "Invalid key for entity '%s'" % (entity,)
268
            raise QuotaholderInvalidKey(m)
269

    
270
        holdings = e.holdings.filter(entity=entity)
271
        resources = [h.resource for h in holdings]
272
        return resources
273

    
274
    def get_quota(self, context={}, get_quota=()):
275
        quotas = []
276
        append = quotas.append
277

    
278
        for entity, resource, key in get_quota:
279
            try:
280
                h = Holding.objects.get(entity=entity, resource=resource)
281
            except Holding.DoesNotExist:
282
                continue
283

    
284
            if h.entity.key != key:
285
                continue
286

    
287
            p = h.policy
288

    
289
            append((h.entity.entity, h.resource, p.quantity, p.capacity,
290
                    p.import_limit, p.export_limit,
291
                    h.imported, h.exported, h.flags))
292

    
293
        return quotas
294

    
295
    def set_quota(self, context={}, set_quota=()):
296
        rejected = []
297
        append = rejected.append
298

    
299
        for (   entity, resource, key,
300
                quantity, capacity,
301
                import_limit, export_limit, flags  ) in set_quota:
302

    
303
                try:
304
                    h = Holding.objects.get(entity=entity, resource=resource)
305
                    if h.entity.key != key:
306
                        append((entity, resource))
307
                        continue
308
                except Holding.DoesNotExist:
309
                    append(entity, resource)
310

    
311
                p = h.policy
312
                policy = newname()
313
                newp = Policy.objects.create    (
314
                            policy=policy,
315
                            quantity=quantity,
316
                            capacity=capacity,
317
                            import_limit=import_limit,
318
                            export_limit=export_limit
319
                )
320

    
321
                h.policy = newp
322
                h.save()
323

    
324
                if p.holdings.count() == 0:
325
                    p.delete()
326

    
327
        return rejected
328

    
329
    def issue_commission(self,  context={}, clientkey=None,
330
                                target=None, key=None,
331
                                owner=None, ownerkey=None,
332
                                provisions=()               ):
333

    
334
        try:
335
            t = Entity.objects.get(entity=target)
336
        except Entity.DoesNotExist:
337
            create_entity = ((entity, owner, key, ownerkey),)
338
            rejected = self.create_entity(  context=context,
339
                                            create_entity=create_entity )
340
            if rejected:
341
                raise QuotaholderNoEntity("No target entity '%s'" % (target,))
342

    
343
            t = Entity.objects.get(entity=target)
344
        else:
345
            if t.key != key:
346
                m = "Invalid key for target entity '%s'" % (target,)
347
                raise QuotaholderInvalidKey(m)
348

    
349
        commission = Commission.objects.create( entity=target,
350
                                                clientkey=clientkey )
351
        serial = commission.serial
352

    
353
        for entity, resource, quantity in provisions:
354
            try:
355
                h = Holding.objects.get(entity=entity, resource=resource)
356
            except Holding.DoesNotExist:
357
                m = ("There is not enough quantity "
358
                     "to allocate from in %s.%s" % (entity, resource))
359
                raise QuotaholderNoQuantity(m)
360

    
361
            hp = h.policy
362

    
363
            if h.importing - h.exported + hp.quantity - quantity < 0:
364
                m = ("There is not enough quantity "
365
                     "to allocate from in %s.%s" % (entity, resource))
366
                raise QuotaholderNoQuantity(m)
367

    
368
            try:
369
                th = Holding.objects.get(entity=target, resource=resource)
370
            except Holding.DoesNotExist:
371
                m = ("There is not enough capacity "
372
                     "to allocate into in %s.%s" % (target, resource))
373
                raise QuotaholderNoCapacity(m)
374

    
375
            tp = th.policy
376

    
377
            if (    th.exported - th.importing - tp.quantity
378
                    + tp.capacity - quantity                ) < 0:
379

    
380
                    m = ("There is not enough capacity "
381
                         "to allocate into in %s.%s" % (target, resource))
382
                    raise QuotaholderNoCapacity(m)
383

    
384
            Provision.objects.create(   serial=serial,
385
                                        entity=entity,
386
                                        resource=resource,
387
                                        quantity=quantity   )
388

    
389
            h.exporting += quantity
390
            th.importing += quantity
391

    
392
            h.save()
393
            th.save()
394

    
395
        return serial
396

    
397
    def accept_commission(self, context={}, clientkey=None, serial=None):
398
        try:
399
            c = Commission.objects.get(clientkey=clientkey, serial=serial)
400
        except Commission.DoesNotExist:
401
            return
402

    
403
        t = c.entity
404

    
405
        provisions = Provision.objects.filter(  clientkey=clientkey,
406
                                                serial=serial       )
407
        for pv in provisions:
408
            pv.entity,
409
            pv.resource
410
            try:
411
                h = Holding.objects.get(entity=pv.entity.entity,
412
                                        resource=pv.resource    )
413
                th = Holding.objects.get(entity=t, resource=pv.resource)
414
            except Holding.DoesNotExist:
415
                m = "Corrupted provision"
416
                raise QuotaholderCorrupted(m)
417

    
418
            h.exported += pv.quantity
419
            th.imported += pv.quantity
420
            h.save()
421
            th.save()
422
            pv.delete()
423

    
424
        return
425

    
426
    def reject_commission(self, context={}, clientkey=None, serial=None):
427
        try:
428
            c = Commission.objects.get(clientkey=clientkey, serial=serial)
429
        except Commission.DoesNotExist:
430
            return
431

    
432
        t = c.entity
433

    
434
        provisions = Provision.objects.filter(  clientkey=clientkey,
435
                                                serial=serial       )
436
        for pv in provisions:
437
            pv.entity,
438
            pv.resource
439
            try:
440
                h = Holding.objects.get(entity=pv.entity.entity,
441
                                        resource=pv.resource    )
442
                th = Holding.objects.get(entity=t, resource=pv.resource)
443
            except Holding.DoesNotExist:
444
                m = "Corrupted provision"
445
                raise QuotaholderCorrupted(m)
446

    
447
            h.exporting -= pv.quantity
448
            th.importing -= pv.quantity
449
            h.save()
450
            th.save()
451
            pv.delete()
452

    
453
        return
454

    
455
    def get_pending_commissions(self, context={}, clientkey=None):
456
        pending = Commission.objects.filter(clientkey=clientkey)
457
        return pending
458

    
459
    def resolve_pending_commissions(self,   context={}, clientkey=None,
460
                                            max_serial=None, accept_set=()  ):
461
        accept_set = set(accept_set)
462
        pending = self.get_pending_commissions(clientkey=clientkey)
463
        pending = sorted(pending)
464

    
465
        accept = self.accept_commission
466
        reject = self.reject_commission
467

    
468
        for serial in pending:
469
            if serial > max_serial:
470
                break
471

    
472
            if serial in accept_set:
473
                accept(clientkey=clientkey, serial=serial)
474
            else:
475
                reject(clientkey=clientkey, serial=serial)
476

    
477
        return
478

    
479
    def release_entity(self, context={}, release_entity=()):
480
        rejected = []
481
        append = rejected.append
482
        for entity, key in release_entity:
483
            try:
484
                e = Entity.objects.get(entity=entity, key=key)
485
            except Entity.DoesNotExist:
486
                append(entity)
487
                continue
488

    
489
            if e.entities.count() != 0:
490
                append(entity)
491
                continue
492

    
493
            if e.holdings.count() != 0:
494
                append(entity)
495
                continue
496

    
497
            e.delete()
498

    
499
        return rejected
500