Statistics
| Branch: | Tag: | Revision:

root / snf-quotaholder-app / quotaholder_django / quotaholder_app / callpoint.py @ 5d996aea

History | View | Annotate | Download (29.8 kB)

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.quotaholder.api import (
35
                            QuotaholderAPI,
36
                            InvalidKeyError, NoEntityError,
37
                            NoQuantityError, NoCapacityError,
38
                            ExportLimitError, ImportLimitError,
39
                            DuplicateError)
40

    
41
from synnefo.lib.commissioning import \
42
    Callpoint, CorruptedError, InvalidDataError
43
from synnefo.lib.commissioning.utils.newname import newname
44

    
45
from django.db.models import Q
46
from django.db import transaction, IntegrityError
47
from .models import (Holder, Entity, Policy, Holding,
48
                     Commission, Provision, ProvisionLog, now,
49
                     db_get_entity, db_get_holding, db_get_policy,
50
                     db_get_commission, db_filter_provision)
51

    
52

    
53
class QuotaholderDjangoDBCallpoint(Callpoint):
54

    
55
    api_spec = QuotaholderAPI()
56

    
57
    http_exc_lookup = {
58
        CorruptedError:   550,
59
        InvalidDataError: 400,
60
        InvalidKeyError:  401,
61
        NoEntityError:    404,
62
        NoQuantityError:  413,
63
        NoCapacityError:  413,
64
    }
65

    
66
    def init_connection(self, connection):
67
        if connection is not None:
68
            raise ValueError("Cannot specify connection args with %s" %
69
                             type(self).__name__)
70
        pass
71

    
72
    def commit(self):
73
        transaction.commit()
74

    
75
    def rollback(self):
76
        transaction.rollback()
77

    
78
    def do_make_call(self, call_name, data):
79
        call_fn = getattr(self, call_name, None)
80
        if not call_fn:
81
            m = "cannot find call '%s'" % (call_name,)
82
            raise CorruptedError(m)
83

    
84
        return call_fn(**data)
85

    
86
    def create_entity(self, context={}, create_entity=()):
87
        rejected = []
88
        append = rejected.append
89

    
90
        for idx, (entity, owner, key, ownerkey) in enumerate(create_entity):
91
            try:
92
                owner = Entity.objects.get(entity=owner, key=ownerkey)
93
            except Entity.DoesNotExist:
94
                append(idx)
95
                continue
96

    
97
            try:
98
                e = Entity.objects.get(entity=entity)
99
                append(idx)
100
            except Entity.DoesNotExist:
101
                e = Entity.objects.create(entity=entity,
102
                                          owner=owner,
103
                                          key=key)
104
        return rejected
105

    
106
    def set_entity_key(self, context={}, set_entity_key=()):
107
        rejected = []
108
        append = rejected.append
109

    
110
        for entity, key, newkey in set_entity_key:
111
            try:
112
                e = db_get_entity(entity=entity, key=key, for_update=True)
113
            except Entity.DoesNotExist:
114
                append(entity)
115
                continue
116

    
117
            e.key = newkey
118
            e.save()
119

    
120
        return rejected
121

    
122
    def list_entities(self, context={}, entity=None, key=None):
123
        try:
124
            e = Entity.objects.get(entity=entity, key=key)
125
        except Entity.DoesNotExist:
126
            m = "Entity '%s' does not exist" % (entity,)
127
            raise NoEntityError(m)
128

    
129
        children = e.entities.all()
130
        entities = [e.entity for e in children]
131
        return entities
132

    
133
    def get_entity(self, context={}, get_entity=()):
134
        entities = []
135
        append = entities.append
136

    
137
        for entity, key in get_entity:
138
            try:
139
                e = Entity.objects.get(entity=entity, key=key)
140
            except Entity.DoesNotExist:
141
                continue
142

    
143
            append((entity, e.owner.entity))
144

    
145
        return entities
146

    
147
    def get_limits(self, context={}, get_limits=()):
148
        limits = []
149
        append = limits.append
150

    
151
        for policy in get_limits:
152
            try:
153
                p = Policy.objects.get(policy=policy)
154
            except Policy.DoesNotExist:
155
                continue
156

    
157
            append((policy, p.quantity, p.capacity,
158
                    p.import_limit, p.export_limit))
159

    
160
        return limits
161

    
162
    def set_limits(self, context={}, set_limits=()):
163

    
164
        for (   policy, quantity, capacity,
165
                import_limit, export_limit  ) in set_limits:
166

    
167
                try:
168
                    policy = db_get_policy(policy=policy, for_update=True)
169
                except Policy.DoesNotExist:
170
                    Policy.objects.create(  policy=policy,
171
                                            quantity=quantity,
172
                                            capacity=capacity,
173
                                            import_limit=import_limit,
174
                                            export_limit=export_limit   )
175
                else:
176
                    policy.quantity = quantity
177
                    policy.capacity = capacity
178
                    policy.export_limit = export_limit
179
                    policy.import_limit = import_limit
180
                    policy.save()
181

    
182
        return ()
183

    
184
    def get_holding(self, context={}, get_holding=()):
185
        holdings = []
186
        append = holdings.append
187

    
188
        for entity, resource, key in get_holding:
189
            try:
190
                h = Holding.objects.get(entity=entity, resource=resource)
191
            except Holding.DoesNotExist:
192
                continue
193

    
194
            if h.entity.key != key:
195
                continue
196

    
197
            append((h.entity.entity, h.resource, h.policy.policy,
198
                    h.imported, h.exported,
199
                    h.returned, h.released, h.flags))
200

    
201
        return holdings
202

    
203
    def _set_holding(self, entity, resource, policy, flags):
204
        try:
205
            h = db_get_holding(entity=entity, resource=resource,
206
                               for_update=True)
207
            h.policy = p
208
            h.flags = flags
209
            h.save()
210
        except Holding.DoesNotExist:
211
            h = Holding.objects.create( entity=e, resource=resource,
212
                                        policy=p, flags=flags      )
213
        return h
214

    
215
    def set_holding(self, context={}, set_holding=()):
216
        rejected = []
217
        append = rejected.append
218

    
219
        for entity, resource, key, policy, flags in set_holding:
220
            try:
221
                e = Entity.objects.get(entity=entity, key=key)
222
            except Entity.DoesNotExist:
223
                append((entity, resource, policy))
224
                continue
225

    
226
            if e.key != key:
227
                append((entity, resource, policy))
228
                continue
229

    
230
            try:
231
                p = Policy.objects.get(policy=policy)
232
            except Policy.DoesNotExist:
233
                append((entity, resource, policy))
234
                continue
235

    
236
            try:
237
                h = db_get_holding(entity=entity, resource=resource,
238
                                   for_update=True)
239
                h.policy = p
240
                h.flags = flags
241
                h.save()
242
            except Holding.DoesNotExist:
243
                h = Holding.objects.create( entity=e, resource=resource,
244
                                            policy=p, flags=flags      )
245

    
246
        return rejected
247

    
248
    def _init_holding(self, entity, resource, policy,
249
                          imported, exported, returned, released,
250
                          flags):
251
        try:
252
            h = db_get_holding(entity=entity, resource=resource,
253
                               for_update=True)
254
        except Holding.DoesNotExist:
255
            h = Holding(entity=entity, resource=resource)
256

    
257
        h.policy = policy
258
        h.flags = flags
259
        h.imported=imported
260
        h.importing=imported
261
        h.exported=exported
262
        h.exporting=exported
263
        h.returned=returned
264
        h.returning=returned
265
        h.released=released
266
        h.releasing=released
267
        h.save()
268

    
269
    def init_holding(self, context={}, init_holding=()):
270
        rejected = []
271
        append = rejected.append
272

    
273
        for idx, sfh in enumerate(init_holding):
274
            (entity, resource, key, policy,
275
             imported, exported, returned, released,
276
             flags) = sfh
277
            try:
278
                e = Entity.objects.get(entity=entity, key=key)
279
            except Entity.DoesNotExist:
280
                append(idx)
281
                continue
282

    
283
            if e.key != key:
284
                append(idx)
285
                continue
286

    
287
            try:
288
                p = Policy.objects.get(policy=policy)
289
            except Policy.DoesNotExist:
290
                append(idx)
291
                continue
292

    
293
            self._init_holding(e, resource, p,
294
                                   imported, exported,
295
                                   returned, released,
296
                                   flags)
297
        return rejected
298

    
299
    def reset_holding(self, context={}, reset_holding=()):
300
        rejected = []
301
        append = rejected.append
302

    
303
        for idx, tpl in enumerate(reset_holding):
304
            (entity, resource, key,
305
             imported, exported, returned, released) = tpl
306
            try:
307
                e = Entity.objects.get(entity=entity, key=key)
308
            except Entity.DoesNotExist:
309
                append(idx)
310
                continue
311

    
312
            try:
313
                h = db_get_holding(entity=entity, resource=resource,
314
                                   for_update=True)
315
                h.imported=imported
316
                h.importing=imported
317
                h.exported=exported
318
                h.exporting=exported
319
                h.returned=returned
320
                h.returning=returned
321
                h.released=released
322
                h.releasing=released
323
                h.save()
324
            except Holding.DoesNotExist:
325
                append(idx)
326
                continue
327

    
328
        return rejected
329

    
330
    def _check_pending(self, entity, resource):
331
        cs = Commission.objects.filter(entity=entity)
332
        cs = [c for c in cs if c.provisions.filter(resource=resource)]
333
        as_target = [c.serial for c in cs]
334

    
335
        ps = Provision.objects.filter(entity=entity, resource=resource)
336
        as_source = [p.serial.serial for p in ps]
337

    
338
        return as_target + as_source
339

    
340
    def _actual_quantity(self, holding):
341
        hp = holding.policy
342
        return hp.quantity + (holding.imported + holding.returned -
343
                              holding.exported - holding.released)
344

    
345
    def _new_policy_name(self):
346
        return newname('policy_')
347

    
348
    def _increase_resource(self, entity, resource, amount):
349
        try:
350
            h = db_get_holding(entity=entity, resource=resource,
351
                               for_update=True)
352
        except Holding.DoesNotExist:
353
            h = Holding(entity=entity, resource=resource)
354
            p = Policy.objects.create(policy=self._new_policy_name(),
355
                                      quantity=0)
356
            h.policy = p
357
        h.imported += amount
358
        h.save()
359

    
360
    def release_holding(self, context={}, release_holding=()):
361
        rejected = []
362
        append = rejected.append
363

    
364
        for idx, (entity, resource, key) in enumerate(release_holding):
365
            try:
366
                h = db_get_holding(entity=entity, resource=resource,
367
                                   for_update=True)
368
            except Holding.DoesNotExist:
369
                append(idx)
370
                continue
371

    
372
            if h.entity.key != key:
373
                append(idx)
374
                continue
375

    
376
            if self._check_pending(entity, resource):
377
                append(idx)
378
                continue
379

    
380
            q = self._actual_quantity(h)
381
            if q > 0:
382
                owner = h.entity.owner
383
                self._increase_resource(owner, resource, q)
384

    
385
            h.delete()
386

    
387
        return rejected
388

    
389
    def list_resources(self, context={}, entity=None, key=None):
390
        try:
391
            e = Entity.objects.get(entity=entity)
392
        except Entity.DoesNotExist:
393
            m = "No such entity '%s'" % (entity,)
394
            raise NoEntityError(m)
395

    
396
        if e.key != key:
397
            m = "Invalid key for entity '%s'" % (entity,)
398
            raise InvalidKeyError(m)
399

    
400
        holdings = e.holding_set.filter(entity=entity)
401
        resources = [h.resource for h in holdings]
402
        return resources
403

    
404
    def list_holdings(self, context={}, list_holdings=()):
405
        rejected = []
406
        reject = rejected.append
407
        holdings_list = []
408
        append = holdings_list.append
409

    
410
        for entity, key in list_holdings:
411
            try:
412
                e = Entity.objects.get(entity=entity)
413
                if e.key != key:
414
                    raise Entity.DoesNotExist("wrong key")
415
            except Entity.DoesNotExist:
416
                reject(entity)
417
                continue
418

    
419
            holdings = e.holding_set.filter(entity=entity)
420
            append([[entity, h.resource,
421
                     h.imported, h.exported, h.returned, h.released]
422
                        for h in holdings])
423

    
424
        return holdings_list, rejected
425

    
426
    def get_quota(self, context={}, get_quota=()):
427
        quotas = []
428
        append = quotas.append
429

    
430
        for entity, resource, key in get_quota:
431
            try:
432
                h = Holding.objects.get(entity=entity, resource=resource)
433
            except Holding.DoesNotExist:
434
                continue
435

    
436
            if h.entity.key != key:
437
                continue
438

    
439
            p = h.policy
440

    
441
            append((h.entity.entity, h.resource, p.quantity, p.capacity,
442
                    p.import_limit, p.export_limit,
443
                    h.imported, h.exported,
444
                    h.returned, h.released,
445
                    h.flags))
446

    
447
        return quotas
448

    
449
    def set_quota(self, context={}, set_quota=()):
450
        rejected = []
451
        append = rejected.append
452

    
453
        for (   entity, resource, key,
454
                quantity, capacity,
455
                import_limit, export_limit, flags  ) in set_quota:
456

    
457
                try:
458
                    e = Entity.objects.get(entity=entity, key=key)
459
                except Entity.DoesNotExist:
460
                    append((entity, resource))
461
                    continue
462

    
463
                policy = newname('policy_')
464
                newp = Policy   (
465
                            policy=policy,
466
                            quantity=quantity,
467
                            capacity=capacity,
468
                            import_limit=import_limit,
469
                            export_limit=export_limit
470
                )
471

    
472
                try:
473
                    h = db_get_holding(entity=entity, resource=resource,
474
                                       for_update=True)
475
                    p = h.policy
476
                    h.policy = newp
477
                    h.flags = flags
478
                except Holding.DoesNotExist:
479
                    h = Holding(entity=e, resource=resource,
480
                                policy=newp, flags=flags)
481
                    p = None
482

    
483
                # the order is intentionally reversed so that it
484
                # would break if we are not within a transaction.
485
                # Has helped before.
486
                h.save()
487
                newp.save()
488

    
489
                if p is not None and p.holding_set.count() == 0:
490
                    p.delete()
491

    
492
        return rejected
493

    
494
    def issue_commission(self,  context     =   {},
495
                                clientkey   =   None,
496
                                target      =   None,
497
                                key         =   None,
498
                                name        =   None,
499
                                provisions  =   ()  ):
500

    
501
        try:
502
            t = Entity.objects.get(entity=target)
503
        except Entity.DoesNotExist:
504
            m = "No target entity '%s'" % (target,)
505
            raise NoEntityError(m)
506
        else:
507
            if t.key != key:
508
                m = "Invalid key for target entity '%s'" % (target,)
509
                raise InvalidKeyError(m)
510

    
511
        create = Commission.objects.create
512
        commission = create(entity_id=target, clientkey=clientkey, name=name)
513
        serial = commission.serial
514

    
515
        checked = []
516
        for entity, resource, quantity in provisions:
517

    
518
            if entity == target:
519
                m = "Cannot issue commission from an entity to itself (%s)" % (
520
                    entity,)
521
                raise InvalidDataError(m)
522

    
523
            ent_res = entity, resource
524
            if ent_res in checked:
525
                m = "Duplicate provision for %s.%s" % ent_res
526
                raise DuplicateError(m)
527
            checked.append(ent_res)
528

    
529
            try:
530
                e = Entity.objects.get(entity=entity)
531
            except Entity.DoesNotExist:
532
                m = "No source entity '%s'" % (entity,)
533
                raise NoEntityError(m)
534

    
535
            release = 0
536
            if quantity < 0:
537
                release = 1
538

    
539
            try:
540
                h = db_get_holding(entity=entity, resource=resource,
541
                                   for_update=True)
542
            except Holding.DoesNotExist:
543
                m = ("There is not enough quantity "
544
                     "to allocate from in %s.%s" % (entity, resource))
545
                raise NoQuantityError(m)
546

    
547
            hp = h.policy
548

    
549
            if (hp.export_limit is not None and
550
                h.exporting + quantity > hp.export_limit):
551
                    m = ("Export limit reached for %s.%s" % (entity, resource))
552
                    raise ExportLimitError(m)
553

    
554
            if hp.quantity is not None:
555
                available = (+ hp.quantity + h.imported + h.returned
556
                             - h.exporting - h.releasing)
557

    
558
                if available - quantity < 0:
559
                    m = ("There is not enough quantity "
560
                         "to allocate from in %s.%s" % (entity, resource))
561
                    raise NoQuantityError(m)
562

    
563
            try:
564
                th = db_get_holding(entity=target, resource=resource,
565
                                    for_update=True)
566
            except Holding.DoesNotExist:
567
                m = ("There is not enough capacity "
568
                     "to allocate into in %s.%s" % (target, resource))
569
                raise NoCapacityError(m)
570

    
571
            tp = th.policy
572

    
573
            if (tp.import_limit is not None and
574
                th.importing + quantity > tp.import_limit):
575
                    m = ("Import limit reached for %s.%s" % (target, resource))
576
                    raise ImportLimitError(m)
577

    
578
            if tp.capacity is not None:
579
                capacity = (+ tp.capacity + th.exported + th.released
580
                            - th.importing - th.returning)
581

    
582
                if capacity - quantity < 0:
583
                        m = ("There is not enough capacity "
584
                             "to allocate into in %s.%s" % (target, resource))
585
                        raise NoCapacityError(m)
586

    
587
            Provision.objects.create(   serial      =   commission,
588
                                        entity      =   e,
589
                                        resource    =   resource,
590
                                        quantity    =   quantity   )
591
            if release:
592
                h.returning -= quantity
593
                th.releasing -= quantity
594
            else:
595
                h.exporting += quantity
596
                th.importing += quantity
597

    
598
            h.save()
599
            th.save()
600

    
601
        return serial
602

    
603
    def _log_provision(self, commission, s_holding, t_holding,
604
                             provision, log_time, reason):
605

    
606
        s_entity = s_holding.entity
607
        s_policy = s_holding.policy
608
        t_entity = t_holding.entity
609
        t_policy = t_holding.policy
610

    
611
        ProvisionLog.objects.create(
612
                        serial              =   commission.serial,
613
                        name                =   commission.name,
614
                        source              =   s_entity.entity,
615
                        target              =   t_entity.entity,
616
                        resource            =   provision.resource,
617
                        source_quantity     =   s_policy.quantity,
618
                        source_capacity     =   s_policy.capacity,
619
                        source_import_limit =   s_policy.import_limit,
620
                        source_export_limit =   s_policy.export_limit,
621
                        source_imported     =   s_holding.imported,
622
                        source_exported     =   s_holding.exported,
623
                        source_returned     =   s_holding.returned,
624
                        source_released     =   s_holding.released,
625
                        target_quantity     =   t_policy.quantity,
626
                        target_capacity     =   t_policy.capacity,
627
                        target_import_limit =   t_policy.import_limit,
628
                        target_export_limit =   t_policy.export_limit,
629
                        target_imported     =   t_holding.imported,
630
                        target_exported     =   t_holding.exported,
631
                        target_returned     =   t_holding.returned,
632
                        target_released     =   t_holding.released,
633
                        delta_quantity      =   provision.quantity,
634
                        issue_time          =   commission.issue_time,
635
                        log_time            =   log_time,
636
                        reason              =   reason)
637

    
638
    def accept_commission(self, context={}, clientkey=None,
639
                                serials=(), reason=''):
640
        log_time = now()
641

    
642
        for serial in serials:
643
            try:
644
                c = db_get_commission(clientkey=clientkey, serial=serial,
645
                                      for_update=True)
646
            except Commission.DoesNotExist:
647
                return
648

    
649
            t = c.entity
650

    
651
            provisions = db_filter_provision(serial=serial, for_update=True)
652
            for pv in provisions:
653
                try:
654
                    h = db_get_holding(entity=pv.entity.entity,
655
                                       resource=pv.resource, for_update=True)
656
                    th = db_get_holding(entity=t, resource=pv.resource,
657
                                        for_update=True)
658
                except Holding.DoesNotExist:
659
                    m = "Corrupted provision"
660
                    raise CorruptedError(m)
661

    
662
                quantity = pv.quantity
663
                release = 0
664
                if quantity < 0:
665
                    release = 1
666

    
667
                if release:
668
                    h.returned -= quantity
669
                    th.released -= quantity
670
                else:
671
                    h.exported += quantity
672
                    th.imported += quantity
673

    
674
                reason = 'ACCEPT:' + reason[-121:]
675
                self._log_provision(c, h, th, pv, log_time, reason)
676
                h.save()
677
                th.save()
678
                pv.delete()
679
            c.delete()
680

    
681
        return
682

    
683
    def reject_commission(self, context={}, clientkey=None,
684
                                serials=(), reason=''):
685
        log_time = now()
686

    
687
        for serial in serials:
688
            try:
689
                c = db_get_commission(clientkey=clientkey, serial=serial,
690
                                      for_update=True)
691
            except Commission.DoesNotExist:
692
                return
693

    
694
            t = c.entity
695

    
696
            provisions = db_filter_provision(serial=serial, for_update=True)
697
            for pv in provisions:
698
                try:
699
                    h = db_get_holding(entity=pv.entity.entity,
700
                                       resource=pv.resource, for_update=True)
701
                    th = db_get_holding(entity=t, resource=pv.resource,
702
                                        for_update=True)
703
                except Holding.DoesNotExist:
704
                    m = "Corrupted provision"
705
                    raise CorruptedError(m)
706

    
707
                quantity = pv.quantity
708
                release = 0
709
                if quantity < 0:
710
                    release = 1
711

    
712
                if release:
713
                    h.returning += quantity
714
                    th.releasing += quantity
715
                else:
716
                    h.exporting -= quantity
717
                    th.importing -= quantity
718

    
719
                reason = 'REJECT:' + reason[-121:]
720
                self._log_provision(c, h, th, pv, log_time, reason)
721
                h.save()
722
                th.save()
723
                pv.delete()
724
            c.delete()
725

    
726
        return
727

    
728
    def get_pending_commissions(self, context={}, clientkey=None):
729
        pending = Commission.objects.filter(clientkey=clientkey)\
730
                                    .values_list('serial', flat=True)
731
        return pending
732

    
733
    def resolve_pending_commissions(self,   context={}, clientkey=None,
734
                                            max_serial=None, accept_set=()  ):
735
        accept_set = set(accept_set)
736
        pending = self.get_pending_commissions(context=context, clientkey=clientkey)
737
        pending = sorted(pending)
738

    
739
        accept = self.accept_commission
740
        reject = self.reject_commission
741

    
742
        for serial in pending:
743
            if serial > max_serial:
744
                break
745

    
746
            if serial in accept_set:
747
                accept(context=context, clientkey=clientkey, serials=[serial])
748
            else:
749
                reject(context=context, clientkey=clientkey, serials=[serial])
750

    
751
        return
752

    
753
    def release_entity(self, context={}, release_entity=()):
754
        rejected = []
755
        append = rejected.append
756
        for entity, key in release_entity:
757
            try:
758
                e = db_get_entity(entity=entity, key=key, for_update=True)
759
            except Entity.DoesNotExist:
760
                append(entity)
761
                continue
762

    
763
            if e.entities.count() != 0:
764
                append(entity)
765
                continue
766

    
767
            if e.holding_set.count() != 0:
768
                append(entity)
769
                continue
770

    
771
            e.delete()
772

    
773
        return rejected
774

    
775
    def get_timeline(self, context={}, after="", before="Z", get_timeline=()):
776
        entity_set = set()
777
        e_add = entity_set.add
778
        resource_set = set()
779
        r_add = resource_set.add
780

    
781
        for entity, resource, key in get_timeline:
782
            if entity not in entity_set:
783
                try:
784
                    e = Entity.objects.get(entity=entity, key=key)
785
                    e_add(entity)
786
                except Entity.DoesNotExist:
787
                    continue
788

    
789
            r_add((entity, resource))
790

    
791
        chunk_size = 65536
792
        nr = 0
793
        timeline = []
794
        append = timeline.append
795
        filterlogs = ProvisionLog.objects.filter
796
        if entity_set:
797
            q_entity = Q(source__in = entity_set) | Q(target__in = entity_set)
798
        else:
799
            q_entity = Q()
800

    
801
        while 1:
802
            logs = filterlogs(  q_entity,
803
                                issue_time__gt      =   after,
804
                                issue_time__lte     =   before,
805
                                reason__startswith  =   'ACCEPT:'   )
806

    
807
            logs = logs.order_by('issue_time')
808
            #logs = logs.values()
809
            logs = logs[:chunk_size]
810
            nr += len(logs)
811
            if not logs:
812
                break
813
            for g in logs:
814
                if ((g.source, g.resource) not in resource_set
815
                    or (g.target, g.resource) not in resource_set):
816
                        continue
817

    
818
                o = {
819
                    'serial'                    :   g.serial,
820
                    'source'                    :   g.source,
821
                    'target'                    :   g.target,
822
                    'resource'                  :   g.resource,
823
                    'name'                      :   g.name,
824
                    'quantity'                  :   g.delta_quantity,
825
                    'source_allocated'          :   g.source_allocated(),
826
                    'source_allocated_through'  :   g.source_allocated_through(),
827
                    'source_inbound'            :   g.source_inbound(),
828
                    'source_inbound_through'    :   g.source_inbound_through(),
829
                    'source_outbound'           :   g.source_outbound(),
830
                    'source_outbound_through'   :   g.source_outbound_through(),
831
                    'target_allocated'          :   g.target_allocated(),
832
                    'target_allocated_through'  :   g.target_allocated_through(),
833
                    'target_inbound'            :   g.target_inbound(),
834
                    'target_inbound_through'    :   g.target_inbound_through(),
835
                    'target_outbound'           :   g.target_outbound(),
836
                    'target_outbound_through'   :   g.target_outbound_through(),
837
                    'issue_time'                :   g.issue_time,
838
                    'log_time'                  :   g.log_time,
839
                    'reason'                    :   g.reason,
840
                }
841

    
842
                append(o)
843

    
844
            after = g.issue_time
845
            if after >= before:
846
                break
847

    
848
        return timeline
849

    
850

    
851
API_Callpoint = QuotaholderDjangoDBCallpoint
852