blob: 9a3ba2d0779099733aaeb0d989af9886d706ae77 [file] [log] [blame]
Lott, Christopher (cl778h)81084bc2020-06-01 20:53:12 -04001# ==================================================================================
2# Copyright (c) 2020 AT&T Intellectual Property.
3# Copyright (c) 2020 Nokia
4#
5# Licensed under the Apache License, Version 2.0 (the "License");
6# you may not use this file except in compliance with the License.
7# You may obtain a copy of the License at
8#
9# http://www.apache.org/licenses/LICENSE-2.0
10#
11# Unless required by applicable law or agreed to in writing, software
12# distributed under the License is distributed on an "AS IS" BASIS,
13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14# See the License for the specific language governing permissions and
15# limitations under the License.
16# ==================================================================================
17"""
18Provides classes and methods to define, raise, reraise and clear alarms.
19All actions are implemented by sending RMR messages to the Alarm Adapter
20that comply with the JSON schema in file alarm-schema.json.
21"""
22
23from ctypes import c_void_p
24from enum import Enum, auto
25import json
26import time
27from mdclogpy import Logger
28from ricxappframe.rmr import rmr
29
30##############
31# PRIVATE API
32##############
33
34mdc_logger = Logger(name=__name__)
35RETRIES = 4
36
37##############
38# PUBLIC API
39##############
40
41# constants
42RIC_ALARM_UPDATE = 13111
43# RIC_ALARM_QUERY = 13112 # TBD
44
45# Publish dict keys as constants for convenience of client code.
46# Mixed lower/upper casing to comply with the Adapter JSON requirements.
47KEY_ALARM = "alarm"
48KEY_MANAGED_OBJECT_ID = "managedObjectId"
49KEY_APPLICATION_ID = "applicationId"
50KEY_SPECIFIC_PROBLEM = "specificProblem"
51KEY_PERCEIVED_SEVERITY = "perceivedSeverity"
52KEY_ADDITIONAL_INFO = "additionalInfo"
53KEY_IDENTIFYING_INFO = "identifyingInfo"
54KEY_ALARM_ACTION = "AlarmAction"
55KEY_ALARM_TIME = "AlarmTime"
56
57
58class AlarmAction(Enum):
59 """
60 Action to perform at the Alarm Adapter
61 """
62 RAISE = auto()
63 CLEAR = auto()
64 CLEARALL = auto()
65
66
67class AlarmSeverity(Enum):
68 """
69 Severity of an alarm
70 """
71 UNSPECIFIED = auto()
72 CRITICAL = auto()
73 MAJOR = auto()
74 MINOR = auto()
75 WARNING = auto()
76 CLEARED = auto()
77 DEFAULT = auto()
78
79
80class AlarmDetail(dict):
81 """
82 An alarm that can be raised or cleared.
83
84 Parameters
85 ----------
86 managed_object_id: str
87 The name of the managed object that is the cause of the fault (required)
88
89 application_id: str
90 The name of the process that raised the alarm (required)
91
92 specific_problem: int
93 The problem that is the cause of the alarm
94
95 perceived_severity: AlarmSeverity
96 The severity of the alarm, a value from the enum.
97
98 identifying_info: str
99 Identifying additional information, which is part of alarm identity
100
101 additional_info: str
102 Additional information given by the application (optional)
103 """
104 # pylint: disable=too-many-arguments
105 def __init__(self,
106 managed_object_id: str,
107 application_id: str,
108 specific_problem: int,
109 perceived_severity: AlarmSeverity,
110 identifying_info: str,
111 additional_info: str = ""):
112 """
113 Creates an object with the specified items.
114 """
115 dict.__init__(self)
116 self[KEY_MANAGED_OBJECT_ID] = managed_object_id
117 self[KEY_APPLICATION_ID] = application_id
118 self[KEY_SPECIFIC_PROBLEM] = specific_problem
119 self[KEY_PERCEIVED_SEVERITY] = perceived_severity.name
120 self[KEY_IDENTIFYING_INFO] = identifying_info
121 self[KEY_ADDITIONAL_INFO] = additional_info
122
123
124class AlarmManager:
125 """
126 Provides an API for an Xapp to raise and clear alarms by sending messages
127 via RMR, which should route the messages to an Alarm Adapter.
128
129 Parameters
130 ----------
131 vctx: ctypes c_void_p
132 Pointer to RMR context obtained by initializing RMR.
133 The context is used to allocate space and send messages.
134 The RMR routing table must have a destination for message
135 type RIC_ALARM_UPDATE as defined in this module.
136
137 managed_object_id: str
138 The name of the managed object that raises alarms
139
140 application_id: str
141 The name of the process that raises alarms
142 """
143 def __init__(self,
144 vctx: c_void_p,
145 managed_object_id: str,
146 application_id: str):
147 """
148 Creates an alarm manager.
149 """
150 self.vctx = vctx
151 self.managed_object_id = managed_object_id
152 self.application_id = application_id
153
154 def create_alarm(self,
155 specific_problem: int,
156 perceived_severity: AlarmSeverity,
157 identifying_info: str,
158 additional_info: str = ""):
159 """
160 Convenience method that creates an alarm instance, an AlarmDetail object,
161 using cached values for managed object ID and application ID.
162
163 Parameters
164 ----------
165 specific_problem: int
166 The problem that is the cause of the alarm
167
168 perceived_severity: AlarmSeverity
169 The severity of the alarm, a value from the enum.
170
171 identifying_info: str
172 Identifying additional information, which is part of alarm identity
173
174 additional_info: str
175 Additional information given by the application (optional)
176
177 Returns
178 -------
179 AlarmDetail
180 """
181 return AlarmDetail(managed_object_id=self.managed_object_id,
182 application_id=self.application_id,
183 specific_problem=specific_problem, perceived_severity=perceived_severity,
184 identifying_info=identifying_info, additional_info=additional_info)
185
186 @staticmethod
187 def _create_alarm_message(alarm: AlarmDetail, action: AlarmAction):
188 """
189 Creates a dict with the specified alarm detail plus action and time.
190 Uses the current system time in milliseconds since the Epoch.
191
192 Parameters
193 ----------
194 detail: AlarmDetail
195 The alarm details.
196
197 action: AlarmAction
198 The action to perform at the Alarm Adapter on this alarm.
199 """
200 return {
201 **alarm,
202 KEY_ALARM_ACTION: action.name,
203 KEY_ALARM_TIME: int(round(time.time() * 1000))
204 }
205
206 def _rmr_send_alarm(self, msg: dict):
207 """
208 Serializes the dict and sends the result via RMR using a predefined message type.
209
210 Parameters
211 ----------
212 msg: dict
213 Dictionary with alarm message to encode and send
214
215 Returns
216 -------
217 bool
218 True if the send succeeded (possibly with retries), False otherwise
219 """
220 payload = json.dumps(msg).encode()
221 mdc_logger.debug("_rmr_send_alarm: payload is {}".format(payload))
222 sbuf = rmr.rmr_alloc_msg(vctx=self.vctx, size=len(payload), payload=payload,
223 mtype=RIC_ALARM_UPDATE, gen_transaction_id=True)
224
225 for _ in range(0, RETRIES):
226 sbuf = rmr.rmr_send_msg(self.vctx, sbuf)
227 post_send_summary = rmr.message_summary(sbuf)
228 mdc_logger.debug("_rmr_send_alarm: try {0} result is {1}".format(_, post_send_summary[rmr.RMR_MS_MSG_STATE]))
229 # stop trying if RMR does not indicate retry
230 if post_send_summary[rmr.RMR_MS_MSG_STATE] != rmr.RMR_ERR_RETRY:
231 break
232
233 rmr.rmr_free_msg(sbuf)
234 if post_send_summary[rmr.RMR_MS_MSG_STATE] != rmr.RMR_OK:
235 mdc_logger.warning("_rmr_send_alarm: failed after {} retries".format(RETRIES))
236 return False
237
238 return True
239
240 def raise_alarm(self, detail: AlarmDetail):
241 """
242 Builds and sends a message to the AlarmAdapter to raise an alarm
243 with the specified detail.
244
245 Parameters
246 ----------
247 detail: AlarmDetail
248 Alarm to raise
249
250 Returns
251 -------
252 bool
253 True if the send succeeded (possibly with retries), False otherwise
254 """
255 msg = self._create_alarm_message(detail, AlarmAction.RAISE)
256 return self._rmr_send_alarm(msg)
257
258 def clear_alarm(self, detail: AlarmDetail):
259 """
260 Builds and sends a message to the AlarmAdapter to clear the alarm
261 with the specified detail.
262
263 Parameters
264 ----------
265 detail: AlarmDetail
266 Alarm to clear
267
268 Returns
269 -------
270 bool
271 True if the send succeeded (possibly with retries), False otherwise
272 """
273 msg = self._create_alarm_message(detail, AlarmAction.CLEAR)
274 return self._rmr_send_alarm(msg)
275
276 def reraise_alarm(self, detail: AlarmDetail):
277 """
278 Builds and sends a message to the AlarmAdapter to clear the alarm with the
279 the specified detail, then builds and sends a message to raise the alarm again.
280
281 Parameters
282 ----------
283 detail: AlarmDetail
284 Alarm to clear and raise again.
285
286 Returns
287 -------
288 bool
289 True if the send succeeded (possibly with retries), False otherwise
290 """
291 success = self.clear_alarm(detail)
292 if success:
293 success = self.raise_alarm(detail)
294 return success
295
296 def clear_all_alarms(self):
297 """
298 Builds and sends a message to the AlarmAdapter to clear all alarms.
299
300 Returns
301 -------
302 bool
303 True if the send succeeded (possibly with retries), False otherwise
304 """
305 detail = self.create_alarm(0, AlarmSeverity.DEFAULT, "", "")
306 msg = self._create_alarm_message(detail, AlarmAction.CLEARALL)
307 return self._rmr_send_alarm(msg)