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
32 from requests
.exceptions
import ConnectionError
, RequestException
34 from osm_policy_module
.common
.common_db_client
import CommonDbClient
35 from osm_policy_module
.common
.lcm_client
import LcmClient
36 from osm_policy_module
.common
.mon_client
import MonClient
37 from osm_policy_module
.core
import database
38 from osm_policy_module
.core
.config
import Config
39 from osm_policy_module
.core
.database
import (
42 AlarmActionRepository
,
44 from osm_policy_module
.core
.exceptions
import VdurNotFound
46 log
= logging
.getLogger(__name__
)
49 class AlarmingService
:
50 def __init__(self
, config
: Config
, loop
=None):
53 loop
= asyncio
.get_event_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
)
59 async def configure_vnf_alarms(self
, nsr_id
: str, vnf_member_index
=None):
60 log
.info("Configuring vnf alarms for network service %s", nsr_id
)
64 with database
.db
.atomic():
65 if vnf_member_index
is None:
66 vnfrs
= self
.db_client
.get_vnfrs(nsr_id
)
69 vnfr
= self
.db_client
.get_vnfr(nsr_id
, vnf_member_index
)
71 # vnfrs = self.db_client.get_vnfrs(nsr_id)
73 log
.debug("Processing vnfr: %s", vnfr
)
74 vnfd
= self
.db_client
.get_vnfd(vnfr
["vnfd-id"])
75 for vdur
in vnfr
["vdur"]:
78 lambda vdu
: vdu
["id"] == vdur
["vdu-id-ref"], vnfd
["vdu"]
82 alarm_descriptors
= vdu
["alarm"]
83 for alarm_descriptor
in alarm_descriptors
:
85 VnfAlarmRepository
.get(
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
,
94 "vdu %s already has an alarm configured with same id %s",
96 alarm_descriptor
["alarm-id"],
99 except VnfAlarm
.DoesNotExist
:
101 vnf_monitoring_param
= next(
103 lambda param
: param
["id"]
104 == alarm_descriptor
["vnf-monitoring-param-ref"],
105 vdu
.get("monitoring-parameter", []),
109 metric_name
= self
._get
_metric
_name
(
112 alarm_action
= dict()
113 for action_type
in ["ok", "insufficient-data", "alarm"]:
114 if "actions" in alarm_descriptor
and action_type
in alarm_descriptor
["actions"]:
115 for url
in alarm_descriptor
["actions"][action_type
]:
116 if "webhook" in alarm_action
:
117 alarm_action
["webhook"].append(url
["url"])
119 alarm_action
["webhook"] = [url
["url"]]
120 alarm_uuid
= await self
.mon_client
.create_alarm(
121 metric_name
=metric_name
,
123 vdu_name
=vdur
["name"],
124 vnf_member_index
=vnfr
["member-vnf-index-ref"],
125 threshold
=alarm_descriptor
["value"],
126 operation
=alarm_descriptor
["operation"],
127 action
=str(alarm_action
),
129 alarm
= VnfAlarmRepository
.create(
130 alarm_id
=alarm_descriptor
["alarm-id"],
131 alarm_uuid
=alarm_uuid
,
133 vnf_member_index
=vnfr
["member-vnf-index-ref"],
134 vdu_name
=vdur
["name"],
135 last_action
='insufficient-data',
140 for action_type
in ["ok", "insufficient-data", "alarm"]:
142 "actions" in alarm_descriptor
143 and action_type
in alarm_descriptor
["actions"]
145 for url
in alarm_descriptor
["actions"][
148 AlarmActionRepository
.create(
153 alarms_created
.append(alarm
)
155 except Exception as e
:
156 log
.exception("Error configuring VNF alarms:")
157 if len(alarms_created
) > 0:
158 log
.debug("Cleaning alarm resources in MON")
159 for alarm
in alarms_created
:
161 await self
.mon_client
.delete_alarm(
163 alarm
.vnf_member_index
,
169 "Error deleting alarm in MON %s", alarm
.alarm_uuid
175 async def delete_orphaned_alarms(self
, nsr_id
):
176 # TODO: Review as it seems this code is never called
177 log
.info("Deleting orphaned vnf alarms for network service %s", nsr_id
)
178 database
.db
.connect()
180 with database
.db
.atomic():
181 for alarm
in VnfAlarmRepository
.list(VnfAlarm
.nsr_id
== nsr_id
):
183 self
.db_client
.get_vdur(
184 nsr_id
, alarm
.vnf_member_index
, alarm
.vdu_name
187 log
.debug("Deleting orphaned alarm %s", alarm
.alarm_uuid
)
189 await self
.mon_client
.delete_alarm(
191 alarm
.vnf_member_index
,
197 "Error deleting alarm in MON %s", alarm
.alarm_uuid
199 alarm
.delete_instance()
200 except Exception as e
:
201 log
.exception("Error deleting orphaned alarms:")
206 async def delete_vnf_alarms(self
, nsr_id
, vnf_member_index
=None):
207 log
.info("Deleting vnf alarms for network service %s", nsr_id
)
208 database
.db
.connect()
210 with database
.db
.atomic():
211 if vnf_member_index
is None:
212 alarm_conditions
= VnfAlarm
.nsr_id
== nsr_id
214 query_list
= [VnfAlarm
.nsr_id
== nsr_id
,
215 VnfAlarm
.vnf_member_index
== vnf_member_index
]
216 alarm_conditions
= functools
.reduce(operator
.and_
, query_list
)
217 for alarm
in VnfAlarmRepository
.list(alarm_conditions
):
218 log
.debug("Deleting vnf alarm %s", alarm
.alarm_uuid
)
220 await self
.mon_client
.delete_alarm(
222 alarm
.vnf_member_index
,
228 "Error deleting alarm in MON %s", alarm
.alarm_uuid
230 alarm
.delete_instance()
232 except Exception as e
:
233 log
.exception("Error deleting vnf alarms:")
238 async def handle_alarm(self
, alarm_uuid
: str, status
: str, payload
: dict):
239 alert_timeout
= int(self
.conf
.get('alert', 'timeout'))
240 database
.db
.connect()
242 with database
.db
.atomic():
243 alarm
= VnfAlarmRepository
.get(VnfAlarm
.alarm_uuid
== alarm_uuid
)
245 "Handling vnf alarm %s with status %s", alarm
.alarm_id
, status
247 for action
in alarm
.actions
:
249 Compares the current status with the last_action status.
250 If both the status are 'alarm', it avoid sending repetitive alarm notification.
251 If both the status are 'ok', it avoid sending repetitive ok notification.
253 if action
.type == status
:
254 if bool(self
.conf
.get('alert', 'enhanced_alarms')):
255 if ((status
!= "ok" or (status
== "ok" and alarm
.ok_ack
is False)) and
256 (status
!= "alarm" or (status
== "alarm" and alarm
.alarm_ack
is False))):
258 "Executing request to url %s for vnf alarm %s with status %s",
264 if status
== "alarm" and alarm
.last_action
== "ok":
267 if status
== "ok" and alarm
.last_action
== "alarm":
268 alarm
.alarm_ack
= False
269 alarm
.last_action
= status
271 except Exception as e
:
274 payload
["notify_details"]["alarm_number"] = alarm
.id_suffix
275 headers
= {"content-type": "application/json"}
277 resp
= requests
.post(url
=action
.url
, data
=json
.dumps(payload
),
278 headers
=headers
, verify
=False, timeout
=alert_timeout
)
279 log
.info("Response %s", resp
)
280 if resp
.status_code
== 200:
284 if status
== "alarm":
285 alarm
.alarm_ack
= True
287 if status
== "insufficient-data":
288 alarm
.alarm_ack
= False
291 except RequestException
as e
:
292 log
.info("Error: RequestException while connecting to url %s", action
.url
)
293 log
.debug("RequestException %s", e
)
295 except ConnectionError
:
296 log
.exception("Error connecting to url %s", action
.url
)
299 "Executing request to url %s for vnf alarm %s with status %s",
305 requests
.post(url
=action
.url
, json
=json
.dumps(payload
), timeout
=alert_timeout
)
306 except RequestException
as e
:
307 log
.info("Error: RequestException while connecting to url %s", action
.url
)
308 log
.debug("RequestException %s", e
)
309 except ConnectionError
:
310 log
.exception("Error connecting to url %s", action
.url
)
312 except VnfAlarm
.DoesNotExist
:
314 "There is no alarming action configured for alarm %s.", alarm_uuid
319 def _get_metric_name(self
, vnf_monitoring_param
: dict):
320 if "performance-metric" in vnf_monitoring_param
:
321 return vnf_monitoring_param
["performance-metric"]
323 "No metric name found for vnf_monitoring_param %s"
324 % vnf_monitoring_param
["id"]