Feature 10912: NS Update Change VNF Package
[osm/POL.git] / osm_policy_module / alarming / service.py
index 37fa6a1..0e355d8 100644 (file)
 import asyncio
 import json
 import logging
+import operator
+import functools
 
 import requests
-from requests.exceptions import ConnectionError
+from requests.exceptions import ConnectionError, RequestException
 
 from osm_policy_module.common.common_db_client import CommonDbClient
 from osm_policy_module.common.lcm_client import LcmClient
@@ -54,13 +56,19 @@ class AlarmingService:
         self.mon_client = MonClient(config, loop=self.loop)
         self.lcm_client = LcmClient(config, loop=self.loop)
 
-    async def configure_vnf_alarms(self, nsr_id: str):
+    async def configure_vnf_alarms(self, nsr_id: str, vnf_member_index=None):
         log.info("Configuring vnf alarms for network service %s", nsr_id)
         alarms_created = []
         database.db.connect()
         try:
             with database.db.atomic():
-                vnfrs = self.db_client.get_vnfrs(nsr_id)
+                if vnf_member_index is None:
+                    vnfrs = self.db_client.get_vnfrs(nsr_id)
+                else:
+                    vnfrs = []
+                    vnfr = self.db_client.get_vnfr(nsr_id, vnf_member_index)
+                    vnfrs.append(vnfr)
+                # vnfrs = self.db_client.get_vnfrs(nsr_id)
                 for vnfr in vnfrs:
                     log.debug("Processing vnfr: %s", vnfr)
                     vnfd = self.db_client.get_vnfd(vnfr["vnfd-id"])
@@ -101,6 +109,14 @@ class AlarmingService:
                                 metric_name = self._get_metric_name(
                                     vnf_monitoring_param
                                 )
+                                alarm_action = dict()
+                                for action_type in ["ok", "insufficient-data", "alarm"]:
+                                    if "actions" in alarm_descriptor and action_type in alarm_descriptor["actions"]:
+                                        for url in alarm_descriptor["actions"][action_type]:
+                                            if "webhook" in alarm_action:
+                                                alarm_action["webhook"].append(url["url"])
+                                            else:
+                                                alarm_action["webhook"] = [url["url"]]
                                 alarm_uuid = await self.mon_client.create_alarm(
                                     metric_name=metric_name,
                                     ns_id=nsr_id,
@@ -108,6 +124,7 @@ class AlarmingService:
                                     vnf_member_index=vnfr["member-vnf-index-ref"],
                                     threshold=alarm_descriptor["value"],
                                     operation=alarm_descriptor["operation"],
+                                    action=str(alarm_action),
                                 )
                                 alarm = VnfAlarmRepository.create(
                                     alarm_id=alarm_descriptor["alarm-id"],
@@ -115,6 +132,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 (
@@ -182,12 +203,18 @@ class AlarmingService:
         finally:
             database.db.close()
 
-    async def delete_vnf_alarms(self, nsr_id):
+    async def delete_vnf_alarms(self, nsr_id, vnf_member_index=None):
         log.info("Deleting vnf alarms for network service %s", nsr_id)
         database.db.connect()
         try:
             with database.db.atomic():
-                for alarm in VnfAlarmRepository.list(VnfAlarm.nsr_id == nsr_id):
+                if vnf_member_index is None:
+                    alarm_conditions = VnfAlarm.nsr_id == nsr_id
+                else:
+                    query_list = [VnfAlarm.nsr_id == nsr_id,
+                                  VnfAlarm.vnf_member_index == vnf_member_index]
+                    alarm_conditions = functools.reduce(operator.and_, query_list)
+                for alarm in VnfAlarmRepository.list(alarm_conditions):
                     log.debug("Deleting vnf alarm %s", alarm.alarm_uuid)
                     try:
                         await self.mon_client.delete_alarm(
@@ -209,6 +236,7 @@ class AlarmingService:
             database.db.close()
 
     async def handle_alarm(self, alarm_uuid: str, status: str, payload: dict):
+        alert_timeout = int(self.conf.get('alert', 'timeout'))
         database.db.connect()
         try:
             with database.db.atomic():
@@ -217,17 +245,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))
-                        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(