Skip to content

Commit

Permalink
Merge pull request #2519 from johannaengland/eventengine/juniper-aler…
Browse files Browse the repository at this point in the history
…t-count

Add eventengine plugin for juniper alert count
  • Loading branch information
johannaengland authored Sep 5, 2023
2 parents e2b92de + 74fc9d1 commit fb4ea26
Show file tree
Hide file tree
Showing 4 changed files with 554 additions and 27 deletions.
26 changes: 26 additions & 0 deletions python/nav/eventengine/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,29 @@
# along with NAV. If not, see <http://www.gnu.org/licenses/>.
#
"""NAV eventengine"""

try:
from subprocess32 import STDOUT, check_output, TimeoutExpired, CalledProcessError
except ImportError:
from subprocess import STDOUT, check_output, TimeoutExpired, CalledProcessError


def get_eventengine_output(timeout=10):
"""
Runs eventengine in foreground mode, kills it after timeout seconds and
returns the combined stdout+stderr output from the process.
Also asserts that pping shouldn't unexpectedly exit with a zero exitcode.
"""
cmd = ["eventengine", "-f"]
try:
output = check_output(cmd, stderr=STDOUT, timeout=timeout)
except TimeoutExpired as error:
# this is the normal case, since we need to kill eventengine after the timeout
print(error.output.decode("utf-8"))
return error.output.decode("utf-8")
except CalledProcessError as error:
print(error.output.decode("utf-8"))
raise
else:
print(output)
assert False, "eventengine exited with non-zero status"
122 changes: 122 additions & 0 deletions python/nav/eventengine/plugins/juniperalertcount.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
#
# Copyright (C) 2022 Sikt
#
# This file is part of Network Administration Visualized (NAV).
#
# NAV is free software: you can redistribute it and/or modify it under
# the terms of the GNU General Public License version 3 as published by
# the Free Software Foundation.
#
# This program is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
# more details. You should have received a copy of the GNU General Public
# License along with NAV. If not, see <http://www.gnu.org/licenses/>.
#
"""juniper alert count handler plugin"""

from nav.eventengine import unresolved
from nav.eventengine.alerts import AlertGenerator
from nav.eventengine.plugin import EventHandler
from nav.models.event import STATE_CHOICES


class JuniperAlertCountHandler(EventHandler):
"""Accepts juniperYellowAlarmState and juniperRedAlarmState events"""

handled_types = (
'juniperYellowAlarmState',
'juniperRedAlarmState',
)

def handle(self):
event = self.event

if event.state == event.STATE_STATELESS:
self._logger.warning(
'Ignoring stateless %s event: %r', event.event_type, event
)
self.event.delete()
elif event.state == event.STATE_START:
return self._handle_start()
elif event.state == event.STATE_END:
return self._handle_end()

def _handle_start(self):
event = self.event
alert = AlertGenerator(event)

if self._delete_event_with_incorrect_alert_type(
event=event,
alert_type=alert.alert_type,
accepted_alert_types=["juniperRedAlarmOn", "juniperYellowAlarmOn"],
):
return

alert.history_vars = {"count": alert["count"]}
unresolved_alert = unresolved.refers_to_unresolved_alert(self.event)

if unresolved_alert:
if int(alert["count"]) == int(
unresolved_alert.variables.get(variable="count").value
):
self._logger.warning(
'Ignoring duplicate %s start event: %r',
event.event_type,
event,
)
event.delete()
return

unresolved_alert.end_time = event.time
unresolved_alert.save()

alert.post()
event.delete()

def _handle_end(self):
event = self.event
alert = AlertGenerator(event)

if self._delete_event_with_incorrect_alert_type(
event=event,
alert_type=alert.alert_type,
accepted_alert_types=["juniperRedAlarmOff", "juniperYellowAlarmOff"],
):
return

unresolved_alert = unresolved.refers_to_unresolved_alert(event)

if unresolved_alert:
alert.post()
else:
self._logger.warning(
"no unresolved %s for %s, ignoring end event",
self.event.event_type,
self.get_target(),
)

self.event.delete()

def get_target(self):
return self.event.get_subject()

def _delete_event_with_incorrect_alert_type(
self, event, alert_type, accepted_alert_types
):
"""
Checks if an the alert type of an event is within the given accepted alert
types and deletes the event and returns true if not
"""
if alert_type not in accepted_alert_types:
self._logger.warning(
'Ignoring %s %s event with alert type %s: %r',
event.event_type,
dict(STATE_CHOICES)[event.state],
alert_type,
event,
)
event.delete()
return True

return False
28 changes: 1 addition & 27 deletions tests/integration/eventengine/boxdown_test.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,10 @@
"""Black-box integration tests for the apparent proper processing of boxState events"""
import os

try:
from subprocess32 import STDOUT, check_output, TimeoutExpired, CalledProcessError
except ImportError:
from subprocess import STDOUT, check_output, TimeoutExpired, CalledProcessError

import pytest

from nav.config import find_config_file
from nav.eventengine import get_eventengine_output
from nav.models.manage import Netbox
from nav.models.event import EventQueue as Event

Expand Down Expand Up @@ -36,28 +32,6 @@ def post_fake_boxdown(netbox):
event.save()


def get_eventengine_output(timeout=10):
"""
Runs eventengine in foreground mode, kills it after timeout seconds and
returns the combined stdout+stderr output from the process.
Also asserts that pping shouldn't unexpectedly exit with a zero exitcode.
"""
cmd = ["eventengine", "-f"]
try:
output = check_output(cmd, stderr=STDOUT, timeout=timeout)
except TimeoutExpired as error:
# this is the normal case, since we need to kill eventengine after the timeout
print(error.output.decode("utf-8"))
return error.output.decode("utf-8")
except CalledProcessError as error:
print(error.output.decode("utf-8"))
raise
else:
print(output)
assert False, "eventengine exited with non-zero status"


@pytest.fixture()
def host_going_down():
box = Netbox(
Expand Down
Loading

0 comments on commit fb4ea26

Please sign in to comment.