d1d1ea567a335b2c6644437423f737f78c7167a0
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
30 from requests
.exceptions
import ConnectionError
, RequestException
32 from osm_policy_module
.common
.common_db_client
import CommonDbClient
33 from osm_policy_module
.common
.lcm_client
import LcmClient
34 from osm_policy_module
.common
.mon_client
import MonClient
35 from osm_policy_module
.core
import database
36 from osm_policy_module
.core
.config
import Config
37 from osm_policy_module
.core
.database
import (
40 AlarmActionRepository
,
42 from osm_policy_module
.core
.exceptions
import VdurNotFound
44 log
= logging
.getLogger(__name__
)
47 class AlarmingService
:
48 def __init__(self
, config
: Config
, loop
=None):
51 loop
= asyncio
.get_event_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
)
57 async def configure_vnf_alarms(self
, nsr_id
: str):
58 log
.info("Configuring vnf alarms for network service %s", nsr_id
)
62 with database
.db
.atomic():
63 vnfrs
= self
.db_client
.get_vnfrs(nsr_id
)
65 log
.debug("Processing vnfr: %s", vnfr
)
66 vnfd
= self
.db_client
.get_vnfd(vnfr
["vnfd-id"])
67 for vdur
in vnfr
["vdur"]:
70 lambda vdu
: vdu
["id"] == vdur
["vdu-id-ref"], vnfd
["vdu"]
74 alarm_descriptors
= vdu
["alarm"]
75 for alarm_descriptor
in alarm_descriptors
:
77 VnfAlarmRepository
.get(
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
,
86 "vdu %s already has an alarm configured with same id %s",
88 alarm_descriptor
["alarm-id"],
91 except VnfAlarm
.DoesNotExist
:
93 vnf_monitoring_param
= next(
95 lambda param
: param
["id"]
96 == alarm_descriptor
["vnf-monitoring-param-ref"],
97 vdu
.get("monitoring-parameter", []),
101 metric_name
= self
._get
_metric
_name
(
104 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"])
111 alarm_action
["webhook"] = [url
["url"]]
112 alarm_uuid
= await self
.mon_client
.create_alarm(
113 metric_name
=metric_name
,
115 vdu_name
=vdur
["name"],
116 vnf_member_index
=vnfr
["member-vnf-index-ref"],
117 threshold
=alarm_descriptor
["value"],
118 operation
=alarm_descriptor
["operation"],
119 action
=str(alarm_action
),
121 alarm
= VnfAlarmRepository
.create(
122 alarm_id
=alarm_descriptor
["alarm-id"],
123 alarm_uuid
=alarm_uuid
,
125 vnf_member_index
=vnfr
["member-vnf-index-ref"],
126 vdu_name
=vdur
["name"],
127 last_action
='insufficient-data',
132 for action_type
in ["ok", "insufficient-data", "alarm"]:
134 "actions" in alarm_descriptor
135 and action_type
in alarm_descriptor
["actions"]
137 for url
in alarm_descriptor
["actions"][
140 AlarmActionRepository
.create(
145 alarms_created
.append(alarm
)
147 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
:
153 await self
.mon_client
.delete_alarm(
155 alarm
.vnf_member_index
,
161 "Error deleting alarm in MON %s", alarm
.alarm_uuid
167 async def delete_orphaned_alarms(self
, nsr_id
):
168 # TODO: Review as it seems this code is never called
169 log
.info("Deleting orphaned vnf alarms for network service %s", nsr_id
)
170 database
.db
.connect()
172 with database
.db
.atomic():
173 for alarm
in VnfAlarmRepository
.list(VnfAlarm
.nsr_id
== nsr_id
):
175 self
.db_client
.get_vdur(
176 nsr_id
, alarm
.vnf_member_index
, alarm
.vdu_name
179 log
.debug("Deleting orphaned alarm %s", alarm
.alarm_uuid
)
181 await self
.mon_client
.delete_alarm(
183 alarm
.vnf_member_index
,
189 "Error deleting alarm in MON %s", alarm
.alarm_uuid
191 alarm
.delete_instance()
192 except Exception as e
:
193 log
.exception("Error deleting orphaned alarms:")
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()
202 with database
.db
.atomic():
203 for alarm
in VnfAlarmRepository
.list(VnfAlarm
.nsr_id
== nsr_id
):
204 log
.debug("Deleting vnf alarm %s", alarm
.alarm_uuid
)
206 await self
.mon_client
.delete_alarm(
208 alarm
.vnf_member_index
,
214 "Error deleting alarm in MON %s", alarm
.alarm_uuid
216 alarm
.delete_instance()
218 except Exception as e
:
219 log
.exception("Error deleting vnf alarms:")
224 async def handle_alarm(self
, alarm_uuid
: str, status
: str, payload
: dict):
225 alert_timeout
= int(self
.conf
.get('alert', 'timeout'))
226 database
.db
.connect()
228 with database
.db
.atomic():
229 alarm
= VnfAlarmRepository
.get(VnfAlarm
.alarm_uuid
== alarm_uuid
)
231 "Handling vnf alarm %s with status %s", alarm
.alarm_id
, status
233 for action
in alarm
.actions
:
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.
239 if action
.type == status
:
240 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))):
244 "Executing request to url %s for vnf alarm %s with status %s",
250 if status
== "alarm" and alarm
.last_action
== "ok":
253 if status
== "ok" and alarm
.last_action
== "alarm":
254 alarm
.alarm_ack
= False
255 alarm
.last_action
= status
257 except Exception as e
:
260 payload
["notify_details"]["alarm_number"] = alarm
.id_suffix
261 headers
= {"content-type": "application/json"}
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:
270 if status
== "alarm":
271 alarm
.alarm_ack
= True
273 if status
== "insufficient-data":
274 alarm
.alarm_ack
= False
277 except RequestException
as e
:
278 log
.info("Error: RequestException while connecting to url %s", action
.url
)
279 log
.debug("RequestException %s", e
)
281 except ConnectionError
:
282 log
.exception("Error connecting to url %s", action
.url
)
285 "Executing request to url %s for vnf alarm %s with status %s",
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
)
298 except VnfAlarm
.DoesNotExist
:
300 "There is no alarming action configured for alarm %s.", alarm_uuid
305 def _get_metric_name(self
, vnf_monitoring_param
: dict):
306 if "performance-metric" in vnf_monitoring_param
:
307 return vnf_monitoring_param
["performance-metric"]
309 "No metric name found for vnf_monitoring_param %s"
310 % vnf_monitoring_param
["id"]