Coverage for src/zenossapi/routers/events.py: 94%
135 statements
« prev ^ index » next coverage.py v7.9.1, created at 2025-06-25 05:47 +0000
« prev ^ index » next coverage.py v7.9.1, created at 2025-06-25 05:47 +0000
1# -*- coding: utf-8 -*-
3"""
4Zenoss evconsole_router
5"""
6import logging
8from zenossapi.apiclient import ZenossAPIClientError
9from zenossapi.routers import ZenossRouter
12class EventsRouter(ZenossRouter):
13 """
14 Class for interacting with Zenoss events.
15 """
17 def __init__(self, url, headers, ssl_verify):
18 super(EventsRouter, self).__init__(url, headers, ssl_verify,
19 'evconsole_router', 'EventsRouter')
21 self.evid = None
22 self.event_states_map = dict(
23 New=0,
24 Acknowledged=1,
25 Suppressed=2,
26 Closed=3,
27 Cleared=4,
28 Aged=6,
29 )
30 self.event_severity_map = dict(
31 Critical=5,
32 Error=4,
33 Warning=3,
34 Info=2,
35 Debug=1,
36 Clear=0,
37 )
39 def __repr__(self):
40 if self.evid:
41 identifier = "id {0}".format(self.evid)
42 else:
43 identifier = "at {0}".format(hex(id(self)))
45 return '<{0} object {1}>'.format(
46 type(self).__name__, identifier
47 )
49 def query_events(self, limit=10, start=0, sort='lastTime', sort_dir='DESC',
50 params=None, exclude=None, keys=None, context=None,
51 archive=False, validate_params=True, detail_format=False):
52 """
53 Get a list of events based on query parameters.
55 Some search parameter examples:
57 Find New events:
58 params=dict(eventState=[0])
59 Find open events (in New or Acknowledged state):
60 params=dict(eventState=[0,1])
61 Find open events for Production servers:
62 params=dict(eventState=[0,1], prodState=[1000])
64 Other handy search keys: agent, count, DevicePriority, firstTime,
65 lastTime, monitor, message, severity, summary
67 Arguments:
68 limit (int): Maximum number of events to get
69 start (int): Minimum index of events to get
70 sort (str): Sort key for events list
71 sort_dir (str): Sort direction, ASC or DESC
72 params (dict): Key/value pairs for filtering the events to include
73 exclude (dict): Key/value pairs to filter out of the found events
74 keys (list): List of keys to include in the return event data
75 context (str): Device or device class uid as context for the query
76 archive (bool): Search the events archive instead of active events
77 validate_params (bool): Check input params against known param list
78 detail_format (bool): Request detail format of return parameters
80 Returns:
81 dict(int, float, list(dict)): ::
83 {
84 'total': Total count of events matched by the query,
85 'ts': Timestamp for the search,
86 'events': List of events found,
87 }
89 """
90 valid_params = {'agent',
91 'component',
92 'count',
93 'dedupid',
94 'device',
95 'deviceClass',
96 'deviceGroups',
97 'devicePriority',
98 'eventClass',
99 'eventGroup',
100 'eventKey',
101 'eventState',
102 'evid',
103 'facility',
104 'firstTime',
105 'ipAddress',
106 'lastTime',
107 'location',
108 'manager',
109 'message',
110 'ntevid',
111 'ownerId',
112 'priority',
113 'prodState',
114 'severity',
115 'stateChange',
116 'summary',
117 'systems'}
119 if params and validate_params:
120 param_keys = set(params.keys())
121 bad_params = param_keys.difference(valid_params)
122 for bad in bad_params:
123 params.pop(bad)
125 events_data = self._router_request(
126 self._make_request_data(
127 'query',
128 dict(
129 limit=limit,
130 start=start,
131 sort=sort,
132 dir=sort_dir,
133 detailFormat=detail_format,
134 params=params,
135 exclusion_filter=exclude,
136 keys=keys,
137 uid=context,
138 archive=archive,
139 )
140 )
141 )
143 return dict(
144 ts=events_data['asof'],
145 total=events_data['totalCount'],
146 events=events_data['events'],
147 )
149 def get_details_by_evid(self, evid):
150 """
151 Get the details of an event by the event ID.
153 Arguments:
154 evid (str): The event ID
156 Returns:
157 dict: Event details
158 """
159 event_data = self._router_request(
160 self._make_request_data(
161 'detail',
162 dict(
163 evid=evid,
164 )
165 )
166 )
168 return event_data['event'][0]
170 def event_actions(self, action, evids=None, exclude_ids=None, params=None,
171 context=None, last_change=None, limit=None, timeout=60):
172 """
173 Close events by either passing a list of event ids or by a query.
175 Arguments:
176 action (str): The action to take on the event - close,
177 ack, or reopen
178 evids (list): List of event IDs to close
179 exclude_ids (list): List of event IDs to exclude from the close
180 params (dict): Key/value pairs for filtering the events to close
181 context (str): Device or device class uid as context for the query
182 last_change (float): Timestamp - only close events if there has
183 been no change since this time
184 limit (int): Maximum number of events to return in the query
185 timeout (int): Number of seconds before the query times out
186 """
187 if action not in ['close', 'acknowledge', 'reopen']:
188 raise ZenossAPIClientError("Unknown event action: {0}".format(action))
190 self._router_request(
191 self._make_request_data(
192 action,
193 dict(
194 evids=evids,
195 excludeIds=exclude_ids,
196 params=params,
197 uid=context,
198 asof=last_change,
199 limit=limit,
200 timeout=timeout,
201 )
202 )
203 )
205 return True
207 def get_config(self):
208 """
209 Get the event handling configuration.
211 Returns:
212 list(dict):
213 """
214 config_data = self._router_request(
215 self._make_request_data(
216 'getConfig',
217 dict(),
218 )
219 )
221 return config_data['data']
223 def update_config(self, config_values):
224 """
225 Update the Zenoss event handling configuration.
227 Arguments:
228 config_values (dict): Key/value pairs of the config
229 values to change.
230 """
231 self._router_request(
232 self._make_request_data(
233 'setConfigValues',
234 dict(values=config_values),
235 )
236 )
238 return True
240 def get_event_by_evid(self, evid):
241 """
242 Get an event by its event id
244 Arguments:
245 evid (str): The event id
247 Returns:
248 ZenossEvent:
249 """
250 event_data = self.get_details_by_evid(evid)
252 return ZenossEvent(
253 self.api_url,
254 self.api_headers,
255 self.ssl_verify,
256 event_data,
257 )
259 def list_open_events(self, limit=10, start=0, sort='lastTime', sort_dir='DESC'):
260 """
261 Get a list of all open events (new or acknowledged state)
263 Arguments:
264 limit (int): Maximum number of events to return
265 start (int): Minimum index of events to get
266 sort (str): Sort key for events list
267 sort_dir (str): Sort direction, ASC or DESC
269 Returns:
270 dict:
271 """
272 return self.query_events(
273 limit=limit,
274 start=start,
275 sort=sort,
276 sort_dir=sort_dir,
277 params=dict(eventState=[0, 1], severity=[3, 4, 5])
278 )
280 def get_open_events(self, limit=10, start=0, sort='lastTime', sort_dir='DESC'):
281 """
282 Get all open events (new or acknowledged state)
284 Arguments:
285 limit (int): Maximum number of events to return
286 start (int): Minimum index of events to get
287 sort (str): Sort key for events list
288 sort_dir (str): Sort direction, ASC or DESC
290 Returns:
291 list(ZenossEvent):
292 """
293 events_data = self.query_events(
294 limit=limit,
295 start=start,
296 sort=sort,
297 sort_dir=sort_dir,
298 params=dict(eventState=[0, 1], severity=[3, 4, 5]),
299 keys=['evid']
300 )
302 events = []
303 for e in events_data['events']:
304 evdetails = self.get_details_by_evid(e['evid'])
305 events.append(
306 ZenossEvent(
307 self.api_url,
308 self.api_headers,
309 self.ssl_verify,
310 evdetails,
311 )
312 )
314 return events
316 def list_open_production_events(self, limit=10, start=0, sort='lastTime',
317 sort_dir='DESC'):
318 """
319 Get a list of all open events (new or acknowledged state) for devices
320 with a production state of Production
322 Arguments:
323 limit (int): Maximum number of events to return
324 start (int): Minimum index of events to get
325 sort (str): Sort key for events list
326 sort_dir (str): Sort direction, ASC or DESC
328 Returns:
329 dict:
330 """
331 return self.query_events(
332 limit=limit,
333 start=start,
334 sort=sort,
335 sort_dir=sort_dir,
336 params=dict(eventState=[0, 1], prodState=[1000], severity=[3, 4, 5])
337 )
339 def get_open_production_events(self, limit=10, start=0, sort='lastTime',
340 sort_dir='DESC'):
341 """
342 Get all open events (new or acknowledged state) for devices with a
343 production state of Production
345 Arguments:
346 limit (int): Maximum number of events to return
347 start (int): Minimum index of events to get
348 sort (str): Sort key for events list
349 sort_dir (str): Sort direction, ASC or DESC
351 Returns:
352 list(ZenossEvent):
353 """
354 events_data = self.query_events(
355 limit=limit,
356 start=start,
357 sort=sort,
358 sort_dir=sort_dir,
359 params=dict(eventState=[0, 1], prodState=[1000], severity=[3, 4, 5]),
360 keys=['evid']
361 )
363 events = []
364 for e in events_data['events']:
365 evdetails = self.get_details_by_evid(e['evid'])
366 events.append(
367 ZenossEvent(
368 self.api_url,
369 self.api_headers,
370 self.ssl_verify,
371 evdetails,
372 )
373 )
375 return events
377 def add_event(self, summary, device, severity, component=None,
378 event_class_key='', event_class='/Status', return_event=True, **kwargs):
379 """
380 Create a new Zenoss event.
382 Arguments:
383 summary (str): Summary for the new event
384 device (str): Device ID for the new event
385 component (str): Component UID for the new event
386 severity (str): Severity to assign the new event, must be one of
387 Critical, Error, Warning, Info, Debug, or Clear
388 event_class_key (str): The Event Class Key to assign to the event
389 event_class (str): Event Class for the event
390 return_event (bool): By default the function will return the event details but this can be expensive and usually not needed. Pass in False to disable this behavior
392 Returns:
393 ZenossEvent:
394 """
395 if severity in self.event_severity_map:
396 severity_num = self.event_severity_map[severity]
397 else:
398 severity_num = severity
400 event_spec = dict(
401 summary=summary,
402 device=device,
403 component=component,
404 severity=severity,
405 evclasskey=event_class_key,
406 evclass=event_class,
407 **kwargs
408 )
410 self._router_request(
411 self._make_request_data(
412 'add_event',
413 event_spec,
414 )
415 )
417 if return_event is False:
418 return None
420 ev_filter = dict(
421 summary=summary,
422 device=[device],
423 component=[component] if component else None,
424 severity=severity_num,
425 eventClassKey=event_class_key,
426 **kwargs
427 )
429 # TODO: Add a single retry with a half second or so sleep if no event is returned.
430 # This would help give zenoss time to process the event the first time it's seen.
431 # Once the event is in zenoss and the counter is simply iterating this would be unnecessary.
432 event_data = self.query_events(
433 limit=1,
434 params=ev_filter,
435 keys=['evid']
436 )
437 if len(event_data['events']) > 0:
439 evid = event_data['events'][0]['evid']
441 return self.get_event_by_evid(evid)
443 else:
444 logging.debug('No event returned')
445 return None
447 def clear_heartbeats(self):
448 """
449 Clear all heartbeat events
451 Returns:
452 bool: True on success
453 """
454 self._router_request(
455 self._make_request_data(
456 'clear_heartbeats',
457 dict(),
458 )
459 )
461 return True
463 def clear_heartbeat(self, collector, daemon):
464 """
465 Clear a heartbeat event for a specific daemon.
467 Arguments:
468 collector (str): Collector the daemon is running in,
469 e.g. slvcollector
470 daemon (str): Monitoring daemon to clear the heartbeat event
471 for, e.g. zencommand
472 """
473 self._router_request(
474 self._make_request_data(
475 'clear_heartbeat',
476 dict(
477 monitor=collector,
478 daemon=daemon,
479 )
480 )
481 )
483 return True
486class ZenossEvent(EventsRouter):
487 """
488 Class for Zenoss event objects
489 """
491 def __init__(self, url, headers, ssl_verify, event_data):
492 super(ZenossEvent, self).__init__(url, headers, ssl_verify)
494 self.evid = event_data['evid']
495 self.device_class = event_data['DeviceClass']
496 self.device_groups = event_data['DeviceGroups']
497 self.device_priority = event_data['DevicePriority']
498 self.location = event_data['Location']
499 self.systems = event_data['Systems']
500 self.agent = event_data['agent']
501 self.clearid = event_data['clearid']
502 self.component = event_data['component']
503 self.component_title = event_data['component_title']
504 self.component_url = event_data['component_url']
505 self.component_uuid = event_data['component_uuid']
506 self.count = event_data['count']
507 self.dedupid = event_data['dedupid']
508 self.details = event_data['details']
509 self.device = event_data['device']
510 self.device_title = event_data['device_title']
511 self.device_url = event_data['device_url']
512 self.device_uuid = event_data['device_uuid']
513 self.event_class = event_data['eventClass']
514 self.event_class_key = event_data['eventClassKey']
515 self.event_class_mapping = event_data['eventClassMapping']
516 self.event_class_mapping_url = event_data['eventClassMapping_url']
517 self.event_class_url = event_data['eventClass_url']
518 self.event_group = event_data['eventGroup']
519 self.event_key = event_data['eventKey']
520 self.event_state = event_data['eventState']
521 self.facility = event_data['facility']
522 self.first_time = event_data['firstTime']
523 self.id = event_data['id']
524 self.ip_address = event_data['ipAddress']
525 self.last_time = event_data['lastTime']
526 self.log = event_data['log']
527 self.message = event_data['message']
528 self.collector = event_data['monitor']
529 self.ntevid = event_data['ntevid']
530 self.ownerid = event_data['ownerid']
531 self.priority = event_data['priority']
532 self.production_state = event_data['prodState']
533 self.severity = event_data['severity']
534 self.state_change = event_data['stateChange']
535 self.summary = event_data['summary']
537 def update_log(self, message):
538 """
539 Add an entry to the event's log
541 Arguments:
542 message (str): Log entry to add
543 """
544 self._router_request(
545 self._make_request_data(
546 'write_log',
547 dict(
548 evid=self.evid,
549 message=message,
550 )
551 )
552 )
554 event_detail = self.get_details_by_evid(self.evid)
555 self.log = event_detail['log']
557 return True
559 def close(self):
560 """
561 Close the event.
562 """
563 return self.event_actions('close', [self.evid])
565 def ack(self):
566 """
567 Acknowledge the event.
568 """
569 return self.event_actions('acknowledge', [self.evid])
571 def reopen(self):
572 """
573 Reopen (unacknowledge or unclose) the event.
574 """
575 return self.event_actions('reopen', [self.evid])