|
24 | 24 | from random import randint
|
25 | 25 | from math import ceil
|
26 | 26 |
|
| 27 | +from django.db.transaction import atomic |
27 | 28 | from twisted.python.failure import Failure
|
28 | 29 | from twisted.internet import task, reactor
|
29 | 30 | from twisted.internet.defer import Deferred
|
@@ -556,12 +557,69 @@ def flush(self):
|
556 | 557 | _COUNTERS = CounterFlusher()
|
557 | 558 |
|
558 | 559 |
|
559 |
| -def log_received_events(): |
560 |
| - """Checks the event queue for events addressed to ipdevpoll and logs them""" |
561 |
| - return run_in_thread(_log_received_events) |
| 560 | +def handle_incoming_events(): |
| 561 | + """Checks the event queue for events addressed to ipdevpoll and handles them""" |
| 562 | + # Since this extensively accesses the database, it needs to run in a thread: |
| 563 | + return run_in_thread(_handle_incoming_events) |
562 | 564 |
|
563 | 565 |
|
564 |
| -def _log_received_events(): |
| 566 | +@atomic |
| 567 | +def _handle_incoming_events(): |
565 | 568 | events = EventQueue.objects.filter(target='ipdevpoll')
|
| 569 | + # Filter out (and potentially delete) events not worthy of our attention |
| 570 | + events = [event for event in events if _event_pre_filter(event)] |
| 571 | + |
| 572 | + boxes_to_reschedule = defaultdict(list) |
| 573 | + # There may be multiple notifications queued for the same request, so group them |
| 574 | + # by netbox+jobname |
566 | 575 | for event in events:
|
567 |
| - _logger.debug("Event on queue: %r", event) |
| 576 | + boxes_to_reschedule[(event.netbox_id, event.subid)].append(event) |
| 577 | + _logger.debug("boxes_to_reschedule: %r", boxes_to_reschedule) |
| 578 | + |
| 579 | + |
| 580 | +def _event_pre_filter(event: EventQueue): |
| 581 | + """Returns True if this event is worthy of this process' attention. If the event |
| 582 | + isn't worthy of *any* ipdevpoll process' attention, we delete it from the database |
| 583 | + too. |
| 584 | + """ |
| 585 | + _logger.debug("Found event on queue: %r", event) |
| 586 | + if not _is_valid_refresh_event(event): |
| 587 | + event.delete() |
| 588 | + return False |
| 589 | + if not _is_refresh_event_for_me(event): |
| 590 | + return False |
| 591 | + # TODO: Should also delete events that seem to be stale. If the requested job is |
| 592 | + # logged as having run after the event's timestamp, the event is stale. |
| 593 | + return True |
| 594 | + |
| 595 | + |
| 596 | +def _is_valid_refresh_event(event: EventQueue): |
| 597 | + if event.event_type_id != 'notification': |
| 598 | + _logger.info("Ignoring non-notification event from %s", event.source) |
| 599 | + return False |
| 600 | + |
| 601 | + if not event.subid: |
| 602 | + _logger.info( |
| 603 | + "Ignoring notification event from %s with blank job name", event.source |
| 604 | + ) |
| 605 | + return False |
| 606 | + |
| 607 | + return True |
| 608 | + |
| 609 | + |
| 610 | +def _is_refresh_event_for_me(event: EventQueue): |
| 611 | + schedulers = JobScheduler.get_job_schedulers_by_name() |
| 612 | + s = schedulers['ip2mac'] |
| 613 | + if event.subid not in schedulers: |
| 614 | + _logger.debug( |
| 615 | + "This process does not schedule %s, %r is not for us", event.subid, event |
| 616 | + ) |
| 617 | + return False |
| 618 | + |
| 619 | + if event.netbox_id not in schedulers[event.subid].netboxes: |
| 620 | + _logger.debug( |
| 621 | + "This process does not poll from %s, %r is not for us", event.netbox, event |
| 622 | + ) |
| 623 | + return False |
| 624 | + |
| 625 | + return True |
0 commit comments