From 3fbf2fbf60e3aed3ec7ca0134839f19bc0d733d4 Mon Sep 17 00:00:00 2001 From: sritharan Date: Thu, 7 Apr 2022 05:20:38 +0000 Subject: [PATCH] Feature 10918: Alarm Notification Enhancement Change-Id: I0ca845edbfbb1105174863240964b3275130f687 Signed-off-by: sritharan --- osm_policy_module/alarming/service.py | 79 ++++++++++++++++--- osm_policy_module/core/database.py | 4 + osm_policy_module/core/pol.yaml | 1 + .../migrations/002_add_vnf_alarm.py | 4 + .../unit/alarming/test_alarming_service.py | 79 +++++++++++++++---- 5 files changed, 138 insertions(+), 29 deletions(-) diff --git a/osm_policy_module/alarming/service.py b/osm_policy_module/alarming/service.py index a938234..d1d1ea5 100644 --- a/osm_policy_module/alarming/service.py +++ b/osm_policy_module/alarming/service.py @@ -124,6 +124,10 @@ class AlarmingService: nsr_id=nsr_id, vnf_member_index=vnfr["member-vnf-index-ref"], vdu_name=vdur["name"], + last_action='insufficient-data', + id_suffix=0, + ok_ack=False, + alarm_ack=False ) for action_type in ["ok", "insufficient-data", "alarm"]: if ( @@ -227,20 +231,69 @@ class AlarmingService: "Handling vnf alarm %s with status %s", alarm.alarm_id, status ) for action in alarm.actions: + """ + Compares the current status with the last_action status. + If both the status are 'alarm', it avoid sending repetitive alarm notification. + If both the status are 'ok', it avoid sending repetitive ok notification. + """ if action.type == status: - log.info( - "Executing request to url %s for vnf alarm %s with status %s", - action.url, - alarm.alarm_id, - status, - ) - try: - requests.post(url=action.url, json=json.dumps(payload), timeout=alert_timeout) - except RequestException as e: - log.info("Error: RequestException while connecting to url %s", action.url) - log.debug("RequestException %s", e) - except ConnectionError: - log.exception("Error connecting to url %s", action.url) + if bool(self.conf.get('alert', 'enhanced_alarms')): + if ((status != "ok" or (status == "ok" and alarm.ok_ack is False)) and + (status != "alarm" or (status == "alarm" and alarm.alarm_ack is False))): + log.info( + "Executing request to url %s for vnf alarm %s with status %s", + action.url, + alarm.alarm_id, + status + ) + try: + if status == "alarm" and alarm.last_action == "ok": + alarm.id_suffix += 1 + alarm.ok_ack = False + if status == "ok" and alarm.last_action == "alarm": + alarm.alarm_ack = False + alarm.last_action = status + alarm.save() + except Exception as e: + log.exception(e) + + payload["notify_details"]["alarm_number"] = alarm.id_suffix + headers = {"content-type": "application/json"} + try: + resp = requests.post(url=action.url, data=json.dumps(payload), + headers=headers, verify=False, timeout=alert_timeout) + log.info("Response %s", resp) + if resp.status_code == 200: + if status == "ok": + alarm.ok_ack = True + alarm.save() + if status == "alarm": + alarm.alarm_ack = True + alarm.save() + if status == "insufficient-data": + alarm.alarm_ack = False + alarm.ok_ack = False + alarm.save() + except RequestException as e: + log.info("Error: RequestException while connecting to url %s", action.url) + log.debug("RequestException %s", e) + + except ConnectionError: + log.exception("Error connecting to url %s", action.url) + else: + log.info( + "Executing request to url %s for vnf alarm %s with status %s", + action.url, + alarm.alarm_id, + status + ) + try: + requests.post(url=action.url, json=json.dumps(payload), timeout=alert_timeout) + except RequestException as e: + log.info("Error: RequestException while connecting to url %s", action.url) + log.debug("RequestException %s", e) + except ConnectionError: + log.exception("Error connecting to url %s", action.url) except VnfAlarm.DoesNotExist: log.debug( diff --git a/osm_policy_module/core/database.py b/osm_policy_module/core/database.py index 7bf11d8..b7a0b4f 100644 --- a/osm_policy_module/core/database.py +++ b/osm_policy_module/core/database.py @@ -98,6 +98,10 @@ class VnfAlarm(BaseModel): nsr_id = CharField() vnf_member_index = CharField() vdu_name = CharField() + last_action = CharField(default='insufficient-data') + id_suffix = IntegerField() + ok_ack = BooleanField(default=False) + alarm_ack = BooleanField(default=False) class AlarmAction(BaseModel): diff --git a/osm_policy_module/core/pol.yaml b/osm_policy_module/core/pol.yaml index 5117260..4e3571a 100644 --- a/osm_policy_module/core/pol.yaml +++ b/osm_policy_module/core/pol.yaml @@ -39,3 +39,4 @@ sql: alert: timeout: 10 # timeout in secs + enhanced_alarms: true # alarm enhancement diff --git a/osm_policy_module/migrations/002_add_vnf_alarm.py b/osm_policy_module/migrations/002_add_vnf_alarm.py index 0c5c4b9..65bf26b 100644 --- a/osm_policy_module/migrations/002_add_vnf_alarm.py +++ b/osm_policy_module/migrations/002_add_vnf_alarm.py @@ -60,6 +60,10 @@ def migrate(migrator, database, fake=False, **kwargs): nsr_id = pw.CharField(max_length=255) vnf_member_index = pw.IntegerField() vdu_name = pw.CharField(max_length=255) + last_action = pw.CharField(max_length=255, default='insufficient-data') + id_suffix = pw.IntegerField() + ok_ack = pw.BooleanField(default=False) + alarm_ack = pw.BooleanField(default=False) class Meta: table_name = "vnfalarm" diff --git a/osm_policy_module/tests/unit/alarming/test_alarming_service.py b/osm_policy_module/tests/unit/alarming/test_alarming_service.py index e897cd9..563768f 100644 --- a/osm_policy_module/tests/unit/alarming/test_alarming_service.py +++ b/osm_policy_module/tests/unit/alarming/test_alarming_service.py @@ -39,30 +39,72 @@ class TestAlarmingService(TestCase): def setUp(self): self.config = Config() self.loop = asyncio.new_event_loop() + self.payload = { + "notify_details": { + "alarm_number": 0 + } + } + self.headers = {"content-type": "application/json"} @mock.patch.object(VnfAlarmRepository, "get") @mock.patch("requests.post") @mock.patch("osm_policy_module.core.database.db") - def test_handle_alarm(self, database, requests_post, get_alarm): + def test_handle_alarm_suppression(self, database, requests_post, get_alarm): alert_timeout = int(self.config.get('alert', 'timeout')) - mock_alarm = self._build_mock_alarm("test_id") + mock_alarm = self._build_mock_alarm("test_id", last_action="ok") get_alarm.return_value = mock_alarm - service = AlarmingService(self.config) - self.loop.run_until_complete(service.handle_alarm("test_id", "alarm", {})) - requests_post.assert_called_once_with(json="{}", url="http://alarm-url/", timeout=alert_timeout) + if bool(self.config.get('alert', 'enhanced_alarms')): + self.loop.run_until_complete(service.handle_alarm("test_id", "alarm", self.payload)) + requests_post.assert_called_once_with( + url='http://alarm-url/', data='{"notify_details": {"alarm_number": 1}}', + headers={'content-type': 'application/json'}, verify=False, timeout=alert_timeout + ) + else: + self.loop.run_until_complete(service.handle_alarm("test_id", "alarm", {})) + requests_post.assert_called_once_with(json="{}", url="http://alarm-url/", timeout=alert_timeout) - requests_post.reset_mock() - self.loop.run_until_complete(service.handle_alarm("test_id", "ok", {})) - requests_post.assert_called_once_with(json="{}", url="http://ok-url/", timeout=alert_timeout) + @mock.patch.object(VnfAlarmRepository, "get") + @mock.patch("requests.post") + @mock.patch("osm_policy_module.core.database.db") + def test_handle_ok_suppression(self, database, requests_post, get_alarm): + alert_timeout = int(self.config.get('alert', 'timeout')) + mock_alarm = self._build_mock_alarm("test_id", last_action="alarm") + get_alarm.return_value = mock_alarm + service = AlarmingService(self.config) + if bool(self.config.get('alert', 'enhanced_alarms')): + self.loop.run_until_complete(service.handle_alarm("test_id", "ok", self.payload)) + requests_post.assert_called_once_with( + url='http://ok-url/', data='{"notify_details": {"alarm_number": 0}}', + headers={'content-type': 'application/json'}, verify=False, timeout=alert_timeout + ) + else: + self.loop.run_until_complete(service.handle_alarm("test_id", "ok", {})) + requests_post.assert_called_once_with(json="{}", url="http://ok-url/", timeout=alert_timeout) - requests_post.reset_mock() - self.loop.run_until_complete( - service.handle_alarm("test_id", "insufficient-data", {}) - ) - requests_post.assert_called_once_with( - json="{}", url="http://insufficient-data-url/", timeout=alert_timeout - ) + @mock.patch.object(VnfAlarmRepository, "get") + @mock.patch("requests.post") + @mock.patch("osm_policy_module.core.database.db") + def test_handle_insufficientalarm(self, database, requests_post, get_alarm): + alert_timeout = int(self.config.get('alert', 'timeout')) + mock_alarm = self._build_mock_alarm("test_id") + get_alarm.return_value = mock_alarm + service = AlarmingService(self.config) + if bool(self.config.get('alert', 'enhanced_alarms')): + self.loop.run_until_complete( + service.handle_alarm("test_id", "insufficient-data", self.payload) + ) + requests_post.assert_called_once_with( + url='http://insufficient-data-url/', data='{"notify_details": {"alarm_number": 0}}', + headers={'content-type': 'application/json'}, verify=False, timeout=alert_timeout + ) + else: + self.loop.run_until_complete( + service.handle_alarm("test_id", "insufficient-data", {}) + ) + requests_post.assert_called_once_with( + json="{}", url='http://insufficient-data-url/', timeout=alert_timeout + ) @mock.patch.object(VnfAlarmRepository, "get") @mock.patch("requests.post") @@ -70,7 +112,6 @@ class TestAlarmingService(TestCase): def test_handle_alarm_unknown_status(self, database, requests_post, get_alarm): mock_alarm = self._build_mock_alarm("test_id") get_alarm.return_value = mock_alarm - service = AlarmingService(self.config) self.loop.run_until_complete(service.handle_alarm("test_id", "unknown", {})) requests_post.assert_not_called() @@ -81,6 +122,8 @@ class TestAlarmingService(TestCase): alarm_url="http://alarm-url/", insufficient_data_url="http://insufficient-data-url/", ok_url="http://ok-url/", + id_suffix=0, + last_action="insufficient-data" ): mock_alarm = mock.Mock() mock_alarm.alarm_id = alarm_id @@ -93,5 +136,9 @@ class TestAlarmingService(TestCase): ok_action = mock.Mock() ok_action.type = "ok" ok_action.url = ok_url + mock_alarm.ok_ack = False + mock_alarm.alarm_ack = False + mock_alarm.id_suffix = id_suffix + mock_alarm.last_action = last_action mock_alarm.actions = [insufficient_data_action, alarm_action, ok_action] return mock_alarm -- 2.25.1