Feature 10918: Alarm Notification Enhancement 65/11865/4
authorsritharan <priyadarshini@tataelxsi.co.in>
Thu, 7 Apr 2022 05:20:38 +0000 (05:20 +0000)
committersritharan <priyadarshini@tataelxsi.co.in>
Wed, 11 May 2022 06:18:25 +0000 (06:18 +0000)
Change-Id: I0ca845edbfbb1105174863240964b3275130f687
Signed-off-by: sritharan <priyadarshini@tataelxsi.co.in>
osm_policy_module/alarming/service.py
osm_policy_module/core/database.py
osm_policy_module/core/pol.yaml
osm_policy_module/migrations/002_add_vnf_alarm.py
osm_policy_module/tests/unit/alarming/test_alarming_service.py

index a938234..d1d1ea5 100644 (file)
@@ -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(
index 7bf11d8..b7a0b4f 100644 (file)
@@ -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):
index 5117260..4e3571a 100644 (file)
@@ -39,3 +39,4 @@ sql:
 
 alert:
   timeout: 10 # timeout in secs
+  enhanced_alarms: true # alarm enhancement
index 0c5c4b9..65bf26b 100644 (file)
@@ -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"
index e897cd9..563768f 100644 (file)
@@ -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