blob: f3a316dcbc7953bf3bcba9d614e7a5403ef6b443 [file] [log] [blame]
Benjamin Diazf7451f82019-04-01 14:56:26 -03001# -*- coding: utf-8 -*-
Atul Agarwaldb8c1052020-04-28 15:42:28 +05302# pylint: disable=no-member
Benjamin Diazf7451f82019-04-01 14:56:26 -03003
4# Copyright 2018 Whitestack, LLC
5# *************************************************************
6
7# This file is part of OSM Monitoring module
8# All Rights Reserved to Whitestack, LLC
9
10# Licensed under the Apache License, Version 2.0 (the "License"); you may
11# not use this file except in compliance with the License. You may obtain
12# a copy of the License at
13
14# http://www.apache.org/licenses/LICENSE-2.0
15
16# Unless required by applicable law or agreed to in writing, software
17# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
18# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
19# License for the specific language governing permissions and limitations
20# under the License.
21
22# For those usages not covered by the Apache License, Version 2.0 please
23# contact: bdiaz@whitestack.com or glavado@whitestack.com
24##
Benjamin Diazf7451f82019-04-01 14:56:26 -030025import json
26import logging
elumalai1d7fa332022-04-12 12:54:37 +053027import operator
28import functools
Benjamin Diazf7451f82019-04-01 14:56:26 -030029
30import requests
Atul Agarwal46e7efd2021-10-18 17:22:58 +000031from requests.exceptions import ConnectionError, RequestException
Benjamin Diazf7451f82019-04-01 14:56:26 -030032
33from osm_policy_module.common.common_db_client import CommonDbClient
34from osm_policy_module.common.lcm_client import LcmClient
35from osm_policy_module.common.mon_client import MonClient
36from osm_policy_module.core import database
37from osm_policy_module.core.config import Config
garciadeblas4584f8e2021-05-14 16:50:06 +020038from osm_policy_module.core.database import (
39 VnfAlarm,
40 VnfAlarmRepository,
41 AlarmActionRepository,
42)
Benjamin Diazf7451f82019-04-01 14:56:26 -030043from osm_policy_module.core.exceptions import VdurNotFound
44
45log = logging.getLogger(__name__)
46
47
48class AlarmingService:
Mark Beierld37c54c2023-05-10 11:15:10 -040049 def __init__(self, config: Config):
Benjamin Diazf7451f82019-04-01 14:56:26 -030050 self.conf = config
Benjamin Diazf7451f82019-04-01 14:56:26 -030051 self.db_client = CommonDbClient(config)
Mark Beierld37c54c2023-05-10 11:15:10 -040052 self.mon_client = MonClient(config)
53 self.lcm_client = LcmClient(config)
Benjamin Diazf7451f82019-04-01 14:56:26 -030054
elumalaicb5642a2022-04-28 02:51:04 +053055 async def configure_vnf_alarms(self, nsr_id: str, vnf_member_index=None):
Benjamin Diazf7451f82019-04-01 14:56:26 -030056 log.info("Configuring vnf alarms for network service %s", nsr_id)
57 alarms_created = []
Benjamin Diazacac7552019-05-23 14:19:06 -030058 database.db.connect()
59 try:
60 with database.db.atomic():
elumalaicb5642a2022-04-28 02:51:04 +053061 if vnf_member_index is None:
62 vnfrs = self.db_client.get_vnfrs(nsr_id)
63 else:
64 vnfrs = []
65 vnfr = self.db_client.get_vnfr(nsr_id, vnf_member_index)
66 vnfrs.append(vnfr)
67 # vnfrs = self.db_client.get_vnfrs(nsr_id)
Benjamin Diazf7451f82019-04-01 14:56:26 -030068 for vnfr in vnfrs:
69 log.debug("Processing vnfr: %s", vnfr)
garciadeblas4584f8e2021-05-14 16:50:06 +020070 vnfd = self.db_client.get_vnfd(vnfr["vnfd-id"])
71 for vdur in vnfr["vdur"]:
Benjamin Diazf7451f82019-04-01 14:56:26 -030072 vdu = next(
73 filter(
garciadeblas4584f8e2021-05-14 16:50:06 +020074 lambda vdu: vdu["id"] == vdur["vdu-id-ref"], vnfd["vdu"]
Benjamin Diazf7451f82019-04-01 14:56:26 -030075 )
76 )
garciadeblas4584f8e2021-05-14 16:50:06 +020077 if "alarm" in vdu:
78 alarm_descriptors = vdu["alarm"]
Benjamin Diazf7451f82019-04-01 14:56:26 -030079 for alarm_descriptor in alarm_descriptors:
80 try:
81 VnfAlarmRepository.get(
garciadeblas4584f8e2021-05-14 16:50:06 +020082 VnfAlarm.alarm_id
83 == alarm_descriptor["alarm-id"],
84 VnfAlarm.vnf_member_index
85 == vnfr["member-vnf-index-ref"],
86 VnfAlarm.vdu_name == vdur["name"],
87 VnfAlarm.nsr_id == nsr_id,
Benjamin Diazf7451f82019-04-01 14:56:26 -030088 )
garciadeblas4584f8e2021-05-14 16:50:06 +020089 log.debug(
90 "vdu %s already has an alarm configured with same id %s",
91 vdur["name"],
92 alarm_descriptor["alarm-id"],
93 )
Benjamin Diazf7451f82019-04-01 14:56:26 -030094 continue
95 except VnfAlarm.DoesNotExist:
96 pass
97 vnf_monitoring_param = next(
98 filter(
garciadeblas4584f8e2021-05-14 16:50:06 +020099 lambda param: param["id"]
100 == alarm_descriptor["vnf-monitoring-param-ref"],
101 vdu.get("monitoring-parameter", []),
garciaale7ce998f2020-12-03 18:05:24 -0300102 ),
garciadeblas4584f8e2021-05-14 16:50:06 +0200103 {},
Benjamin Diazf7451f82019-04-01 14:56:26 -0300104 )
garciadeblas4584f8e2021-05-14 16:50:06 +0200105 metric_name = self._get_metric_name(
106 vnf_monitoring_param
107 )
Atul Agarwale9228cf2021-03-19 10:11:38 +0000108 alarm_action = dict()
109 for action_type in ["ok", "insufficient-data", "alarm"]:
garciadeblasbe42d542022-11-14 00:29:47 +0100110 if (
111 "actions" in alarm_descriptor
112 and action_type in alarm_descriptor["actions"]
113 ):
114 for url in alarm_descriptor["actions"][
115 action_type
116 ]:
Atul Agarwale9228cf2021-03-19 10:11:38 +0000117 if "webhook" in alarm_action:
garciadeblasbe42d542022-11-14 00:29:47 +0100118 alarm_action["webhook"].append(
119 url["url"]
120 )
Atul Agarwale9228cf2021-03-19 10:11:38 +0000121 else:
122 alarm_action["webhook"] = [url["url"]]
Benjamin Diazf7451f82019-04-01 14:56:26 -0300123 alarm_uuid = await self.mon_client.create_alarm(
Gianpietro Lavado41610192019-12-06 15:46:23 +0000124 metric_name=metric_name,
Benjamin Diazf7451f82019-04-01 14:56:26 -0300125 ns_id=nsr_id,
garciadeblas4584f8e2021-05-14 16:50:06 +0200126 vdu_name=vdur["name"],
127 vnf_member_index=vnfr["member-vnf-index-ref"],
128 threshold=alarm_descriptor["value"],
129 operation=alarm_descriptor["operation"],
Atul Agarwale9228cf2021-03-19 10:11:38 +0000130 action=str(alarm_action),
Rahul Kumarbb6bdfa2023-11-09 08:45:38 +0000131 vnfr=vnfr,
132 vnfd=vnfd,
Benjamin Diazf7451f82019-04-01 14:56:26 -0300133 )
134 alarm = VnfAlarmRepository.create(
garciadeblas4584f8e2021-05-14 16:50:06 +0200135 alarm_id=alarm_descriptor["alarm-id"],
Benjamin Diazf7451f82019-04-01 14:56:26 -0300136 alarm_uuid=alarm_uuid,
137 nsr_id=nsr_id,
garciadeblas4584f8e2021-05-14 16:50:06 +0200138 vnf_member_index=vnfr["member-vnf-index-ref"],
139 vdu_name=vdur["name"],
garciadeblasbe42d542022-11-14 00:29:47 +0100140 last_action="insufficient-data",
sritharan3fbf2fb2022-04-07 05:20:38 +0000141 id_suffix=0,
142 ok_ack=False,
garciadeblasbe42d542022-11-14 00:29:47 +0100143 alarm_ack=False,
Benjamin Diazf7451f82019-04-01 14:56:26 -0300144 )
garciadeblas4584f8e2021-05-14 16:50:06 +0200145 for action_type in ["ok", "insufficient-data", "alarm"]:
146 if (
147 "actions" in alarm_descriptor
148 and action_type in alarm_descriptor["actions"]
149 ):
150 for url in alarm_descriptor["actions"][
151 action_type
152 ]:
Benjamin Diazf7451f82019-04-01 14:56:26 -0300153 AlarmActionRepository.create(
154 type=action_type,
garciadeblas4584f8e2021-05-14 16:50:06 +0200155 url=url["url"],
156 alarm=alarm,
Benjamin Diazf7451f82019-04-01 14:56:26 -0300157 )
158 alarms_created.append(alarm)
159
Benjamin Diazacac7552019-05-23 14:19:06 -0300160 except Exception as e:
161 log.exception("Error configuring VNF alarms:")
162 if len(alarms_created) > 0:
163 log.debug("Cleaning alarm resources in MON")
164 for alarm in alarms_created:
165 try:
garciadeblas4584f8e2021-05-14 16:50:06 +0200166 await self.mon_client.delete_alarm(
167 alarm.nsr_id,
168 alarm.vnf_member_index,
169 alarm.vdu_name,
170 alarm.alarm_uuid,
171 )
Benjamin Diazacac7552019-05-23 14:19:06 -0300172 except ValueError:
garciadeblas4584f8e2021-05-14 16:50:06 +0200173 log.exception(
174 "Error deleting alarm in MON %s", alarm.alarm_uuid
175 )
Benjamin Diazacac7552019-05-23 14:19:06 -0300176 raise e
177 finally:
178 database.db.close()
Benjamin Diazf7451f82019-04-01 14:56:26 -0300179
180 async def delete_orphaned_alarms(self, nsr_id):
garciaale7ce998f2020-12-03 18:05:24 -0300181 # TODO: Review as it seems this code is never called
Benjamin Diazf7451f82019-04-01 14:56:26 -0300182 log.info("Deleting orphaned vnf alarms for network service %s", nsr_id)
183 database.db.connect()
Benjamin Diazacac7552019-05-23 14:19:06 -0300184 try:
185 with database.db.atomic():
Benjamin Diazf7451f82019-04-01 14:56:26 -0300186 for alarm in VnfAlarmRepository.list(VnfAlarm.nsr_id == nsr_id):
187 try:
garciadeblas4584f8e2021-05-14 16:50:06 +0200188 self.db_client.get_vdur(
189 nsr_id, alarm.vnf_member_index, alarm.vdu_name
190 )
Benjamin Diazf7451f82019-04-01 14:56:26 -0300191 except VdurNotFound:
192 log.debug("Deleting orphaned alarm %s", alarm.alarm_uuid)
193 try:
194 await self.mon_client.delete_alarm(
195 alarm.nsr_id,
196 alarm.vnf_member_index,
197 alarm.vdu_name,
garciadeblas4584f8e2021-05-14 16:50:06 +0200198 alarm.alarm_uuid,
199 )
Benjamin Diazf7451f82019-04-01 14:56:26 -0300200 except ValueError:
garciadeblas4584f8e2021-05-14 16:50:06 +0200201 log.exception(
202 "Error deleting alarm in MON %s", alarm.alarm_uuid
203 )
Benjamin Diazf7451f82019-04-01 14:56:26 -0300204 alarm.delete_instance()
Benjamin Diazacac7552019-05-23 14:19:06 -0300205 except Exception as e:
206 log.exception("Error deleting orphaned alarms:")
207 raise e
208 finally:
209 database.db.close()
Benjamin Diazf7451f82019-04-01 14:56:26 -0300210
elumalai1d7fa332022-04-12 12:54:37 +0530211 async def delete_vnf_alarms(self, nsr_id, vnf_member_index=None):
Benjamin Diazf7451f82019-04-01 14:56:26 -0300212 log.info("Deleting vnf alarms for network service %s", nsr_id)
213 database.db.connect()
Benjamin Diazacac7552019-05-23 14:19:06 -0300214 try:
215 with database.db.atomic():
elumalai1d7fa332022-04-12 12:54:37 +0530216 if vnf_member_index is None:
217 alarm_conditions = VnfAlarm.nsr_id == nsr_id
218 else:
garciadeblasbe42d542022-11-14 00:29:47 +0100219 query_list = [
220 VnfAlarm.nsr_id == nsr_id,
221 VnfAlarm.vnf_member_index == vnf_member_index,
222 ]
elumalai1d7fa332022-04-12 12:54:37 +0530223 alarm_conditions = functools.reduce(operator.and_, query_list)
224 for alarm in VnfAlarmRepository.list(alarm_conditions):
Benjamin Diazf7451f82019-04-01 14:56:26 -0300225 log.debug("Deleting vnf alarm %s", alarm.alarm_uuid)
226 try:
227 await self.mon_client.delete_alarm(
228 alarm.nsr_id,
229 alarm.vnf_member_index,
230 alarm.vdu_name,
garciadeblas4584f8e2021-05-14 16:50:06 +0200231 alarm.alarm_uuid,
232 )
Benjamin Diazf7451f82019-04-01 14:56:26 -0300233 except ValueError:
garciadeblas4584f8e2021-05-14 16:50:06 +0200234 log.exception(
235 "Error deleting alarm in MON %s", alarm.alarm_uuid
236 )
Benjamin Diazf7451f82019-04-01 14:56:26 -0300237 alarm.delete_instance()
238
Benjamin Diazacac7552019-05-23 14:19:06 -0300239 except Exception as e:
240 log.exception("Error deleting vnf alarms:")
241 raise e
242 finally:
243 database.db.close()
Benjamin Diazf7451f82019-04-01 14:56:26 -0300244
245 async def handle_alarm(self, alarm_uuid: str, status: str, payload: dict):
garciadeblasbe42d542022-11-14 00:29:47 +0100246 alert_timeout = int(self.conf.get("alert", "timeout"))
Benjamin Diazf7451f82019-04-01 14:56:26 -0300247 database.db.connect()
248 try:
249 with database.db.atomic():
250 alarm = VnfAlarmRepository.get(VnfAlarm.alarm_uuid == alarm_uuid)
garciadeblas4584f8e2021-05-14 16:50:06 +0200251 log.debug(
252 "Handling vnf alarm %s with status %s", alarm.alarm_id, status
253 )
Benjamin Diazf7451f82019-04-01 14:56:26 -0300254 for action in alarm.actions:
sritharan3fbf2fb2022-04-07 05:20:38 +0000255 """
256 Compares the current status with the last_action status.
257 If both the status are 'alarm', it avoid sending repetitive alarm notification.
258 If both the status are 'ok', it avoid sending repetitive ok notification.
259 """
Benjamin Diazf7451f82019-04-01 14:56:26 -0300260 if action.type == status:
garciadeblasbe42d542022-11-14 00:29:47 +0100261 if bool(self.conf.get("alert", "enhanced_alarms")):
262 if (
263 status != "ok"
264 or (status == "ok" and alarm.ok_ack is False)
265 ) and (
266 status != "alarm"
267 or (status == "alarm" and alarm.alarm_ack is False)
268 ):
sritharan3fbf2fb2022-04-07 05:20:38 +0000269 log.info(
270 "Executing request to url %s for vnf alarm %s with status %s",
271 action.url,
272 alarm.alarm_id,
garciadeblasbe42d542022-11-14 00:29:47 +0100273 status,
sritharan3fbf2fb2022-04-07 05:20:38 +0000274 )
275 try:
3717831ded242024-03-26 05:32:43 +0000276 if (
277 status == "alarm"
278 and alarm.last_action == "ok"
279 or (
280 status == "alarm"
281 and alarm.last_action == "insufficient-data"
282 )
283 ):
sritharan3fbf2fb2022-04-07 05:20:38 +0000284 alarm.id_suffix += 1
285 alarm.ok_ack = False
286 if status == "ok" and alarm.last_action == "alarm":
287 alarm.alarm_ack = False
288 alarm.last_action = status
289 alarm.save()
290 except Exception as e:
291 log.exception(e)
292
garciadeblasbe42d542022-11-14 00:29:47 +0100293 payload["notify_details"][
294 "alarm_number"
295 ] = alarm.id_suffix
sritharan3fbf2fb2022-04-07 05:20:38 +0000296 headers = {"content-type": "application/json"}
297 try:
garciadeblasbe42d542022-11-14 00:29:47 +0100298 resp = requests.post(
299 url=action.url,
300 data=json.dumps(payload),
301 headers=headers,
garciadeblasbe42d542022-11-14 00:29:47 +0100302 timeout=alert_timeout,
303 )
sritharan3fbf2fb2022-04-07 05:20:38 +0000304 log.info("Response %s", resp)
305 if resp.status_code == 200:
306 if status == "ok":
307 alarm.ok_ack = True
308 alarm.save()
309 if status == "alarm":
310 alarm.alarm_ack = True
311 alarm.save()
312 if status == "insufficient-data":
313 alarm.alarm_ack = False
314 alarm.ok_ack = False
315 alarm.save()
garciadeblasbe42d542022-11-14 00:29:47 +0100316 except ConnectionError:
317 log.exception(
318 "Error connecting to url %s", action.url
319 )
sritharan3fbf2fb2022-04-07 05:20:38 +0000320 except RequestException as e:
garciadeblasbe42d542022-11-14 00:29:47 +0100321 log.info(
322 "Error: RequestException while connecting to url %s",
323 action.url,
324 )
sritharan3fbf2fb2022-04-07 05:20:38 +0000325 log.debug("RequestException %s", e)
326
sritharan3fbf2fb2022-04-07 05:20:38 +0000327 else:
328 log.info(
329 "Executing request to url %s for vnf alarm %s with status %s",
330 action.url,
331 alarm.alarm_id,
garciadeblasbe42d542022-11-14 00:29:47 +0100332 status,
sritharan3fbf2fb2022-04-07 05:20:38 +0000333 )
334 try:
garciadeblasbe42d542022-11-14 00:29:47 +0100335 requests.post(
336 url=action.url,
337 json=json.dumps(payload),
338 timeout=alert_timeout,
339 )
sritharan3fbf2fb2022-04-07 05:20:38 +0000340 except ConnectionError:
341 log.exception("Error connecting to url %s", action.url)
garciadeblasbe42d542022-11-14 00:29:47 +0100342 except RequestException as e:
343 log.info(
344 "Error: RequestException while connecting to url %s",
345 action.url,
346 )
347 log.debug("RequestException %s", e)
Atul Agarwaldb8c1052020-04-28 15:42:28 +0530348
Benjamin Diazf7451f82019-04-01 14:56:26 -0300349 except VnfAlarm.DoesNotExist:
garciadeblas4584f8e2021-05-14 16:50:06 +0200350 log.debug(
351 "There is no alarming action configured for alarm %s.", alarm_uuid
352 )
Benjamin Diazf7451f82019-04-01 14:56:26 -0300353 finally:
354 database.db.close()
Gianpietro Lavado41610192019-12-06 15:46:23 +0000355
garciaale7ce998f2020-12-03 18:05:24 -0300356 def _get_metric_name(self, vnf_monitoring_param: dict):
garciadeblas4584f8e2021-05-14 16:50:06 +0200357 if "performance-metric" in vnf_monitoring_param:
358 return vnf_monitoring_param["performance-metric"]
359 raise ValueError(
360 "No metric name found for vnf_monitoring_param %s"
361 % vnf_monitoring_param["id"]
362 )