1 # -*- coding: utf-8 -*-
2 # pylint: disable=no-member
4 # Copyright 2018 Whitestack, LLC
5 # *************************************************************
7 # This file is part of OSM Monitoring module
8 # All Rights Reserved to Whitestack, LLC
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
14 # http://www.apache.org/licenses/LICENSE-2.0
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
22 # For those usages not covered by the Apache License, Version 2.0 please
23 # contact: bdiaz@whitestack.com or glavado@whitestack.com
31 from requests
.exceptions
import ConnectionError
, RequestException
33 from osm_policy_module
.common
.common_db_client
import CommonDbClient
34 from osm_policy_module
.common
.lcm_client
import LcmClient
35 from osm_policy_module
.common
.mon_client
import MonClient
36 from osm_policy_module
.core
import database
37 from osm_policy_module
.core
.config
import Config
38 from osm_policy_module
.core
.database
import (
41 AlarmActionRepository
,
43 from osm_policy_module
.core
.exceptions
import VdurNotFound
45 log
= logging
.getLogger(__name__
)
48 class AlarmingService
:
49 def __init__(self
, config
: Config
):
51 self
.db_client
= CommonDbClient(config
)
52 self
.mon_client
= MonClient(config
)
53 self
.lcm_client
= LcmClient(config
)
55 async def configure_vnf_alarms(self
, nsr_id
: str, vnf_member_index
=None):
56 log
.info("Configuring vnf alarms for network service %s", nsr_id
)
60 with database
.db
.atomic():
61 if vnf_member_index
is None:
62 vnfrs
= self
.db_client
.get_vnfrs(nsr_id
)
65 vnfr
= self
.db_client
.get_vnfr(nsr_id
, vnf_member_index
)
67 # vnfrs = self.db_client.get_vnfrs(nsr_id)
69 log
.debug("Processing vnfr: %s", vnfr
)
70 vnfd
= self
.db_client
.get_vnfd(vnfr
["vnfd-id"])
71 for vdur
in vnfr
["vdur"]:
74 lambda vdu
: vdu
["id"] == vdur
["vdu-id-ref"], vnfd
["vdu"]
78 alarm_descriptors
= vdu
["alarm"]
79 for alarm_descriptor
in alarm_descriptors
:
81 VnfAlarmRepository
.get(
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
,
90 "vdu %s already has an alarm configured with same id %s",
92 alarm_descriptor
["alarm-id"],
95 except VnfAlarm
.DoesNotExist
:
97 vnf_monitoring_param
= next(
99 lambda param
: param
["id"]
100 == alarm_descriptor
["vnf-monitoring-param-ref"],
101 vdu
.get("monitoring-parameter", []),
105 metric_name
= self
._get
_metric
_name
(
108 alarm_action
= dict()
109 for action_type
in ["ok", "insufficient-data", "alarm"]:
111 "actions" in alarm_descriptor
112 and action_type
in alarm_descriptor
["actions"]
114 for url
in alarm_descriptor
["actions"][
117 if "webhook" in alarm_action
:
118 alarm_action
["webhook"].append(
122 alarm_action
["webhook"] = [url
["url"]]
123 alarm_uuid
= await self
.mon_client
.create_alarm(
124 metric_name
=metric_name
,
126 vdu_name
=vdur
["name"],
127 vnf_member_index
=vnfr
["member-vnf-index-ref"],
128 threshold
=alarm_descriptor
["value"],
129 operation
=alarm_descriptor
["operation"],
130 action
=str(alarm_action
),
134 alarm
= VnfAlarmRepository
.create(
135 alarm_id
=alarm_descriptor
["alarm-id"],
136 alarm_uuid
=alarm_uuid
,
138 vnf_member_index
=vnfr
["member-vnf-index-ref"],
139 vdu_name
=vdur
["name"],
140 last_action
="insufficient-data",
145 for action_type
in ["ok", "insufficient-data", "alarm"]:
147 "actions" in alarm_descriptor
148 and action_type
in alarm_descriptor
["actions"]
150 for url
in alarm_descriptor
["actions"][
153 AlarmActionRepository
.create(
158 alarms_created
.append(alarm
)
160 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
:
166 await self
.mon_client
.delete_alarm(
168 alarm
.vnf_member_index
,
174 "Error deleting alarm in MON %s", alarm
.alarm_uuid
180 async def delete_orphaned_alarms(self
, nsr_id
):
181 # TODO: Review as it seems this code is never called
182 log
.info("Deleting orphaned vnf alarms for network service %s", nsr_id
)
183 database
.db
.connect()
185 with database
.db
.atomic():
186 for alarm
in VnfAlarmRepository
.list(VnfAlarm
.nsr_id
== nsr_id
):
188 self
.db_client
.get_vdur(
189 nsr_id
, alarm
.vnf_member_index
, alarm
.vdu_name
192 log
.debug("Deleting orphaned alarm %s", alarm
.alarm_uuid
)
194 await self
.mon_client
.delete_alarm(
196 alarm
.vnf_member_index
,
202 "Error deleting alarm in MON %s", alarm
.alarm_uuid
204 alarm
.delete_instance()
205 except Exception as e
:
206 log
.exception("Error deleting orphaned alarms:")
211 async def delete_vnf_alarms(self
, nsr_id
, vnf_member_index
=None):
212 log
.info("Deleting vnf alarms for network service %s", nsr_id
)
213 database
.db
.connect()
215 with database
.db
.atomic():
216 if vnf_member_index
is None:
217 alarm_conditions
= VnfAlarm
.nsr_id
== nsr_id
220 VnfAlarm
.nsr_id
== nsr_id
,
221 VnfAlarm
.vnf_member_index
== vnf_member_index
,
223 alarm_conditions
= functools
.reduce(operator
.and_
, query_list
)
224 for alarm
in VnfAlarmRepository
.list(alarm_conditions
):
225 log
.debug("Deleting vnf alarm %s", alarm
.alarm_uuid
)
227 await self
.mon_client
.delete_alarm(
229 alarm
.vnf_member_index
,
235 "Error deleting alarm in MON %s", alarm
.alarm_uuid
237 alarm
.delete_instance()
239 except Exception as e
:
240 log
.exception("Error deleting vnf alarms:")
245 async def handle_alarm(self
, alarm_uuid
: str, status
: str, payload
: dict):
246 alert_timeout
= int(self
.conf
.get("alert", "timeout"))
247 database
.db
.connect()
249 with database
.db
.atomic():
250 alarm
= VnfAlarmRepository
.get(VnfAlarm
.alarm_uuid
== alarm_uuid
)
252 "Handling vnf alarm %s with status %s", alarm
.alarm_id
, status
254 for action
in alarm
.actions
:
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.
260 if action
.type == status
:
261 if bool(self
.conf
.get("alert", "enhanced_alarms")):
264 or (status
== "ok" and alarm
.ok_ack
is False)
267 or (status
== "alarm" and alarm
.alarm_ack
is False)
270 "Executing request to url %s for vnf alarm %s with status %s",
278 and alarm
.last_action
== "ok"
281 and alarm
.last_action
== "insufficient-data"
286 if status
== "ok" and alarm
.last_action
== "alarm":
287 alarm
.alarm_ack
= False
288 alarm
.last_action
= status
290 except Exception as e
:
293 payload
["notify_details"][
296 headers
= {"content-type": "application/json"}
298 resp
= requests
.post(
300 data
=json
.dumps(payload
),
302 timeout
=alert_timeout
,
304 log
.info("Response %s", resp
)
305 if resp
.status_code
== 200:
309 if status
== "alarm":
310 alarm
.alarm_ack
= True
312 if status
== "insufficient-data":
313 alarm
.alarm_ack
= False
316 except ConnectionError
:
318 "Error connecting to url %s", action
.url
320 except RequestException
as e
:
322 "Error: RequestException while connecting to url %s",
325 log
.debug("RequestException %s", e
)
329 "Executing request to url %s for vnf alarm %s with status %s",
337 json
=json
.dumps(payload
),
338 timeout
=alert_timeout
,
340 except ConnectionError
:
341 log
.exception("Error connecting to url %s", action
.url
)
342 except RequestException
as e
:
344 "Error: RequestException while connecting to url %s",
347 log
.debug("RequestException %s", e
)
349 except VnfAlarm
.DoesNotExist
:
351 "There is no alarming action configured for alarm %s.", alarm_uuid
356 def _get_metric_name(self
, vnf_monitoring_param
: dict):
357 if "performance-metric" in vnf_monitoring_param
:
358 return vnf_monitoring_param
["performance-metric"]
360 "No metric name found for vnf_monitoring_param %s"
361 % vnf_monitoring_param
["id"]