blob: d1d1ea567a335b2c6644437423f737f78c7167a0 [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
28
29import requests
Atul Agarwal46e7efd2021-10-18 17:22:58 +000030from requests.exceptions import ConnectionError, RequestException
Benjamin Diazf7451f82019-04-01 14:56:26 -030031
32from osm_policy_module.common.common_db_client import CommonDbClient
33from osm_policy_module.common.lcm_client import LcmClient
34from osm_policy_module.common.mon_client import MonClient
35from osm_policy_module.core import database
36from osm_policy_module.core.config import Config
garciadeblas4584f8e2021-05-14 16:50:06 +020037from osm_policy_module.core.database import (
38 VnfAlarm,
39 VnfAlarmRepository,
40 AlarmActionRepository,
41)
Benjamin Diazf7451f82019-04-01 14:56:26 -030042from osm_policy_module.core.exceptions import VdurNotFound
43
44log = logging.getLogger(__name__)
45
46
47class AlarmingService:
Benjamin Diazf7451f82019-04-01 14:56:26 -030048 def __init__(self, config: Config, loop=None):
49 self.conf = config
50 if not loop:
51 loop = asyncio.get_event_loop()
52 self.loop = loop
53 self.db_client = CommonDbClient(config)
54 self.mon_client = MonClient(config, loop=self.loop)
55 self.lcm_client = LcmClient(config, loop=self.loop)
56
57 async def configure_vnf_alarms(self, nsr_id: str):
58 log.info("Configuring vnf alarms for network service %s", nsr_id)
59 alarms_created = []
Benjamin Diazacac7552019-05-23 14:19:06 -030060 database.db.connect()
61 try:
62 with database.db.atomic():
Benjamin Diazf7451f82019-04-01 14:56:26 -030063 vnfrs = self.db_client.get_vnfrs(nsr_id)
64 for vnfr in vnfrs:
65 log.debug("Processing vnfr: %s", vnfr)
garciadeblas4584f8e2021-05-14 16:50:06 +020066 vnfd = self.db_client.get_vnfd(vnfr["vnfd-id"])
67 for vdur in vnfr["vdur"]:
Benjamin Diazf7451f82019-04-01 14:56:26 -030068 vdu = next(
69 filter(
garciadeblas4584f8e2021-05-14 16:50:06 +020070 lambda vdu: vdu["id"] == vdur["vdu-id-ref"], vnfd["vdu"]
Benjamin Diazf7451f82019-04-01 14:56:26 -030071 )
72 )
garciadeblas4584f8e2021-05-14 16:50:06 +020073 if "alarm" in vdu:
74 alarm_descriptors = vdu["alarm"]
Benjamin Diazf7451f82019-04-01 14:56:26 -030075 for alarm_descriptor in alarm_descriptors:
76 try:
77 VnfAlarmRepository.get(
garciadeblas4584f8e2021-05-14 16:50:06 +020078 VnfAlarm.alarm_id
79 == alarm_descriptor["alarm-id"],
80 VnfAlarm.vnf_member_index
81 == vnfr["member-vnf-index-ref"],
82 VnfAlarm.vdu_name == vdur["name"],
83 VnfAlarm.nsr_id == nsr_id,
Benjamin Diazf7451f82019-04-01 14:56:26 -030084 )
garciadeblas4584f8e2021-05-14 16:50:06 +020085 log.debug(
86 "vdu %s already has an alarm configured with same id %s",
87 vdur["name"],
88 alarm_descriptor["alarm-id"],
89 )
Benjamin Diazf7451f82019-04-01 14:56:26 -030090 continue
91 except VnfAlarm.DoesNotExist:
92 pass
93 vnf_monitoring_param = next(
94 filter(
garciadeblas4584f8e2021-05-14 16:50:06 +020095 lambda param: param["id"]
96 == alarm_descriptor["vnf-monitoring-param-ref"],
97 vdu.get("monitoring-parameter", []),
garciaale7ce998f2020-12-03 18:05:24 -030098 ),
garciadeblas4584f8e2021-05-14 16:50:06 +020099 {},
Benjamin Diazf7451f82019-04-01 14:56:26 -0300100 )
garciadeblas4584f8e2021-05-14 16:50:06 +0200101 metric_name = self._get_metric_name(
102 vnf_monitoring_param
103 )
Atul Agarwale9228cf2021-03-19 10:11:38 +0000104 alarm_action = dict()
105 for action_type in ["ok", "insufficient-data", "alarm"]:
106 if "actions" in alarm_descriptor and action_type in alarm_descriptor["actions"]:
107 for url in alarm_descriptor["actions"][action_type]:
108 if "webhook" in alarm_action:
109 alarm_action["webhook"].append(url["url"])
110 else:
111 alarm_action["webhook"] = [url["url"]]
Benjamin Diazf7451f82019-04-01 14:56:26 -0300112 alarm_uuid = await self.mon_client.create_alarm(
Gianpietro Lavado41610192019-12-06 15:46:23 +0000113 metric_name=metric_name,
Benjamin Diazf7451f82019-04-01 14:56:26 -0300114 ns_id=nsr_id,
garciadeblas4584f8e2021-05-14 16:50:06 +0200115 vdu_name=vdur["name"],
116 vnf_member_index=vnfr["member-vnf-index-ref"],
117 threshold=alarm_descriptor["value"],
118 operation=alarm_descriptor["operation"],
Atul Agarwale9228cf2021-03-19 10:11:38 +0000119 action=str(alarm_action),
Benjamin Diazf7451f82019-04-01 14:56:26 -0300120 )
121 alarm = VnfAlarmRepository.create(
garciadeblas4584f8e2021-05-14 16:50:06 +0200122 alarm_id=alarm_descriptor["alarm-id"],
Benjamin Diazf7451f82019-04-01 14:56:26 -0300123 alarm_uuid=alarm_uuid,
124 nsr_id=nsr_id,
garciadeblas4584f8e2021-05-14 16:50:06 +0200125 vnf_member_index=vnfr["member-vnf-index-ref"],
126 vdu_name=vdur["name"],
sritharan3fbf2fb2022-04-07 05:20:38 +0000127 last_action='insufficient-data',
128 id_suffix=0,
129 ok_ack=False,
130 alarm_ack=False
Benjamin Diazf7451f82019-04-01 14:56:26 -0300131 )
garciadeblas4584f8e2021-05-14 16:50:06 +0200132 for action_type in ["ok", "insufficient-data", "alarm"]:
133 if (
134 "actions" in alarm_descriptor
135 and action_type in alarm_descriptor["actions"]
136 ):
137 for url in alarm_descriptor["actions"][
138 action_type
139 ]:
Benjamin Diazf7451f82019-04-01 14:56:26 -0300140 AlarmActionRepository.create(
141 type=action_type,
garciadeblas4584f8e2021-05-14 16:50:06 +0200142 url=url["url"],
143 alarm=alarm,
Benjamin Diazf7451f82019-04-01 14:56:26 -0300144 )
145 alarms_created.append(alarm)
146
Benjamin Diazacac7552019-05-23 14:19:06 -0300147 except Exception as e:
148 log.exception("Error configuring VNF alarms:")
149 if len(alarms_created) > 0:
150 log.debug("Cleaning alarm resources in MON")
151 for alarm in alarms_created:
152 try:
garciadeblas4584f8e2021-05-14 16:50:06 +0200153 await self.mon_client.delete_alarm(
154 alarm.nsr_id,
155 alarm.vnf_member_index,
156 alarm.vdu_name,
157 alarm.alarm_uuid,
158 )
Benjamin Diazacac7552019-05-23 14:19:06 -0300159 except ValueError:
garciadeblas4584f8e2021-05-14 16:50:06 +0200160 log.exception(
161 "Error deleting alarm in MON %s", alarm.alarm_uuid
162 )
Benjamin Diazacac7552019-05-23 14:19:06 -0300163 raise e
164 finally:
165 database.db.close()
Benjamin Diazf7451f82019-04-01 14:56:26 -0300166
167 async def delete_orphaned_alarms(self, nsr_id):
garciaale7ce998f2020-12-03 18:05:24 -0300168 # TODO: Review as it seems this code is never called
Benjamin Diazf7451f82019-04-01 14:56:26 -0300169 log.info("Deleting orphaned vnf alarms for network service %s", nsr_id)
170 database.db.connect()
Benjamin Diazacac7552019-05-23 14:19:06 -0300171 try:
172 with database.db.atomic():
Benjamin Diazf7451f82019-04-01 14:56:26 -0300173 for alarm in VnfAlarmRepository.list(VnfAlarm.nsr_id == nsr_id):
174 try:
garciadeblas4584f8e2021-05-14 16:50:06 +0200175 self.db_client.get_vdur(
176 nsr_id, alarm.vnf_member_index, alarm.vdu_name
177 )
Benjamin Diazf7451f82019-04-01 14:56:26 -0300178 except VdurNotFound:
179 log.debug("Deleting orphaned alarm %s", alarm.alarm_uuid)
180 try:
181 await self.mon_client.delete_alarm(
182 alarm.nsr_id,
183 alarm.vnf_member_index,
184 alarm.vdu_name,
garciadeblas4584f8e2021-05-14 16:50:06 +0200185 alarm.alarm_uuid,
186 )
Benjamin Diazf7451f82019-04-01 14:56:26 -0300187 except ValueError:
garciadeblas4584f8e2021-05-14 16:50:06 +0200188 log.exception(
189 "Error deleting alarm in MON %s", alarm.alarm_uuid
190 )
Benjamin Diazf7451f82019-04-01 14:56:26 -0300191 alarm.delete_instance()
Benjamin Diazacac7552019-05-23 14:19:06 -0300192 except Exception as e:
193 log.exception("Error deleting orphaned alarms:")
194 raise e
195 finally:
196 database.db.close()
Benjamin Diazf7451f82019-04-01 14:56:26 -0300197
198 async def delete_vnf_alarms(self, nsr_id):
199 log.info("Deleting vnf alarms for network service %s", nsr_id)
200 database.db.connect()
Benjamin Diazacac7552019-05-23 14:19:06 -0300201 try:
202 with database.db.atomic():
Benjamin Diazf7451f82019-04-01 14:56:26 -0300203 for alarm in VnfAlarmRepository.list(VnfAlarm.nsr_id == nsr_id):
204 log.debug("Deleting vnf alarm %s", alarm.alarm_uuid)
205 try:
206 await self.mon_client.delete_alarm(
207 alarm.nsr_id,
208 alarm.vnf_member_index,
209 alarm.vdu_name,
garciadeblas4584f8e2021-05-14 16:50:06 +0200210 alarm.alarm_uuid,
211 )
Benjamin Diazf7451f82019-04-01 14:56:26 -0300212 except ValueError:
garciadeblas4584f8e2021-05-14 16:50:06 +0200213 log.exception(
214 "Error deleting alarm in MON %s", alarm.alarm_uuid
215 )
Benjamin Diazf7451f82019-04-01 14:56:26 -0300216 alarm.delete_instance()
217
Benjamin Diazacac7552019-05-23 14:19:06 -0300218 except Exception as e:
219 log.exception("Error deleting vnf alarms:")
220 raise e
221 finally:
222 database.db.close()
Benjamin Diazf7451f82019-04-01 14:56:26 -0300223
224 async def handle_alarm(self, alarm_uuid: str, status: str, payload: dict):
Atul Agarwal10e97a42021-10-24 17:05:55 +0000225 alert_timeout = int(self.conf.get('alert', 'timeout'))
Benjamin Diazf7451f82019-04-01 14:56:26 -0300226 database.db.connect()
227 try:
228 with database.db.atomic():
229 alarm = VnfAlarmRepository.get(VnfAlarm.alarm_uuid == alarm_uuid)
garciadeblas4584f8e2021-05-14 16:50:06 +0200230 log.debug(
231 "Handling vnf alarm %s with status %s", alarm.alarm_id, status
232 )
Benjamin Diazf7451f82019-04-01 14:56:26 -0300233 for action in alarm.actions:
sritharan3fbf2fb2022-04-07 05:20:38 +0000234 """
235 Compares the current status with the last_action status.
236 If both the status are 'alarm', it avoid sending repetitive alarm notification.
237 If both the status are 'ok', it avoid sending repetitive ok notification.
238 """
Benjamin Diazf7451f82019-04-01 14:56:26 -0300239 if action.type == status:
sritharan3fbf2fb2022-04-07 05:20:38 +0000240 if bool(self.conf.get('alert', 'enhanced_alarms')):
241 if ((status != "ok" or (status == "ok" and alarm.ok_ack is False)) and
242 (status != "alarm" or (status == "alarm" and alarm.alarm_ack is False))):
243 log.info(
244 "Executing request to url %s for vnf alarm %s with status %s",
245 action.url,
246 alarm.alarm_id,
247 status
248 )
249 try:
250 if status == "alarm" and alarm.last_action == "ok":
251 alarm.id_suffix += 1
252 alarm.ok_ack = False
253 if status == "ok" and alarm.last_action == "alarm":
254 alarm.alarm_ack = False
255 alarm.last_action = status
256 alarm.save()
257 except Exception as e:
258 log.exception(e)
259
260 payload["notify_details"]["alarm_number"] = alarm.id_suffix
261 headers = {"content-type": "application/json"}
262 try:
263 resp = requests.post(url=action.url, data=json.dumps(payload),
264 headers=headers, verify=False, timeout=alert_timeout)
265 log.info("Response %s", resp)
266 if resp.status_code == 200:
267 if status == "ok":
268 alarm.ok_ack = True
269 alarm.save()
270 if status == "alarm":
271 alarm.alarm_ack = True
272 alarm.save()
273 if status == "insufficient-data":
274 alarm.alarm_ack = False
275 alarm.ok_ack = False
276 alarm.save()
277 except RequestException as e:
278 log.info("Error: RequestException while connecting to url %s", action.url)
279 log.debug("RequestException %s", e)
280
281 except ConnectionError:
282 log.exception("Error connecting to url %s", action.url)
283 else:
284 log.info(
285 "Executing request to url %s for vnf alarm %s with status %s",
286 action.url,
287 alarm.alarm_id,
288 status
289 )
290 try:
291 requests.post(url=action.url, json=json.dumps(payload), timeout=alert_timeout)
292 except RequestException as e:
293 log.info("Error: RequestException while connecting to url %s", action.url)
294 log.debug("RequestException %s", e)
295 except ConnectionError:
296 log.exception("Error connecting to url %s", action.url)
Atul Agarwaldb8c1052020-04-28 15:42:28 +0530297
Benjamin Diazf7451f82019-04-01 14:56:26 -0300298 except VnfAlarm.DoesNotExist:
garciadeblas4584f8e2021-05-14 16:50:06 +0200299 log.debug(
300 "There is no alarming action configured for alarm %s.", alarm_uuid
301 )
Benjamin Diazf7451f82019-04-01 14:56:26 -0300302 finally:
303 database.db.close()
Gianpietro Lavado41610192019-12-06 15:46:23 +0000304
garciaale7ce998f2020-12-03 18:05:24 -0300305 def _get_metric_name(self, vnf_monitoring_param: dict):
garciadeblas4584f8e2021-05-14 16:50:06 +0200306 if "performance-metric" in vnf_monitoring_param:
307 return vnf_monitoring_param["performance-metric"]
308 raise ValueError(
309 "No metric name found for vnf_monitoring_param %s"
310 % vnf_monitoring_param["id"]
311 )