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",
276 if status
== "alarm" and alarm
.last_action
== "ok":
279 if status
== "ok" and alarm
.last_action
== "alarm":
280 alarm
.alarm_ack
= False
281 alarm
.last_action
= status
283 except Exception as e
:
286 payload
["notify_details"][
289 headers
= {"content-type": "application/json"}
291 resp
= requests
.post(
293 data
=json
.dumps(payload
),
295 timeout
=alert_timeout
,
297 log
.info("Response %s", resp
)
298 if resp
.status_code
== 200:
302 if status
== "alarm":
303 alarm
.alarm_ack
= True
305 if status
== "insufficient-data":
306 alarm
.alarm_ack
= False
309 except ConnectionError
:
311 "Error connecting to url %s", action
.url
313 except RequestException
as e
:
315 "Error: RequestException while connecting to url %s",
318 log
.debug("RequestException %s", e
)
322 "Executing request to url %s for vnf alarm %s with status %s",
330 json
=json
.dumps(payload
),
331 timeout
=alert_timeout
,
333 except ConnectionError
:
334 log
.exception("Error connecting to url %s", action
.url
)
335 except RequestException
as e
:
337 "Error: RequestException while connecting to url %s",
340 log
.debug("RequestException %s", e
)
342 except VnfAlarm
.DoesNotExist
:
344 "There is no alarming action configured for alarm %s.", alarm_uuid
349 def _get_metric_name(self
, vnf_monitoring_param
: dict):
350 if "performance-metric" in vnf_monitoring_param
:
351 return vnf_monitoring_param
["performance-metric"]
353 "No metric name found for vnf_monitoring_param %s"
354 % vnf_monitoring_param
["id"]