Statistics
| Branch: | Tag: | Revision:

root / snf-quotaholder-app / quotaholder_django / quotaholder_app / callpoint.py @ 4eb170f3

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

    
50

    
51
class QuotaholderDjangoDBCallpoint(Callpoint):
52

    
53
    api_spec = QuotaholderAPI()
54

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

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

    
70
    def commit(self):
71
        transaction.commit()
72

    
73
    def rollback(self):
74
        transaction.rollback()
75

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

    
82
        return call_fn(**data)
83

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

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

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

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

    
108
        for entity, key, newkey in set_entity_key:
109
            try:
110
                e = Entity.objects.get(entity=entity, key=key)
111
            except Entity.DoesNotExist:
112
                append(entity)
113
                continue
114

    
115
            e.key = newkey
116
            e.save()
117

    
118
        return rejected
119

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

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

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

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

    
141
            append((entity, e.owner.entity))
142

    
143
        return entities
144

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

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

    
155
            append((policy, p.quantity, p.capacity,
156
                    p.import_limit, p.export_limit))
157

    
158
        return limits
159

    
160
    def set_limits(self, context={}, set_limits=()):
161

    
162
        for (   policy, quantity, capacity,
163
                import_limit, export_limit  ) in set_limits:
164

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

    
180
        return ()
181

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

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

    
192
            if h.entity.key != key:
193
                continue
194

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

    
199
        return holdings
200

    
201
    def _set_holding(self, entity, resource, policy, flags):
202
        try:
203
            h = Holding.objects.get(entity=entity, resource=resource)
204
            h.policy = p
205
            h.flags = flags
206
            h.save()
207
        except Holding.DoesNotExist:
208
            h = Holding.objects.create( entity=e, resource=resource,
209
                                        policy=p, flags=flags      )
210
        return h
211

    
212
    def set_holding(self, context={}, set_holding=()):
213
        rejected = []
214
        append = rejected.append
215

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

    
223
            if e.key != key:
224
                append((entity, resource, policy))
225
                continue
226

    
227
            try:
228
                p = Policy.objects.get(policy=policy)
229
            except Policy.DoesNotExist:
230
                append((entity, resource, policy))
231
                continue
232

    
233
            try:
234
                h = Holding.objects.get(entity=entity, resource=resource)
235
                h.policy = p
236
                h.flags = flags
237
                h.save()
238
            except Holding.DoesNotExist:
239
                h = Holding.objects.create( entity=e, resource=resource,
240
                                            policy=p, flags=flags      )
241

    
242
        return rejected
243

    
244
    def _init_holding(self, entity, resource, policy,
245
                          imported, exported, returned, released,
246
                          flags):
247
        try:
248
            h = Holding.objects.get(entity=entity, resource=resource)
249
        except Holding.DoesNotExist:
250
            h = Holding(entity=entity, resource=resource)
251

    
252
        h.policy = policy
253
        h.flags = flags
254
        h.imported=imported
255
        h.importing=imported
256
        h.exported=exported
257
        h.exporting=exported
258
        h.returned=returned
259
        h.returning=returned
260
        h.released=released
261
        h.releasing=released
262
        h.save()
263

    
264
    def init_holding(self, context={}, init_holding=()):
265
        rejected = []
266
        append = rejected.append
267

    
268
        for idx, sfh in enumerate(init_holding):
269
            (entity, resource, key, policy,
270
             imported, exported, returned, released,
271
             flags) = sfh
272
            try:
273
                e = Entity.objects.get(entity=entity, key=key)
274
            except Entity.DoesNotExist:
275
                append(idx)
276
                continue
277

    
278
            if e.key != key:
279
                append(idx)
280
                continue
281

    
282
            try:
283
                p = Policy.objects.get(policy=policy)
284
            except Policy.DoesNotExist:
285
                append(idx)
286
                continue
287

    
288
            self._init_holding(e, resource, p,
289
                                   imported, exported,
290
                                   returned, released,
291
                                   flags)
292
        return rejected
293

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

    
298
        for idx, tpl in enumerate(reset_holding):
299
            (entity, resource, key,
300
             imported, exported, returned, released) = tpl
301
            try:
302
                e = Entity.objects.get(entity=entity, key=key)
303
            except Entity.DoesNotExist:
304
                append(idx)
305
                continue
306

    
307
            try:
308
                h = Holding.objects.get(entity=entity, resource=resource)
309
                h.imported=imported
310
                h.importing=imported
311
                h.exported=exported
312
                h.exporting=exported
313
                h.returned=returned
314
                h.returning=returned
315
                h.released=released
316
                h.releasing=released
317
                h.save()
318
            except Holding.DoesNotExist:
319
                append(idx)
320
                continue
321

    
322
        return rejected
323

    
324
    def _check_pending(self, entity, resource):
325
        cs = Commission.objects.filter(entity=entity)
326
        cs = [c for c in cs if c.provisions.filter(resource=resource)]
327
        as_target = [c.serial for c in cs]
328

    
329
        ps = Provision.objects.filter(entity=entity, resource=resource)
330
        as_source = [p.serial.serial for p in ps]
331

    
332
        return as_target + as_source
333

    
334
    def _actual_quantity(self, holding):
335
        hp = holding.policy
336
        return hp.quantity + (holding.imported + holding.returned -
337
                              holding.exported - holding.released)
338

    
339
    def _new_policy_name(self):
340
        return newname('policy_')
341

    
342
    def _increase_resource(self, entity, resource, amount):
343
        try:
344
            h = Holding.objects.get(entity=entity, resource=resource)
345
        except Holding.DoesNotExist:
346
            h = Holding(entity=entity, resource=resource)
347
            p = Policy.objects.create(policy=self._new_policy_name(),
348
                                      quantity=0)
349
            h.policy = p
350
        h.imported += amount
351
        h.save()
352

    
353
    def release_holding(self, context={}, release_holding=()):
354
        rejected = []
355
        append = rejected.append
356

    
357
        for idx, (entity, resource, key) in enumerate(release_holding):
358
            try:
359
                h = Holding.objects.get(entity=entity, resource=resource)
360
            except Holding.DoesNotExist:
361
                append(idx)
362
                continue
363

    
364
            if h.entity.key != key:
365
                append(idx)
366
                continue
367

    
368
            if self._check_pending(entity, resource):
369
                append(idx)
370
                continue
371

    
372
            q = self._actual_quantity(h)
373
            if q > 0:
374
                owner = h.entity.owner
375
                self._increase_resource(owner, resource, q)
376

    
377
            h.delete()
378

    
379
        return rejected
380

    
381
    def list_resources(self, context={}, entity=None, key=None):
382
        try:
383
            e = Entity.objects.get(entity=entity)
384
        except Entity.DoesNotExist:
385
            m = "No such entity '%s'" % (entity,)
386
            raise NoEntityError(m)
387

    
388
        if e.key != key:
389
            m = "Invalid key for entity '%s'" % (entity,)
390
            raise InvalidKeyError(m)
391

    
392
        holdings = e.holding_set.filter(entity=entity)
393
        resources = [h.resource for h in holdings]
394
        return resources
395

    
396
    def list_holdings(self, context={}, list_holdings=()):
397
        rejected = []
398
        reject = rejected.append
399
        holdings_list = []
400
        append = holdings_list.append
401

    
402
        for entity, key in list_holdings:
403
            try:
404
                e = Entity.objects.get(entity=entity)
405
                if e.key != key:
406
                    raise Entity.DoesNotExist("wrong key")
407
            except Entity.DoesNotExist:
408
                reject(entity)
409
                continue
410

    
411
            holdings = e.holding_set.filter(entity=entity)
412
            append([[entity, h.resource,
413
                     h.imported, h.exported, h.returned, h.released]
414
                        for h in holdings])
415

    
416
        return holdings_list, rejected
417

    
418
    def get_quota(self, context={}, get_quota=()):
419
        quotas = []
420
        append = quotas.append
421

    
422
        for entity, resource, key in get_quota:
423
            try:
424
                h = Holding.objects.get(entity=entity, resource=resource)
425
            except Holding.DoesNotExist:
426
                continue
427

    
428
            if h.entity.key != key:
429
                continue
430

    
431
            p = h.policy
432

    
433
            append((h.entity.entity, h.resource, p.quantity, p.capacity,
434
                    p.import_limit, p.export_limit,
435
                    h.imported, h.exported,
436
                    h.returned, h.released,
437
                    h.flags))
438

    
439
        return quotas
440

    
441
    def set_quota(self, context={}, set_quota=()):
442
        rejected = []
443
        append = rejected.append
444

    
445
        for (   entity, resource, key,
446
                quantity, capacity,
447
                import_limit, export_limit, flags  ) in set_quota:
448

    
449
                try:
450
                    e = Entity.objects.get(entity=entity, key=key)
451
                except Entity.DoesNotExist:
452
                    append((entity, resource))
453
                    continue
454

    
455
                policy = newname('policy_')
456
                newp = Policy   (
457
                            policy=policy,
458
                            quantity=quantity,
459
                            capacity=capacity,
460
                            import_limit=import_limit,
461
                            export_limit=export_limit
462
                )
463

    
464
                try:
465
                    h = Holding.objects.get(entity=entity, resource=resource)
466
                    p = h.policy
467
                    h.policy = newp
468
                    h.flags = flags
469
                except Holding.DoesNotExist:
470
                    h = Holding(entity=e, resource=resource,
471
                                policy=newp, flags=flags)
472
                    p = None
473

    
474
                # the order is intentionally reversed so that it
475
                # would break if we are not within a transaction.
476
                # Has helped before.
477
                h.save()
478
                newp.save()
479

    
480
                if p is not None and p.holding_set.count() == 0:
481
                    p.delete()
482

    
483
        return rejected
484

    
485
    def issue_commission(self,  context     =   {},
486
                                clientkey   =   None,
487
                                target      =   None,
488
                                key         =   None,
489
                                name        =   None,
490
                                provisions  =   ()  ):
491

    
492
        try:
493
            t = Entity.objects.get(entity=target)
494
        except Entity.DoesNotExist:
495
            m = "No target entity '%s'" % (target,)
496
            raise NoEntityError(m)
497
        else:
498
            if t.key != key:
499
                m = "Invalid key for target entity '%s'" % (target,)
500
                raise InvalidKeyError(m)
501

    
502
        create = Commission.objects.create
503
        commission = create(entity_id=target, clientkey=clientkey, name=name)
504
        serial = commission.serial
505

    
506
        checked = []
507
        for entity, resource, quantity in provisions:
508

    
509
            if entity == target:
510
                m = "Cannot issue commission from an entity to itself (%s)" % (
511
                    entity,)
512
                raise InvalidDataError(m)
513

    
514
            ent_res = entity, resource
515
            if ent_res in checked:
516
                m = "Duplicate provision for %s.%s" % ent_res
517
                raise DuplicateError(m)
518
            checked.append(ent_res)
519

    
520
            try:
521
                e = Entity.objects.get(entity=entity)
522
            except Entity.DoesNotExist:
523
                m = "No source entity '%s'" % (entity,)
524
                raise NoEntityError(m)
525

    
526
            release = 0
527
            if quantity < 0:
528
                release = 1
529

    
530
            try:
531
                h = Holding.objects.get(entity=entity, resource=resource)
532
            except Holding.DoesNotExist:
533
                m = ("There is not enough quantity "
534
                     "to allocate from in %s.%s" % (entity, resource))
535
                raise NoQuantityError(m)
536

    
537
            hp = h.policy
538

    
539
            if (hp.export_limit is not None and
540
                h.exporting + quantity > hp.export_limit):
541
                    m = ("Export limit reached for %s.%s" % (entity, resource))
542
                    raise ExportLimitError(m)
543

    
544
            if hp.quantity is not None:
545
                available = (+ hp.quantity + h.imported + h.returned
546
                             - h.exporting - h.releasing)
547

    
548
                if available - quantity < 0:
549
                    m = ("There is not enough quantity "
550
                         "to allocate from in %s.%s" % (entity, resource))
551
                    raise NoQuantityError(m)
552

    
553
            try:
554
                th = Holding.objects.get(entity=target, resource=resource)
555
            except Holding.DoesNotExist:
556
                m = ("There is not enough capacity "
557
                     "to allocate into in %s.%s" % (target, resource))
558
                raise NoCapacityError(m)
559

    
560
            tp = th.policy
561

    
562
            if (tp.import_limit is not None and
563
                th.importing + quantity > tp.import_limit):
564
                    m = ("Import limit reached for %s.%s" % (target, resource))
565
                    raise ImportLimitError(m)
566

    
567
            if tp.capacity is not None:
568
                capacity = (+ tp.capacity + th.exported + th.released
569
                            - th.importing - th.returning)
570

    
571
                if capacity - quantity < 0:
572
                        m = ("There is not enough capacity "
573
                             "to allocate into in %s.%s" % (target, resource))
574
                        raise NoCapacityError(m)
575

    
576
            Provision.objects.create(   serial      =   commission,
577
                                        entity      =   e,
578
                                        resource    =   resource,
579
                                        quantity    =   quantity   )
580
            if release:
581
                h.returning -= quantity
582
                th.releasing -= quantity
583
            else:
584
                h.exporting += quantity
585
                th.importing += quantity
586

    
587
            h.save()
588
            th.save()
589

    
590
        return serial
591

    
592
    def _log_provision(self, commission, s_holding, t_holding,
593
                             provision, log_time, reason):
594

    
595
        s_entity = s_holding.entity
596
        s_policy = s_holding.policy
597
        t_entity = t_holding.entity
598
        t_policy = t_holding.policy
599

    
600
        ProvisionLog.objects.create(
601
                        serial              =   commission.serial,
602
                        name                =   commission.name,
603
                        source              =   s_entity.entity,
604
                        target              =   t_entity.entity,
605
                        resource            =   provision.resource,
606
                        source_quantity     =   s_policy.quantity,
607
                        source_capacity     =   s_policy.capacity,
608
                        source_import_limit =   s_policy.import_limit,
609
                        source_export_limit =   s_policy.export_limit,
610
                        source_imported     =   s_holding.imported,
611
                        source_exported     =   s_holding.exported,
612
                        source_returned     =   s_holding.returned,
613
                        source_released     =   s_holding.released,
614
                        target_quantity     =   t_policy.quantity,
615
                        target_capacity     =   t_policy.capacity,
616
                        target_import_limit =   t_policy.import_limit,
617
                        target_export_limit =   t_policy.export_limit,
618
                        target_imported     =   t_holding.imported,
619
                        target_exported     =   t_holding.exported,
620
                        target_returned     =   t_holding.returned,
621
                        target_released     =   t_holding.released,
622
                        delta_quantity      =   provision.quantity,
623
                        issue_time          =   commission.issue_time,
624
                        log_time            =   log_time,
625
                        reason              =   reason)
626

    
627
    def accept_commission(self, context={}, clientkey=None,
628
                                serials=(), reason=''):
629
        log_time = now()
630

    
631
        for serial in serials:
632
            try:
633
                c = Commission.objects.get(clientkey=clientkey, serial=serial)
634
            except Commission.DoesNotExist:
635
                return
636

    
637
            t = c.entity
638

    
639
            provisions = Provision.objects.filter(serial=serial)
640
            for pv in provisions:
641
                try:
642
                    h = Holding.objects.get(entity=pv.entity.entity,
643
                                            resource=pv.resource    )
644
                    th = Holding.objects.get(entity=t, resource=pv.resource)
645
                except Holding.DoesNotExist:
646
                    m = "Corrupted provision"
647
                    raise CorruptedError(m)
648

    
649
                quantity = pv.quantity
650
                release = 0
651
                if quantity < 0:
652
                    release = 1
653

    
654
                if release:
655
                    h.returned -= quantity
656
                    th.released -= quantity
657
                else:
658
                    h.exported += quantity
659
                    th.imported += quantity
660

    
661
                reason = 'ACCEPT:' + reason[-121:]
662
                self._log_provision(c, h, th, pv, log_time, reason)
663
                h.save()
664
                th.save()
665
                pv.delete()
666
            c.delete()
667

    
668
        return
669

    
670
    def reject_commission(self, context={}, clientkey=None,
671
                                serials=(), reason=''):
672
        log_time = now()
673

    
674
        for serial in serials:
675
            try:
676
                c = Commission.objects.get(clientkey=clientkey, serial=serial)
677
            except Commission.DoesNotExist:
678
                return
679

    
680
            t = c.entity
681

    
682
            provisions = Provision.objects.filter(serial=serial)
683
            for pv in provisions:
684
                try:
685
                    h = Holding.objects.get(entity=pv.entity.entity,
686
                                            resource=pv.resource)
687
                    th = Holding.objects.get(entity=t, resource=pv.resource)
688
                except Holding.DoesNotExist:
689
                    m = "Corrupted provision"
690
                    raise CorruptedError(m)
691

    
692
                quantity = pv.quantity
693
                release = 0
694
                if quantity < 0:
695
                    release = 1
696

    
697
                if release:
698
                    h.returning += quantity
699
                    th.releasing += quantity
700
                else:
701
                    h.exporting -= quantity
702
                    th.importing -= quantity
703

    
704
                reason = 'REJECT:' + reason[-121:]
705
                self._log_provision(c, h, th, pv, log_time, reason)
706
                h.save()
707
                th.save()
708
                pv.delete()
709
            c.delete()
710

    
711
        return
712

    
713
    def get_pending_commissions(self, context={}, clientkey=None):
714
        pending = Commission.objects.filter(clientkey=clientkey)\
715
                                    .values_list('serial', flat=True)
716
        return pending
717

    
718
    def resolve_pending_commissions(self,   context={}, clientkey=None,
719
                                            max_serial=None, accept_set=()  ):
720
        accept_set = set(accept_set)
721
        pending = self.get_pending_commissions(context=context, clientkey=clientkey)
722
        pending = sorted(pending)
723

    
724
        accept = self.accept_commission
725
        reject = self.reject_commission
726

    
727
        for serial in pending:
728
            if serial > max_serial:
729
                break
730

    
731
            if serial in accept_set:
732
                accept(context=context, clientkey=clientkey, serials=[serial])
733
            else:
734
                reject(context=context, clientkey=clientkey, serials=[serial])
735

    
736
        return
737

    
738
    def release_entity(self, context={}, release_entity=()):
739
        rejected = []
740
        append = rejected.append
741
        for entity, key in release_entity:
742
            try:
743
                e = Entity.objects.get(entity=entity, key=key)
744
            except Entity.DoesNotExist:
745
                append(entity)
746
                continue
747

    
748
            if e.entities.count() != 0:
749
                append(entity)
750
                continue
751

    
752
            if e.holding_set.count() != 0:
753
                append(entity)
754
                continue
755

    
756
            e.delete()
757

    
758
        return rejected
759

    
760
    def get_timeline(self, context={}, after="", before="Z", get_timeline=()):
761
        entity_set = set()
762
        e_add = entity_set.add
763
        resource_set = set()
764
        r_add = resource_set.add
765

    
766
        for entity, resource, key in get_timeline:
767
            if entity not in entity_set:
768
                try:
769
                    e = Entity.objects.get(entity=entity, key=key)
770
                    e_add(entity)
771
                except Entity.DoesNotExist:
772
                    continue
773

    
774
            r_add((entity, resource))
775

    
776
        chunk_size = 65536
777
        nr = 0
778
        timeline = []
779
        append = timeline.append
780
        filterlogs = ProvisionLog.objects.filter
781
        if entity_set:
782
            q_entity = Q(source__in = entity_set) | Q(target__in = entity_set)
783
        else:
784
            q_entity = Q()
785

    
786
        while 1:
787
            logs = filterlogs(  q_entity,
788
                                issue_time__gt      =   after,
789
                                issue_time__lte     =   before,
790
                                reason__startswith  =   'ACCEPT:'   )
791

    
792
            logs = logs.order_by('issue_time')
793
            #logs = logs.values()
794
            logs = logs[:chunk_size]
795
            nr += len(logs)
796
            if not logs:
797
                break
798
            for g in logs:
799
                if ((g.source, g.resource) not in resource_set
800
                    or (g.target, g.resource) not in resource_set):
801
                        continue
802

    
803
                o = {
804
                    'serial'                    :   g.serial,
805
                    'source'                    :   g.source,
806
                    'target'                    :   g.target,
807
                    'resource'                  :   g.resource,
808
                    'name'                      :   g.name,
809
                    'quantity'                  :   g.delta_quantity,
810
                    'source_allocated'          :   g.source_allocated(),
811
                    'source_allocated_through'  :   g.source_allocated_through(),
812
                    'source_inbound'            :   g.source_inbound(),
813
                    'source_inbound_through'    :   g.source_inbound_through(),
814
                    'source_outbound'           :   g.source_outbound(),
815
                    'source_outbound_through'   :   g.source_outbound_through(),
816
                    'target_allocated'          :   g.target_allocated(),
817
                    'target_allocated_through'  :   g.target_allocated_through(),
818
                    'target_inbound'            :   g.target_inbound(),
819
                    'target_inbound_through'    :   g.target_inbound_through(),
820
                    'target_outbound'           :   g.target_outbound(),
821
                    'target_outbound_through'   :   g.target_outbound_through(),
822
                    'issue_time'                :   g.issue_time,
823
                    'log_time'                  :   g.log_time,
824
                    'reason'                    :   g.reason,
825
                }
826

    
827
                append(o)
828

    
829
            after = g.issue_time
830
            if after >= before:
831
                break
832

    
833
        return timeline
834

    
835

    
836
API_Callpoint = QuotaholderDjangoDBCallpoint
837