blob: dbc375e426ca09ec69839fc369afec0057d29d89 [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##
25import asyncio
26import json
27import logging
elumalai1d7fa332022-04-12 12:54:37 +053028import operator
29import functools
Benjamin Diazf7451f82019-04-01 14:56:26 -030030
31import requests
Atul Agarwal46e7efd2021-10-18 17:22:58 +000032from requests.exceptions import ConnectionError, RequestException
Benjamin Diazf7451f82019-04-01 14:56:26 -030033
34from osm_policy_module.common.common_db_client import CommonDbClient
35from osm_policy_module.common.lcm_client import LcmClient
36from osm_policy_module.common.mon_client import MonClient
37from osm_policy_module.core import database
38from osm_policy_module.core.config import Config
garciadeblas4584f8e2021-05-14 16:50:06 +020039from osm_policy_module.core.database import (
40 VnfAlarm,
41 VnfAlarmRepository,
42 AlarmActionRepository,
43)
Benjamin Diazf7451f82019-04-01 14:56:26 -030044from osm_policy_module.core.exceptions import VdurNotFound
45
46log = logging.getLogger(__name__)
47
48
49class AlarmingService:
Benjamin Diazf7451f82019-04-01 14:56:26 -030050 def __init__(self, config: Config, loop=None):
51 self.conf = config
52 if not loop:
53 loop = asyncio.get_event_loop()
54 self.loop = loop
55 self.db_client = CommonDbClient(config)
56 self.mon_client = MonClient(config, loop=self.loop)
57 self.lcm_client = LcmClient(config, loop=self.loop)
58
elumalaicb5642a2022-04-28 02:51:04 +053059 async def configure_vnf_alarms(self, nsr_id: str, vnf_member_index=None):
Benjamin Diazf7451f82019-04-01 14:56:26 -030060 log.info("Configuring vnf alarms for network service %s", nsr_id)
61 alarms_created = []
Benjamin Diazacac7552019-05-23 14:19:06 -030062 database.db.connect()
63 try:
64 with database.db.atomic():
elumalaicb5642a2022-04-28 02:51:04 +053065 if vnf_member_index is None:
66 vnfrs = self.db_client.get_vnfrs(nsr_id)
67 else:
68 vnfrs = []
69 vnfr = self.db_client.get_vnfr(nsr_id, vnf_member_index)
70 vnfrs.append(vnfr)
71 # vnfrs = self.db_client.get_vnfrs(nsr_id)
Benjamin Diazf7451f82019-04-01 14:56:26 -030072 for vnfr in vnfrs:
73 log.debug("Processing vnfr: %s", vnfr)
garciadeblas4584f8e2021-05-14 16:50:06 +020074 vnfd = self.db_client.get_vnfd(vnfr["vnfd-id"])
75 for vdur in vnfr["vdur"]:
Benjamin Diazf7451f82019-04-01 14:56:26 -030076 vdu = next(
77 filter(
garciadeblas4584f8e2021-05-14 16:50:06 +020078 lambda vdu: vdu["id"] == vdur["vdu-id-ref"], vnfd["vdu"]
Benjamin Diazf7451f82019-04-01 14:56:26 -030079 )
80 )
garciadeblas4584f8e2021-05-14 16:50:06 +020081 if "alarm" in vdu:
82 alarm_descriptors = vdu["alarm"]
Benjamin Diazf7451f82019-04-01 14:56:26 -030083 for alarm_descriptor in alarm_descriptors:
84 try:
85 VnfAlarmRepository.get(
garciadeblas4584f8e2021-05-14 16:50:06 +020086 VnfAlarm.alarm_id
87 == alarm_descriptor["alarm-id"],
88 VnfAlarm.vnf_member_index
89 == vnfr["member-vnf-index-ref"],
90 VnfAlarm.vdu_name == vdur["name"],
91 VnfAlarm.nsr_id == nsr_id,
Benjamin Diazf7451f82019-04-01 14:56:26 -030092 )
garciadeblas4584f8e2021-05-14 16:50:06 +020093 log.debug(
94 "vdu %s already has an alarm configured with same id %s",
95 vdur["name"],
96 alarm_descriptor["alarm-id"],
97 )
Benjamin Diazf7451f82019-04-01 14:56:26 -030098 continue
99 except VnfAlarm.DoesNotExist:
100 pass
101 vnf_monitoring_param = next(
102 filter(
garciadeblas4584f8e2021-05-14 16:50:06 +0200103 lambda param: param["id"]
104 == alarm_descriptor["vnf-monitoring-param-ref"],
105 vdu.get("monitoring-parameter", []),
garciaale7ce998f2020-12-03 18:05:24 -0300106 ),
garciadeblas4584f8e2021-05-14 16:50:06 +0200107 {},
Benjamin Diazf7451f82019-04-01 14:56:26 -0300108 )
garciadeblas4584f8e2021-05-14 16:50:06 +0200109 metric_name = self._get_metric_name(
110 vnf_monitoring_param
111 )
Atul Agarwale9228cf2021-03-19 10:11:38 +0000112 alarm_action = dict()
113 for action_type in ["ok", "insufficient-data", "alarm"]:
garciadeblasbe42d542022-11-14 00:29:47 +0100114 if (
115 "actions" in alarm_descriptor
116 and action_type in alarm_descriptor["actions"]
117 ):
118 for url in alarm_descriptor["actions"][
119 action_type
120 ]:
Atul Agarwale9228cf2021-03-19 10:11:38 +0000121 if "webhook" in alarm_action:
garciadeblasbe42d542022-11-14 00:29:47 +0100122 alarm_action["webhook"].append(
123 url["url"]
124 )
Atul Agarwale9228cf2021-03-19 10:11:38 +0000125 else:
126 alarm_action["webhook"] = [url["url"]]
Benjamin Diazf7451f82019-04-01 14:56:26 -0300127 alarm_uuid = await self.mon_client.create_alarm(
Gianpietro Lavado41610192019-12-06 15:46:23 +0000128 metric_name=metric_name,
Benjamin Diazf7451f82019-04-01 14:56:26 -0300129 ns_id=nsr_id,
garciadeblas4584f8e2021-05-14 16:50:06 +0200130 vdu_name=vdur["name"],
131 vnf_member_index=vnfr["member-vnf-index-ref"],
132 threshold=alarm_descriptor["value"],
133 operation=alarm_descriptor["operation"],
Atul Agarwale9228cf2021-03-19 10:11:38 +0000134 action=str(alarm_action),
Benjamin Diazf7451f82019-04-01 14:56:26 -0300135 )
136 alarm = VnfAlarmRepository.create(
garciadeblas4584f8e2021-05-14 16:50:06 +0200137 alarm_id=alarm_descriptor["alarm-id"],
Benjamin Diazf7451f82019-04-01 14:56:26 -0300138 alarm_uuid=alarm_uuid,
139 nsr_id=nsr_id,
garciadeblas4584f8e2021-05-14 16:50:06 +0200140 vnf_member_index=vnfr["member-vnf-index-ref"],
141 vdu_name=vdur["name"],
garciadeblasbe42d542022-11-14 00:29:47 +0100142 last_action="insufficient-data",
sritharan3fbf2fb2022-04-07 05:20:38 +0000143 id_suffix=0,
144 ok_ack=False,
garciadeblasbe42d542022-11-14 00:29:47 +0100145 alarm_ack=False,
Benjamin Diazf7451f82019-04-01 14:56:26 -0300146 )
garciadeblas4584f8e2021-05-14 16:50:06 +0200147 for action_type in ["ok", "insufficient-data", "alarm"]:
148 if (
149 "actions" in alarm_descriptor
150 and action_type in alarm_descriptor["actions"]
151 ):
152 for url in alarm_descriptor["actions"][
153 action_type
154 ]:
Benjamin Diazf7451f82019-04-01 14:56:26 -0300155 AlarmActionRepository.create(
156 type=action_type,
garciadeblas4584f8e2021-05-14 16:50:06 +0200157 url=url["url"],
158 alarm=alarm,
Benjamin Diazf7451f82019-04-01 14:56:26 -0300159 )
160 alarms_created.append(alarm)
161
Benjamin Diazacac7552019-05-23 14:19:06 -0300162 except Exception as e:
163 log.exception("Error configuring VNF alarms:")
164 if len(alarms_created) > 0:
165 log.debug("Cleaning alarm resources in MON")
166 for alarm in alarms_created:
167 try:
garciadeblas4584f8e2021-05-14 16:50:06 +0200168 await self.mon_client.delete_alarm(
169 alarm.nsr_id,
170 alarm.vnf_member_index,
171 alarm.vdu_name,
172 alarm.alarm_uuid,
173 )
Benjamin Diazacac7552019-05-23 14:19:06 -0300174 except ValueError:
garciadeblas4584f8e2021-05-14 16:50:06 +0200175 log.exception(
176 "Error deleting alarm in MON %s", alarm.alarm_uuid
177 )
Benjamin Diazacac7552019-05-23 14:19:06 -0300178 raise e
179 finally:
180 database.db.close()
Benjamin Diazf7451f82019-04-01 14:56:26 -0300181
182 async def delete_orphaned_alarms(self, nsr_id):
garciaale7ce998f2020-12-03 18:05:24 -0300183 # TODO: Review as it seems this code is never called
Benjamin Diazf7451f82019-04-01 14:56:26 -0300184 log.info("Deleting orphaned vnf alarms for network service %s", nsr_id)
185 database.db.connect()
Benjamin Diazacac7552019-05-23 14:19:06 -0300186 try:
187 with database.db.atomic():
Benjamin Diazf7451f82019-04-01 14:56:26 -0300188 for alarm in VnfAlarmRepository.list(VnfAlarm.nsr_id == nsr_id):
189 try:
garciadeblas4584f8e2021-05-14 16:50:06 +0200190 self.db_client.get_vdur(
191 nsr_id, alarm.vnf_member_index, alarm.vdu_name
192 )
Benjamin Diazf7451f82019-04-01 14:56:26 -0300193 except VdurNotFound:
194 log.debug("Deleting orphaned alarm %s", alarm.alarm_uuid)
195 try:
196 await self.mon_client.delete_alarm(
197 alarm.nsr_id,
198 alarm.vnf_member_index,
199 alarm.vdu_name,
garciadeblas4584f8e2021-05-14 16:50:06 +0200200 alarm.alarm_uuid,
201 )
Benjamin Diazf7451f82019-04-01 14:56:26 -0300202 except ValueError:
garciadeblas4584f8e2021-05-14 16:50:06 +0200203 log.exception(
204 "Error deleting alarm in MON %s", alarm.alarm_uuid
205 )
Benjamin Diazf7451f82019-04-01 14:56:26 -0300206 alarm.delete_instance()
Benjamin Diazacac7552019-05-23 14:19:06 -0300207 except Exception as e:
208 log.exception("Error deleting orphaned alarms:")
209 raise e
210 finally:
211 database.db.close()
Benjamin Diazf7451f82019-04-01 14:56:26 -0300212
elumalai1d7fa332022-04-12 12:54:37 +0530213 async def delete_vnf_alarms(self, nsr_id, vnf_member_index=None):
Benjamin Diazf7451f82019-04-01 14:56:26 -0300214 log.info("Deleting vnf alarms for network service %s", nsr_id)
215 database.db.connect()
Benjamin Diazacac7552019-05-23 14:19:06 -0300216 try:
217 with database.db.atomic():
elumalai1d7fa332022-04-12 12:54:37 +0530218 if vnf_member_index is None:
219 alarm_conditions = VnfAlarm.nsr_id == nsr_id
220 else:
garciadeblasbe42d542022-11-14 00:29:47 +0100221 query_list = [
222 VnfAlarm.nsr_id == nsr_id,
223 VnfAlarm.vnf_member_index == vnf_member_index,
224 ]
elumalai1d7fa332022-04-12 12:54:37 +0530225 alarm_conditions = functools.reduce(operator.and_, query_list)
226 for alarm in VnfAlarmRepository.list(alarm_conditions):
Benjamin Diazf7451f82019-04-01 14:56:26 -0300227 log.debug("Deleting vnf alarm %s", alarm.alarm_uuid)
228 try:
229 await self.mon_client.delete_alarm(
230 alarm.nsr_id,
231 alarm.vnf_member_index,
232 alarm.vdu_name,
garciadeblas4584f8e2021-05-14 16:50:06 +0200233 alarm.alarm_uuid,
234 )
Benjamin Diazf7451f82019-04-01 14:56:26 -0300235 except ValueError:
garciadeblas4584f8e2021-05-14 16:50:06 +0200236 log.exception(
237 "Error deleting alarm in MON %s", alarm.alarm_uuid
238 )
Benjamin Diazf7451f82019-04-01 14:56:26 -0300239 alarm.delete_instance()
240
Benjamin Diazacac7552019-05-23 14:19:06 -0300241 except Exception as e:
242 log.exception("Error deleting vnf alarms:")
243 raise e
244 finally:
245 database.db.close()
Benjamin Diazf7451f82019-04-01 14:56:26 -0300246
247 async def handle_alarm(self, alarm_uuid: str, status: str, payload: dict):
garciadeblasbe42d542022-11-14 00:29:47 +0100248 alert_timeout = int(self.conf.get("alert", "timeout"))
Benjamin Diazf7451f82019-04-01 14:56:26 -0300249 database.db.connect()
250 try:
251 with database.db.atomic():
252 alarm = VnfAlarmRepository.get(VnfAlarm.alarm_uuid == alarm_uuid)
garciadeblas4584f8e2021-05-14 16:50:06 +0200253 log.debug(
254 "Handling vnf alarm %s with status %s", alarm.alarm_id, status
255 )
Benjamin Diazf7451f82019-04-01 14:56:26 -0300256 for action in alarm.actions:
sritharan3fbf2fb2022-04-07 05:20:38 +0000257 """
258 Compares the current status with the last_action status.
259 If both the status are 'alarm', it avoid sending repetitive alarm notification.
260 If both the status are 'ok', it avoid sending repetitive ok notification.
261 """
Benjamin Diazf7451f82019-04-01 14:56:26 -0300262 if action.type == status:
garciadeblasbe42d542022-11-14 00:29:47 +0100263 if bool(self.conf.get("alert", "enhanced_alarms")):
264 if (
265 status != "ok"
266 or (status == "ok" and alarm.ok_ack is False)
267 ) and (
268 status != "alarm"
269 or (status == "alarm" and alarm.alarm_ack is False)
270 ):
sritharan3fbf2fb2022-04-07 05:20:38 +0000271 log.info(
272 "Executing request to url %s for vnf alarm %s with status %s",
273 action.url,
274 alarm.alarm_id,
garciadeblasbe42d542022-11-14 00:29:47 +0100275 status,
sritharan3fbf2fb2022-04-07 05:20:38 +0000276 )
277 try:
278 if status == "alarm" and alarm.last_action == "ok":
279 alarm.id_suffix += 1
280 alarm.ok_ack = False
281 if status == "ok" and alarm.last_action == "alarm":
282 alarm.alarm_ack = False
283 alarm.last_action = status
284 alarm.save()
285 except Exception as e:
286 log.exception(e)
287
garciadeblasbe42d542022-11-14 00:29:47 +0100288 payload["notify_details"][
289 "alarm_number"
290 ] = alarm.id_suffix
sritharan3fbf2fb2022-04-07 05:20:38 +0000291 headers = {"content-type": "application/json"}
292 try:
garciadeblasbe42d542022-11-14 00:29:47 +0100293 resp = requests.post(
294 url=action.url,
295 data=json.dumps(payload),
296 headers=headers,
297 verify=False,
298 timeout=alert_timeout,
299 )
sritharan3fbf2fb2022-04-07 05:20:38 +0000300 log.info("Response %s", resp)
301 if resp.status_code == 200:
302 if status == "ok":
303 alarm.ok_ack = True
304 alarm.save()
305 if status == "alarm":
306 alarm.alarm_ack = True
307 alarm.save()
308 if status == "insufficient-data":
309 alarm.alarm_ack = False
310 alarm.ok_ack = False
311 alarm.save()
garciadeblasbe42d542022-11-14 00:29:47 +0100312 except ConnectionError:
313 log.exception(
314 "Error connecting to url %s", action.url
315 )
sritharan3fbf2fb2022-04-07 05:20:38 +0000316 except RequestException as e:
garciadeblasbe42d542022-11-14 00:29:47 +0100317 log.info(
318 "Error: RequestException while connecting to url %s",
319 action.url,
320 )
sritharan3fbf2fb2022-04-07 05:20:38 +0000321 log.debug("RequestException %s", e)
322
sritharan3fbf2fb2022-04-07 05:20:38 +0000323 else:
324 log.info(
325 "Executing request to url %s for vnf alarm %s with status %s",
326 action.url,
327 alarm.alarm_id,
garciadeblasbe42d542022-11-14 00:29:47 +0100328 status,
sritharan3fbf2fb2022-04-07 05:20:38 +0000329 )
330 try:
garciadeblasbe42d542022-11-14 00:29:47 +0100331 requests.post(
332 url=action.url,
333 json=json.dumps(payload),
334 timeout=alert_timeout,
335 )
sritharan3fbf2fb2022-04-07 05:20:38 +0000336 except ConnectionError:
337 log.exception("Error connecting to url %s", action.url)
garciadeblasbe42d542022-11-14 00:29:47 +0100338 except RequestException as e:
339 log.info(
340 "Error: RequestException while connecting to url %s",
341 action.url,
342 )
343 log.debug("RequestException %s", e)
Atul Agarwaldb8c1052020-04-28 15:42:28 +0530344
Benjamin Diazf7451f82019-04-01 14:56:26 -0300345 except VnfAlarm.DoesNotExist:
garciadeblas4584f8e2021-05-14 16:50:06 +0200346 log.debug(
347 "There is no alarming action configured for alarm %s.", alarm_uuid
348 )
Benjamin Diazf7451f82019-04-01 14:56:26 -0300349 finally:
350 database.db.close()
Gianpietro Lavado41610192019-12-06 15:46:23 +0000351
garciaale7ce998f2020-12-03 18:05:24 -0300352 def _get_metric_name(self, vnf_monitoring_param: dict):
garciadeblas4584f8e2021-05-14 16:50:06 +0200353 if "performance-metric" in vnf_monitoring_param:
354 return vnf_monitoring_param["performance-metric"]
355 raise ValueError(
356 "No metric name found for vnf_monitoring_param %s"
357 % vnf_monitoring_param["id"]
358 )