Support of VDU alarms in new SA architecture 67/13467/2
authorgarciadeblas <gerardo.garciadeblas@telefonica.com>
Tue, 30 May 2023 10:51:14 +0000 (12:51 +0200)
committercubag <gcuba@whitestack.com>
Tue, 30 May 2023 17:51:56 +0000 (19:51 +0200)
Change-Id: I6cbce6267804763063f92a2d9c45c10f4542f9c7
Signed-off-by: garciadeblas <gerardo.garciadeblas@telefonica.com>
osm_lcm/ns.py

index 83705d4..6ec52c8 100644 (file)
@@ -135,6 +135,14 @@ class NsLcm(LcmBase):
     SUBOPERATION_STATUS_SKIP = -3
     EE_TLS_NAME = "ee-tls"
     task_name_deploy_vca = "Deploying VCA"
+    rel_operation_types = {
+        "GE": ">=",
+        "LE": "<=",
+        "GT": ">",
+        "LT": "<",
+        "EQ": "==",
+        "NE": "!=",
+    }
 
     def __init__(self, msg, lcm_tasks, config: LcmCfg):
         """
@@ -2382,14 +2390,6 @@ class NsLcm(LcmBase):
         df = vnfd.get("df", [{}])[0]
         # Checking for auto-scaling configuration
         if "scaling-aspect" in df:
-            rel_operation_types = {
-                "GE": ">=",
-                "LE": "<=",
-                "GT": ">",
-                "LT": "<",
-                "EQ": "==",
-                "NE": "!=",
-            }
             scaling_aspects = df["scaling-aspect"]
             all_vnfd_monitoring_params = {}
             for ivld in vnfd.get("int-virtual-link-desc", ()):
@@ -2465,7 +2465,9 @@ class NsLcm(LcmBase):
                                 operation = scaling_criteria[
                                     "scale-in-relational-operation"
                                 ]
-                                rel_operator = rel_operation_types.get(operation, "<=")
+                                rel_operator = self.rel_operation_types.get(
+                                    operation, "<="
+                                )
                                 metric_selector = f'{metric_name}{{ns_id="{nsr_id}", vnf_member_index="{vnf_member_index}", vdu_id="{vdu_id}"}}'
                                 expression = f"(count ({metric_selector}) > {instances_min_number}) and (avg({metric_selector}) {rel_operator} {scalein_threshold})"
                                 labels = {
@@ -2506,7 +2508,9 @@ class NsLcm(LcmBase):
                                 operation = scaling_criteria[
                                     "scale-out-relational-operation"
                                 ]
-                                rel_operator = rel_operation_types.get(operation, "<=")
+                                rel_operator = self.rel_operation_types.get(
+                                    operation, "<="
+                                )
                                 metric_selector = f'{metric_name}{{ns_id="{nsr_id}", vnf_member_index="{vnf_member_index}", vdu_id="{vdu_id}"}}'
                                 expression = f"(count ({metric_selector}) < {instances_max_number}) and (avg({metric_selector}) {rel_operator} {scaleout_threshold})"
                                 labels = {
@@ -2542,6 +2546,100 @@ class NsLcm(LcmBase):
                                 alerts.append(alert)
         return alerts
 
+    def _gather_vnfr_alarm_alerts(self, vnfr, vnfd):
+        alerts = []
+        nsr_id = vnfr["nsr-id-ref"]
+        vnf_member_index = vnfr["member-vnf-index-ref"]
+
+        # Checking for VNF alarm configuration
+        for vdur in vnfr["vdur"]:
+            vdu_id = vdur["vdu-id-ref"]
+            vdu = next(filter(lambda vdu: vdu["id"] == vdu_id, vnfd["vdu"]))
+            if "alarm" in vdu:
+                # Get VDU monitoring params, since alerts are based on them
+                vdu_monitoring_params = {}
+                for mp in vdu.get("monitoring-parameter", []):
+                    vdu_monitoring_params[mp.get("id")] = mp
+                if not vdu_monitoring_params:
+                    self.logger.error(
+                        "VDU alarm refers to a VDU monitoring param, but there are no VDU monitoring params in the VDU"
+                    )
+                    continue
+                # Get alarms in the VDU
+                alarm_descriptors = vdu["alarm"]
+                # Create VDU alarms for each alarm in the VDU
+                for alarm_descriptor in alarm_descriptors:
+                    # Check that the VDU alarm refers to a proper monitoring param
+                    alarm_monitoring_param = alarm_descriptor.get(
+                        "vnf-monitoring-param-ref", ""
+                    )
+                    vdu_specific_monitoring_param = vdu_monitoring_params.get(
+                        alarm_monitoring_param, {}
+                    )
+                    if not vdu_specific_monitoring_param:
+                        self.logger.error(
+                            "VDU alarm refers to a VDU monitoring param not present in the VDU"
+                        )
+                        continue
+                    metric_name = vdu_specific_monitoring_param.get(
+                        "performance-metric"
+                    )
+                    if not metric_name:
+                        self.logger.error(
+                            "VDU alarm refers to a VDU monitoring param that has no associated performance-metric"
+                        )
+                        continue
+                    # Set params of the alarm to be created in Prometheus
+                    metric_name = f"osm_{metric_name}"
+                    metric_threshold = alarm_descriptor.get("value")
+                    uuid = str(uuid4())
+                    alert_name = f"vdu_alarm_{uuid}"
+                    operation = alarm_descriptor["operation"]
+                    rel_operator = self.rel_operation_types.get(operation, "<=")
+                    metric_selector = f'{metric_name}{{ns_id="{nsr_id}", vnf_member_index="{vnf_member_index}", vdu_id="{vdu_id}"}}'
+                    expression = (
+                        f"avg({metric_selector}) {rel_operator} {metric_threshold}"
+                    )
+                    labels = {
+                        "ns_id": nsr_id,
+                        "vnf_member_index": vnf_member_index,
+                        "vdu_id": vdu_id,
+                    }
+                    prom_cfg = {
+                        "alert": alert_name,
+                        "expr": expression,
+                        "for": "1m",  # default value. Ideally, this should be related to an IM param, but there is not such param
+                        "labels": labels,
+                    }
+                    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"]]
+
+                    alert = {
+                        "uuid": uuid,
+                        "name": alert_name,
+                        "metric": metric_name,
+                        "tags": {
+                            "ns_id": nsr_id,
+                            "vnf_member_index": vnf_member_index,
+                            "vdu_id": vdu_id,
+                        },
+                        "alarm_status": "ok",
+                        "action_type": "vdu_alarm",
+                        "action": alarm_action,
+                        "prometheus_config": prom_cfg,
+                    }
+                    alerts.append(alert)
+        return alerts
+
     def update_nsrs_with_pla_result(self, params):
         try:
             nslcmop_id = deep_get(params, ("placement", "nslcmopId"))
@@ -3095,6 +3193,10 @@ class NsLcm(LcmBase):
                         self.logger.info(f"Storing scaling alert in MongoDB: {alert}")
                         self.db.create("alerts", alert)
 
+                    alarm_alerts = self._gather_vnfr_alarm_alerts(vnfr, vnfd)
+                    for alert in alarm_alerts:
+                        self.logger.info(f"Storing VNF alarm alert in MongoDB: {alert}")
+                        self.db.create("alerts", alert)
             if db_nsr:
                 self._write_ns_status(
                     nsr_id=nsr_id,