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

1# -*- coding: utf-8 -*- 

2 

3""" 

4Zenoss evconsole_router 

5""" 

6import logging 

7 

8from zenossapi.apiclient import ZenossAPIClientError 

9from zenossapi.routers import ZenossRouter 

10 

11 

12class EventsRouter(ZenossRouter): 

13 """ 

14 Class for interacting with Zenoss events. 

15 """ 

16 

17 def __init__(self, url, headers, ssl_verify): 

18 super(EventsRouter, self).__init__(url, headers, ssl_verify, 

19 'evconsole_router', 'EventsRouter') 

20 

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 ) 

38 

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))) 

44 

45 return '<{0} object {1}>'.format( 

46 type(self).__name__, identifier 

47 ) 

48 

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. 

54 

55 Some search parameter examples: 

56 

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]) 

63 

64 Other handy search keys: agent, count, DevicePriority, firstTime, 

65 lastTime, monitor, message, severity, summary 

66 

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 

79 

80 Returns: 

81 dict(int, float, list(dict)): :: 

82 

83 { 

84 'total': Total count of events matched by the query, 

85 'ts': Timestamp for the search, 

86 'events': List of events found, 

87 } 

88 

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'} 

118 

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) 

124 

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 ) 

142 

143 return dict( 

144 ts=events_data['asof'], 

145 total=events_data['totalCount'], 

146 events=events_data['events'], 

147 ) 

148 

149 def get_details_by_evid(self, evid): 

150 """ 

151 Get the details of an event by the event ID. 

152 

153 Arguments: 

154 evid (str): The event ID 

155 

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 ) 

167 

168 return event_data['event'][0] 

169 

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. 

174 

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)) 

189 

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 ) 

204 

205 return True 

206 

207 def get_config(self): 

208 """ 

209 Get the event handling configuration. 

210 

211 Returns: 

212 list(dict): 

213 """ 

214 config_data = self._router_request( 

215 self._make_request_data( 

216 'getConfig', 

217 dict(), 

218 ) 

219 ) 

220 

221 return config_data['data'] 

222 

223 def update_config(self, config_values): 

224 """ 

225 Update the Zenoss event handling configuration. 

226 

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 ) 

237 

238 return True 

239 

240 def get_event_by_evid(self, evid): 

241 """ 

242 Get an event by its event id 

243 

244 Arguments: 

245 evid (str): The event id 

246 

247 Returns: 

248 ZenossEvent: 

249 """ 

250 event_data = self.get_details_by_evid(evid) 

251 

252 return ZenossEvent( 

253 self.api_url, 

254 self.api_headers, 

255 self.ssl_verify, 

256 event_data, 

257 ) 

258 

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) 

262 

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 

268 

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 ) 

279 

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) 

283 

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 

289 

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 ) 

301 

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 ) 

313 

314 return events 

315 

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 

321 

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 

327 

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 ) 

338 

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 

344 

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 

350 

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 ) 

362 

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 ) 

374 

375 return events 

376 

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. 

381 

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 

391 

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 

399 

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 ) 

409 

410 self._router_request( 

411 self._make_request_data( 

412 'add_event', 

413 event_spec, 

414 ) 

415 ) 

416 

417 if return_event is False: 

418 return None 

419 

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 ) 

428 

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: 

438 

439 evid = event_data['events'][0]['evid'] 

440 

441 return self.get_event_by_evid(evid) 

442 

443 else: 

444 logging.debug('No event returned') 

445 return None 

446 

447 def clear_heartbeats(self): 

448 """ 

449 Clear all heartbeat events 

450 

451 Returns: 

452 bool: True on success 

453 """ 

454 self._router_request( 

455 self._make_request_data( 

456 'clear_heartbeats', 

457 dict(), 

458 ) 

459 ) 

460 

461 return True 

462 

463 def clear_heartbeat(self, collector, daemon): 

464 """ 

465 Clear a heartbeat event for a specific daemon. 

466 

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 ) 

482 

483 return True 

484 

485 

486class ZenossEvent(EventsRouter): 

487 """ 

488 Class for Zenoss event objects 

489 """ 

490 

491 def __init__(self, url, headers, ssl_verify, event_data): 

492 super(ZenossEvent, self).__init__(url, headers, ssl_verify) 

493 

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'] 

536 

537 def update_log(self, message): 

538 """ 

539 Add an entry to the event's log 

540 

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 ) 

553 

554 event_detail = self.get_details_by_evid(self.evid) 

555 self.log = event_detail['log'] 

556 

557 return True 

558 

559 def close(self): 

560 """ 

561 Close the event. 

562 """ 

563 return self.event_actions('close', [self.evid]) 

564 

565 def ack(self): 

566 """ 

567 Acknowledge the event. 

568 """ 

569 return self.event_actions('acknowledge', [self.evid]) 

570 

571 def reopen(self): 

572 """ 

573 Reopen (unacknowledge or unclose) the event. 

574 """ 

575 return self.event_actions('reopen', [self.evid])