Package ZenEvents :: Module EventManagerBase
[hide private]
[frames] | no frames]

Source Code for Module ZenEvents.EventManagerBase

   1  ########################################################################### 
   2  # 
   3  # This program is part of Zenoss Core, an open source monitoring platform. 
   4  # Copyright (C) 2007, Zenoss Inc. 
   5  # 
   6  # This program is free software; you can redistribute it and/or modify it 
   7  # under the terms of the GNU General Public License version 2 as published by 
   8  # the Free Software Foundation. 
   9  # 
  10  # For complete information please visit: http://www.zenoss.com/oss/ 
  11  # 
  12  ########################################################################### 
  13   
  14  __doc__ = """EventManagerBase 
  15  Data connector to backend of the event management system. 
  16  """ 
  17   
  18  import time 
  19  import types 
  20  import random 
  21  random.seed() 
  22  import logging 
  23  log = logging.getLogger("zen.Events") 
  24   
  25  from AccessControl import ClassSecurityInfo 
  26  from AccessControl import getSecurityManager 
  27  from Globals import DTMLFile 
  28  from Acquisition import aq_base 
  29  import DateTime 
  30  from Products.ZenModel.ZenossSecurity import * 
  31   
  32  from Products.ZenUtils.ObjectCache import ObjectCache 
  33   
  34   
  35  from ZEvent import ZEvent 
  36  from EventDetail import EventDetail 
  37  from BetterEventDetail import BetterEventDetail 
  38  from EventCommand import EventCommand 
  39  from Products.ZenEvents.Exceptions import * 
  40   
  41  from Products.ZenModel.ZenModelRM import ZenModelRM 
  42  from Products.ZenModel.ZenossSecurity import * 
  43  from Products.ZenRelations.RelSchema import * 
  44  from Products.ZenUtils import Time 
  45  from Products.ZenUtils.FakeRequest import FakeRequest 
  46  from Products.ZenEvents.ZenEventClasses import Status_Ping, Status_Wmi_Conn 
  47  from Products.ZenWidgets import messaging 
  48   
  49  from ZenEventClasses import Unknown 
  50   
  51  from DbAccessBase import DbAccessBase 
  52   
  53  from Products.ZenUtils.Utils import unused 
  54   
  55  __pychecker__="maxargs=16" 
  56   
57 -def evtprep(evts):
58 """ 59 Prepares data from L{Products.ZenEvents.EventManagerBase.getEventSummary} 60 for rendering in the eventrainbow template macro. 61 62 Each cell of the old-style event rainbow needs a CSS class specified in 63 order to render its color, fill and border style. evtprep determines the 64 proper class and returns it, along with a string representation of the 65 number of live and acknowledged events. 66 67 >>> from Products.ZenEvents.EventManagerBase import evtprep 68 >>> evtprep(['zenevents_5_noack noack', 2, 2]) 69 {'cssclass': 'zenevents_5_noack noack empty thin', 'data': '2/2'} 70 >>> evtprep(['zenevents_5_noack noack', 1, 2]) 71 {'cssclass': 'zenevents_5_noack noack', 'data': '1/2'} 72 73 @param evts: A tuple of the form (Severity string, Number Acknowledged int, 74 Number Live int) 75 @type evts: tuple 76 @return: A dictionary of the form {'cssclass': Class string, 'data': Event 77 count representation string} 78 79 """ 80 evtsdata = "%d/%d" % (evts[1],evts[2]) 81 if evts[1]==evts[2] or evts[2]==0: 82 return {'cssclass':evts[0] + " empty thin", 83 'data':evtsdata} 84 else: 85 return {'cssclass':evts[0], 'data':evtsdata}
86 87
88 -class EventManagerBase(ZenModelRM, ObjectCache, DbAccessBase):
89 """ 90 Data connector to backend of the event management system. 91 """ 92 #implements(IEventList, IEventStatus, ISendEvents) 93 94 #FQDNID = hash(socket.getfqdn()) 95 96 eventStateConversions = ( 97 ('New', 0), 98 ('Acknowledged',1), 99 ('Suppressed', 2), 100 #('Bogus', 3), 101 ) 102 103 eventActions = ('status', 'history', 'drop') 104 105 severityConversions = ( 106 ('Critical', 5), 107 ('Error', 4), 108 ('Warning', 3), 109 ('Info', 2), 110 ('Debug', 1), 111 ('Clear', 0), 112 ) 113 severities = dict([(b, a) for a, b in severityConversions]) 114 115 priorityConversions = ( 116 ('None', -1), 117 ('Emergency', 0), 118 ('Alert', 1), 119 ('Critical', 2), 120 ('Error', 3), 121 ('Warning', 4), 122 ('Notice', 6), 123 ('Info', 8), 124 ('Debug', 10), 125 ) 126 priorities = dict([(b, a) for a, b in priorityConversions]) 127 128 statusTable = "status" 129 detailTable = "detail" 130 logTable = "log" 131 lastTimeField = "lastTime" 132 firstTimeField = "firstTime" 133 deviceField = "device" 134 componentField = "component" 135 eventClassField = "eventClass" 136 severityField = "severity" 137 stateField = "eventState" 138 countField = "count" 139 prodStateField = "prodState" 140 DeviceGroupField = "DeviceGroups" 141 SystemField = "Systems" 142 143 DeviceWhere = "\"device = '%s'\" % me.getDmdKey()" 144 DeviceResultFields = ("component", "eventClass", "summary", "firstTime", 145 "lastTime", "count" ) 146 ComponentWhere = ("\"(device = '%s' and component = '%s')\"" 147 " % (me.device().getDmdKey(), me.name())") 148 ComponentResultFields = ("eventClass", "summary", "firstTime", 149 "lastTime", "count" ) 150 IpAddressWhere = "\"ipAddress='%s'\" % (me.getId())" 151 EventClassWhere = "\"eventClass like '%s%%'\" % me.getDmdKey()" 152 EventClassInstWhere = """\"eventClass = '%s' and eventClassKey = '%s'\" % (\ 153 me.getEventClass(), me.eventClassKey)""" 154 DeviceClassWhere = "\"DeviceClass like '%s%%'\" % me.getDmdKey()" 155 LocationWhere = "\"Location like '%s%%'\" % me.getDmdKey()" 156 SystemWhere = "\"Systems like '%%|%s%%'\" % me.getDmdKey()" 157 DeviceGroupWhere = "\"DeviceGroups like '%%|%s%%'\" % me.getDmdKey()" 158 159 defaultResultFields = ("device", "component", "eventClass", "summary", 160 "firstTime", "lastTime", "count" ) 161 162 defaultFields = ('eventState', 'severity', 'evid') 163 164 defaultEventId = ('device', 'component', 'eventClass', 165 'eventKey', 'severity') 166 167 requiredEventFields = ('device', 'summary', 'severity') 168 169 refreshConversionsForm = DTMLFile('dtml/refreshNcoProduct', globals()) 170 171 defaultAvailabilityDays = 7 172 defaultPriority = 3 173 eventAgingHours = 4 174 eventAgingSeverity = 4 175 historyMaxAgeDays = 0 176 177 _properties = ( 178 {'id':'backend', 'type':'string','mode':'r', }, 179 {'id':'username', 'type':'string', 'mode':'w'}, 180 {'id':'password', 'type':'string', 'mode':'w'}, 181 {'id':'host', 'type':'string', 'mode':'w'}, 182 {'id':'database', 'type':'string', 'mode':'w'}, 183 {'id':'port', 'type':'int', 'mode':'w'}, 184 {'id':'defaultWhere', 'type':'text', 'mode':'w'}, 185 {'id':'defaultOrderby', 'type':'text', 'mode':'w'}, 186 {'id':'defaultResultFields', 'type':'lines', 'mode':'w'}, 187 {'id':'statusTable', 'type':'string', 'mode':'w'}, 188 {'id':'detailTable', 'type':'string', 'mode':'w'}, 189 {'id':'logTable', 'type':'string', 'mode':'w'}, 190 {'id':'lastTimeField', 'type':'string', 'mode':'w'}, 191 {'id':'firstTimeField', 'type':'string', 'mode':'w'}, 192 {'id':'deviceField', 'type':'string', 'mode':'w'}, 193 {'id':'componentField', 'type':'string', 'mode':'w'}, 194 {'id':'severityField', 'type':'string', 'mode':'w'}, 195 {'id':'countField', 'type':'string', 'mode':'w'}, 196 {'id':'DeviceGroupField', 'type':'string', 'mode':'w'}, 197 {'id':'SystemField', 'type':'string', 'mode':'w'}, 198 {'id':'DeviceWhere', 'type':'string', 'mode':'w'}, 199 {'id':'DeviceResultFields', 'type':'lines', 'mode':'w'}, 200 {'id':'ComponentResultFields', 'type':'lines', 'mode':'w'}, 201 {'id':'EventClassWhere', 'type':'string', 'mode':'w'}, 202 {'id':'EventClassInstWhere', 'type':'string', 'mode':'w'}, 203 {'id':'DeviceClassWhere', 'type':'string', 'mode':'w'}, 204 {'id':'LocationWhere', 'type':'string', 'mode':'w'}, 205 {'id':'SystemWhere', 'type':'string', 'mode':'w'}, 206 {'id':'DeviceGroupWhere', 'type':'string', 'mode':'w'}, 207 {'id':'requiredEventFields', 'type':'lines', 'mode':'w'}, 208 {'id':'defaultEventId', 'type':'lines', 'mode':'w'}, 209 {'id':'defaultFields', 'type':'lines', 'mode':'w'}, 210 {'id':'timeout', 'type':'int', 'mode':'w'}, 211 {'id':'clearthresh', 'type':'int', 'mode':'w'}, 212 {'id':'defaultAvailabilityDays', 'type':'int', 'mode':'w'}, 213 {'id':'defaultPriority', 'type':'int', 'mode':'w'}, 214 {'id':'eventAgingHours', 'type':'int', 'mode':'w'}, 215 {'id':'eventAgingSeverity', 'type':'int', 'mode':'w'}, 216 {'id':'historyMaxAgeDays', 'type':'int', 'mode':'w'}, 217 ) 218 219 _relations = ( 220 ("commands", ToManyCont(ToOne, "Products.ZenEvents.EventCommand", "eventManager")), 221 ) 222 223 factory_type_information = ( 224 { 225 'immediate_view' : 'editEventManager', 226 'actions' : 227 ( 228 { 'id' : 'edit' 229 , 'name' : 'Edit' 230 , 'action' : 'editEventManager' 231 , 'permissions' : ( "Manage DMD", ) 232 }, 233 { 'id' : 'edit' 234 , 'name' : 'Fields' 235 , 'action' : 'editEventManagerFields' 236 , 'permissions' : ( "Manage DMD", ) 237 }, 238 { 'id' : 'history_edit' 239 , 'name' : 'History Fields' 240 , 'action' : 'editEventManagerHistoryFields' 241 , 'permissions' : ( "Manage DMD", ) 242 }, 243 { 'id' : 'commands' 244 , 'name' : 'Commands' 245 , 'action' : 'listEventCommands' 246 , 'permissions' : ( "Manage DMD", ) 247 }, 248 { 'id' : 'changes' 249 , 'name' : 'Modifications' 250 , 'action' : 'viewHistory' 251 , 'permissions' : (ZEN_VIEW_MODIFICATIONS,) 252 }, 253 ) 254 }, 255 ) 256 257 security = ClassSecurityInfo() 258 259
260 - def __init__(self, id, title='', hostname='localhost', username='root', 261 password='', database='events', port=3306, 262 defaultWhere='',defaultOrderby='',defaultResultFields=[]):
263 """ 264 Sets up event database access and initializes the cache. 265 266 @param id: A unique id 267 @type id: string 268 @param title: A title 269 @type title: string 270 @param hostname: The hostname of the events database server 271 @type hostname: string 272 @param username: The name of a user with permissions to access the 273 events database 274 @type username: string 275 @param password: The password of the user 276 @type password: string 277 @param database: The name of the events database 278 @type database: string 279 @param port: The port on which the database server is listening 280 @type port: int 281 @param defaultWhere: The default where clause to use when building 282 queries 283 @type defaultWhere: string 284 @param defaultOrderby: The default order by clause to use when building 285 queries 286 @type defaultOrderby: string 287 @param defaultResultFields: DEPRECATED. Currently unused. 288 @type defaultResultFields: list 289 290 """ 291 unused(defaultOrderby, defaultResultFields) 292 self.id = id 293 self.title = title 294 self.username=username 295 self.password=password 296 self.database=database 297 self.host=hostname 298 self.port=port 299 DbAccessBase.__init__(self) 300 301 self.defaultWhere = defaultWhere 302 self.defaultOrderby="%s desc, %s desc" % ( 303 self.severityField, self.lastTimeField) 304 305 self._schema = {} 306 self._fieldlist = [] 307 self._colors = () 308 self._ackedcolors = () 309 ObjectCache.__init__(self) 310 self.initCache()
311 312 #========================================================================== 313 # Event query functions from IEventQuery 314 #========================================================================== 315 316
317 - def getEventResultFields(self, context):
318 """ 319 A wrapper for L{lookupManagedEntityResultFields} accepting an object 320 with an C{event_key} attribute. 321 322 >>> class dummy(object): 323 ... event_key = 'Device' 324 ... 325 >>> d = dummy() 326 >>> f = dmd.ZenEventManager.getEventResultFields(d) 327 >>> f==dmd.ZenEventManager.DeviceResultFields 328 True 329 >>> d.event_key = 'Robot' 330 >>> f = dmd.ZenEventManager.getEventResultFields(d) 331 >>> f==dmd.ZenEventManager.defaultResultFields 332 True 333 334 @param context: An object with an C{event_key} attribute. 335 @type context: L{ManagedEntity} 336 @return: A sequence of strings representing columns in the database. 337 @rtype: tuple 338 """ 339 return self.lookupManagedEntityResultFields(getattr(context, 340 'event_key', 'Default'))
341
342 - def getEventListME(self, me, **kwargs):
343 """ 344 Queries the database for events on a managed entity. 345 346 @param me: The object for which to fetch events 347 @type me: L{ManagedEntity} 348 @return: L{ZEvent} objects 349 @rtype: list 350 """ 351 where = "" 352 if hasattr(me, 'getWhere'): 353 where = me.getWhere() 354 else: 355 where = self.lookupManagedEntityWhere(me) 356 try: 357 resultFields = kwargs['resultFields']; del kwargs['resultFields'] 358 except KeyError: 359 resultFields = self.lookupManagedEntityResultFields(me.event_key) 360 return self.getEventList(resultFields=resultFields, where=where, 361 **kwargs)
362 363
364 - def getEventBatchME(self, me, selectstatus=None, resultFields=[], 365 where="", orderby="", severity=None, state=2, 366 startdate=None, enddate=None, offset=0, rows=0, 367 getTotalCount=False, filter="", goodevids=[], 368 badevids=[], **kwargs):
369 """ 370 Returns a batch of events based on criteria from checked rows on the 371 event console. 372 373 The event console can show thousands of events, and we want to support a 374 "Select All" feature; enter this method. It builds a query based on the 375 select status from the console ("All", "None", "Acknowledged", 376 "Unacknowledged") and any checkboxes that have been modified manually. 377 378 @param me: The managed entity for which to query events. 379 @type me: L{ManagedEntity} 380 @param resultFields: The columns to return from the database. 381 @type resultFields: list 382 @param where: The base where clause to modify. 383 @type where: string 384 @param orderby: The "ORDER BY" string governing sort order. 385 @type orderby: string 386 @param severity: The minimum severity for which to query. 387 @type severity: int 388 @param state: The minimum state for which to query. 389 @type state: int 390 @param startdate: The early date limit 391 @type startdate: string, DateTime 392 @param enddate: The late date limit 393 @type enddate: string, DateTime 394 @param offset: The row at which to begin returning 395 @type offset: int 396 @param rows: DEPRECATED The number of rows to return (ignored). 397 @type rows: int 398 @param getTotalCount: Whether or not to return a count of the total 399 number of rows 400 @type getTotalCount: bool 401 @param filter: A glob by which to filter events 402 @type filter: string 403 @param goodevids: Ids of events that specifically should be included 404 @type goodevids: list 405 @param badevids: Ids of events that specifically should not be included 406 @type badevids: list 407 @return: Ids of matching events 408 @rtype: list 409 @todo: Remove unused parameters from the method definition 410 """ 411 unused(getTotalCount, rows) 412 newwhere = self.lookupManagedEntityWhere(me) 413 if where: newwhere = self._wand(newwhere, '%s%s', where, '') 414 where = newwhere 415 badevidsstr, goodevidsstr = '','' 416 if not isinstance(goodevids, (list, tuple)): goodevids = [goodevids] 417 if not isinstance(badevids, (list, tuple)): badevids = [badevids] 418 if badevids: badevidsstr = " and evid not in ('%s')" %( 419 "','".join(badevids)) 420 if goodevids: goodevidsstr = " and evid in ('%s')" %( 421 "','".join(goodevids)) 422 if selectstatus=='all': 423 where += badevidsstr 424 elif selectstatus=='none': 425 where += goodevidsstr or ' and 0' 426 elif selectstatus=='acked': 427 oper = bool(goodevidsstr) and ' or' or ' and' 428 where += goodevidsstr + oper + " (eventstate=1 %s) " % badevidsstr 429 elif selectstatus=='unacked': 430 oper = bool(goodevidsstr) and ' or' or 'and' 431 where += goodevidsstr + oper + " (eventstate=0 %s) " % badevidsstr 432 try: 433 resultFields = kwargs['resultFields']; del kwargs['resultFields'] 434 except KeyError: 435 resultFields = self.lookupManagedEntityResultFields(me.event_key) 436 events = self.getEventList( 437 filter=filter, 438 offset=offset, 439 getTotalCount=False, 440 startdate=startdate, 441 enddate=enddate, severity=severity, 442 state=state, orderby=orderby, 443 resultFields=resultFields, 444 where=where,**kwargs) 445 return [ev.evid for ev in events]
446 447
448 - def restrictedUserFilter(self, where):
449 """This is a hook do not delete me!""" 450 return where
451 452
453 - def getEventList(self, resultFields=None, where="", orderby="", 454 severity=None, state=2, startdate=None, enddate=None, offset=0, 455 rows=0, getTotalCount=False, filter="", **kwargs):
456 """ 457 Fetch a list of events from the database matching certain criteria. 458 459 @param resultFields: The columns to return from the database. 460 @type resultFields: list 461 @param where: The base where clause to modify. 462 @type where: string 463 @param orderby: The "ORDER BY" string governing sort order. 464 @type orderby: string 465 @param severity: The minimum severity for which to query. 466 @type severity: int 467 @param state: The minimum state for which to query. 468 @type state: int 469 @param startdate: The early date limit 470 @type startdate: string, DateTime 471 @param enddate: The late date limit 472 @type enddate: string, DateTime 473 @param offset: The row at which to begin returning 474 @type offset: int 475 @param rows: The number of rows to return. 476 @type rows: int 477 @param getTotalCount: Whether or not to return a count of the total 478 number of rows 479 @type getTotalCount: bool 480 @param filter: A glob by which to filter events 481 @type filter: string 482 @return: Matching events as L{ZEvent}s. 483 @rtype: list 484 @todo: Remove unused parameters from the method definition 485 """ 486 unused(kwargs) 487 try: 488 where = self.restrictedUserFilter(where) 489 if not resultFields: 490 resultFields = self.defaultResultFields 491 resultFields = list(resultFields) 492 resultFields.extend(self.defaultFields) 493 calcfoundrows = '' 494 if getTotalCount: 495 calcfoundrows = 'SQL_CALC_FOUND_ROWS' 496 select = ["select ", calcfoundrows, ','.join(resultFields), 497 "from %s where" % self.statusTable ] 498 if not where: 499 where = self.defaultWhere 500 where = self._wand(where, "%s >= %s", self.severityField, severity) 501 where = self._wand(where, "%s <= %s", self.stateField, state) 502 if filter: 503 where += ' and (%s) ' % (' or '.join(['%s LIKE "%%%s%%"' % ( 504 x, filter) for x in resultFields])) 505 if startdate: 506 startdate, enddate = self._setupDateRange(startdate, enddate) 507 where += " and %s >= '%s' and %s <= '%s'" % ( 508 self.lastTimeField, startdate, 509 self.firstTimeField, enddate) 510 select.append(where) 511 if not orderby: 512 orderby = self.defaultOrderby 513 if orderby: 514 select.append("order by") 515 select.append(orderby) 516 if rows: 517 select.append("limit %s, %s" % (offset, rows)) 518 select.append(';') 519 select = " ".join(select) 520 if getTotalCount: 521 try: retdata, totalCount = self.checkCache(select) 522 except TypeError: 523 retdata, totalCount = self.checkCache(select), 100 524 else: retdata = self.checkCache(select) 525 if not False: 526 conn = self.connect() 527 try: 528 curs = conn.cursor() 529 curs.execute(select) 530 retdata = [] 531 # iterate through the data results and convert to python 532 # objects 533 if self.checkRemotePerm(ZEN_VIEW, self.dmd.Events): 534 eventPermission = True 535 else: 536 eventPermission = False 537 for row in curs.fetchall(): 538 row = map(self.convert, resultFields, row) 539 evt = ZEvent(self, resultFields, row, eventPermission) 540 retdata.append(evt) 541 if getTotalCount: 542 curs.execute("SELECT FOUND_ROWS()") 543 totalCount = curs.fetchone()[0] 544 finally: self.close(conn) 545 if getTotalCount: self.addToCache(select, (retdata, totalCount)) 546 else: self.addToCache(select, retdata) 547 self.cleanCache() 548 if getTotalCount: 549 return retdata, totalCount 550 else: return retdata 551 except: 552 log.exception("Failure querying events") 553 raise
554 555
556 - def getEventSummaryME(self, me, severity=1, state=1, prodState=None):
557 """ 558 Return the CSS class, number of acknowledged events, and number of 559 unacknowledged events, per severity, for a C{ManagedEntity}. 560 561 @param me: The object of the inquiry. 562 @type me: L{ManagedEntity} 563 @param severity: The minimum severity for which to retrieve events 564 @type severity: int 565 @param state: The minimum state for which to retrieve events 566 @type state: int 567 @param prodState: The minimum production state for which to retrieve 568 events 569 @type prodState: int 570 @return: List of lists of the form [class, acked count, unacked count]. 571 @rtype: list 572 """ 573 try: 574 where = self.lookupManagedEntityWhere(me) 575 return self.getEventSummary(where, severity, state, prodState) 576 except: 577 log.exception("event summary for %s failed" % me.getDmdKey()) 578 raise
579 580
581 - def getEventSummary(self, where="", severity=1, state=1, prodState=None):
582 """ 583 Return a list of tuples with number of events and the color of the 584 severity that the number represents. 585 586 This method should not be called directly, but overridden by subclasses. 587 588 @param where: The base where clause to modify. 589 @type where: string 590 @param severity: The minimum severity for which to retrieve events 591 @type severity: int 592 @param state: The minimum state for which to retrieve events 593 @type state: int 594 @param prodState: The minimum production state for which to retrieve 595 events 596 @type prodState: int 597 @return: List of lists of the form [class, acked count, unacked count]. 598 @rtype: list 599 """ 600 raise NotImplementedError
601 602
603 - def getEventDetailFromStatusOrHistory(self, evid=None, dedupid=None, 604 better=False):
605 try: 606 event = self.dmd.ZenEventManager.getEventDetail( 607 evid, dedupid, better) 608 except ZenEventNotFound: 609 event = self.dmd.ZenEventHistory.getEventDetail(evid, dedupid, 610 better) 611 return event
612
613 - def getEventDetail(self, evid=None, dedupid=None, better=False):
614 """ 615 Return an EventDetail object for a particular event. 616 617 @param evid: Event ID 618 @type evid: string 619 @param dedupid: string used to determine duplicates 620 @type dedupid: string 621 @param better: provide even more detail than normal? 622 @type better: boolean 623 @return: fields from the event 624 @rtype: EventDetail object 625 """ 626 idfield = evid and "evid" or "dedupid" 627 if not evid: evid = dedupid 628 cachekey = '%s%s' % (idfield, evid) 629 event = self.checkCache(cachekey) 630 if event: return event 631 fields = self.getFieldList() 632 selectevent = "select " 633 selectevent += ", ".join(fields) 634 selectevent += " from %s where" % self.statusTable 635 selectevent += " %s = '%s'" % (idfield, evid) 636 conn = self.connect() 637 try: 638 curs = conn.cursor() 639 curs.execute(selectevent) 640 evrow = curs.fetchone() 641 if not evrow: 642 raise ZenEventNotFound( "Event id '%s' not found" % evid) 643 evdata = map(self.convert, fields, evrow) 644 if better: 645 event = BetterEventDetail(self, fields, evdata) 646 else: 647 event = EventDetail(self, fields, evdata) 648 649 selectdetail = "select name, value from %s where" % self.detailTable 650 selectdetail += " evid = '%s'" % event.evid 651 #print selectdetail 652 curs.execute(selectdetail) 653 event._details = curs.fetchall() 654 655 selectlogs = "select userName, ctime, text" 656 selectlogs += " from %s where" % self.logTable 657 selectlogs += " evid = '%s' order by ctime desc" % event.evid 658 #print selectlogs 659 curs.execute(selectlogs) 660 jrows = curs.fetchall() 661 logs = [] 662 for row in jrows: 663 user = self.cleanstring(row[0]) 664 date = self.dateString(row[1]) 665 text = row[2] 666 logs.append((user, date, text)) 667 event._logs = logs 668 finally: self.close(conn) 669 670 self.addToCache(cachekey, event) 671 self.cleanCache() 672 return event
673 674
675 - def getStatusME(self, me, statusclass=None, **kwargs):
676 """ 677 """ 678 from Products.ZenModel.Organizer import Organizer 679 if me.event_key == "Device": 680 return self.getDeviceStatus(me.getId(), statusclass, **kwargs) 681 elif me.event_key == "Component": 682 return self.getComponentStatus(me.getParentDeviceName(), 683 me.getId(), statusclass, **kwargs) 684 elif isinstance(me, Organizer): 685 return self.getOrganizerStatus(me, statusclass=statusclass, 686 **kwargs) 687 else: 688 return self.getGenericStatus(me)
689 690
691 - def getGenericStatus(self, me):
692 """Return status based on a where clause defined for the me event_type. 693 No fancy caching done this might be a little slow if there are a lot 694 of events. Where clause is evaled 695 """ 696 where = self.lookupManagedEntityWhere(me) 697 select = "select count(*) from %s where %s" % (self.statusTable, where) 698 statusCount = self.checkCache(select) 699 if not statusCount: 700 conn = self.connect() 701 try: 702 curs = conn.cursor() 703 #print select 704 curs.execute(select) 705 statusCount = curs.fetchone()[0] 706 finally: self.close(conn) 707 708 self.addToCache(select,statusCount) 709 return statusCount
710 711
712 - def getOrganizerStatus(self, org, statusclass=None, severity=None, 713 state=0, where=""):
714 """see IEventStatus 715 """ 716 orgfield = self.lookupManagedEntityField(org.event_key) 717 select = "select %s from %s where " % (orgfield, self.statusTable) 718 where = self._wand(where, "%s = '%s'", self.eventClassField,statusclass) 719 where = self._wand(where, "%s >= %s", self.severityField, severity) 720 where = self._wand(where, "%s <= %s", self.stateField, state) 721 select += where 722 #print select 723 statusCache = self.checkCache(select) 724 if not statusCache: 725 conn = self.connect() 726 try: 727 curs = conn.cursor() 728 curs.execute(select) 729 statusCache=[] 730 orgdict={} 731 for row in curs.fetchall(): 732 orgfield = self.cleanstring(row[0]) 733 if not orgfield: continue 734 if orgfield.startswith("|"): orgfield = orgfield[1:] 735 for orgname in orgfield.split("|"): 736 orgdict.setdefault(orgname, 0) 737 orgdict[orgname] += 1 738 statusCache = orgdict.items() 739 self.addToCache(select,statusCache) 740 finally: self.close(conn) 741 countevts = 0 742 for key, value in statusCache: 743 if key.startswith(org.getOrganizerName()): 744 countevts += value 745 return countevts
746 747
748 - def getOrganizerStatusIssues(self, event_key,severity=4,state=0, 749 where="", limit=0):
750 """Return list of tuples (org, count) for all organizers with events. 751 """ 752 orgfield = self.lookupManagedEntityField(event_key) 753 select = "select %s, count from %s where " % (orgfield,self.statusTable) 754 where = self._wand(where, "%s >= %s", self.severityField, severity) 755 where = self._wand(where, "%s <= %s", self.stateField, state) 756 where = self._wand(where,"%s like '%s'",self.eventClassField,"/Status%") 757 select += where 758 statusCache = self.checkCache(select) 759 if not statusCache: 760 conn = self.connect() 761 try: 762 curs = conn.cursor() 763 curs.execute(select) 764 statusCache=[] 765 orgdict={} 766 for row in curs.fetchall(): 767 orgfield = self.cleanstring(row[0]) 768 if not orgfield: continue 769 if orgfield.startswith("|"): orgfield = orgfield[1:] 770 for orgname in orgfield.split("|"): 771 if not orgname: continue 772 count, total = orgdict.setdefault(orgname, (0,0)) 773 count+=1 774 total+=row[1] 775 orgdict[orgname] = (count,total) 776 statusCache = [ [n, c[0], int(c[1])] for n, c in orgdict.items() ] 777 statusCache.sort(lambda x,y: cmp(x[1],y[1])) 778 statusCache.reverse() 779 if limit: 780 statusCache = statusCache[:limit] 781 self.addToCache(select,statusCache) 782 finally: self.close(conn) 783 return statusCache
784 785
786 - def getDevicePingIssues(self, state=2, limit=0):
787 """Return devices with ping problems. 788 """ 789 return self.getDeviceIssues(where="eventClass = '%s'" % Status_Ping, 790 severity=3, 791 state=state, 792 limit=limit)
793 794
795 - def getWmiConnIssues(self, state=2, limit=0):
796 """Return devices with WMI connection failures. 797 """ 798 where="severity>=3 and (eventClass = '%s' or eventClass = '%s')" % ( 799 Status_Wmi_Conn, Status_Ping) 800 return self.getDeviceIssues(where=where,state=state,limit=limit)
801 802
803 - def getDeviceStatusIssues(self, severity=4, state=1, limit=0):
804 """Return only status issues. 805 """ 806 return self.getDeviceIssues(where="eventClass like '/Status%'", 807 severity=severity, state=state, limit=limit)
808 809
810 - def getDeviceIssues(self,severity=1,state=0,where="",mincount=0,limit=0):
811 """Return list of tuples (device, count, total) of events for 812 all devices with events. 813 """ 814 where = self._wand(where, "%s >= %s", self.severityField, severity) 815 where = self._wand(where, "%s <= %s", self.stateField, state) 816 select = """select distinct device, count(device) as evcount, 817 sum(count) from status where %s group by device 818 having evcount > %s""" % (where, mincount) 819 statusCache = self.checkCache(select) 820 if not statusCache: 821 try: 822 conn = self.connect() 823 try: 824 curs = conn.cursor() 825 curs.execute(select) 826 statusCache = [ [d,int(c),int(s)] for d,c,s in curs.fetchall() ] 827 #statusCache = list(curs.fetchall()) 828 statusCache.sort(lambda x,y: cmp(x[1],y[1])) 829 statusCache.reverse() 830 if limit: 831 statusCache = statusCache[:limit] 832 finally: self.close(conn) 833 except: 834 log.exception(select) 835 raise 836 return statusCache
837 838
839 - def getDeviceStatus(self, device, statclass=None, countField=None, 840 severity=3, state=None, where=""):
841 """see IEventStatus 842 """ 843 if countField == None: countField = self.countField 844 select = "select %s, %s from %s where " % ( 845 self.deviceField, self.countField, self.statusTable) 846 where = self._wand(where, "%s = '%s'", self.eventClassField, statclass) 847 where = self._wand(where, "%s >= %s", self.severityField, severity) 848 where = self._wand(where, "%s <= %s", self.stateField, state) 849 select += where 850 #print select 851 statusCache = self.checkCache(select) 852 if not statusCache: 853 try: 854 conn = self.connect() 855 try: 856 curs = conn.cursor() 857 curs.execute(select) 858 statusCache = {} 859 for dev, count in curs.fetchall(): 860 dev = self.cleanstring(dev) 861 statusCache[dev] = count 862 self.addToCache(select,statusCache) 863 finally: self.close(conn) 864 except: 865 log.exception("status failed for device %s", device) 866 return -1 867 return statusCache.get(device, 0)
868 869
870 - def defaultAvailabilityStart(self):
871 return Time.USDate(time.time() - 60*60*24*self.defaultAvailabilityDays)
872 873
874 - def defaultAvailabilityEnd(self):
875 return Time.USDate(time.time())
876 877
878 - def getAvailability(self, state, **kw):
879 import Availability 880 for name in "device", "component", "eventClass", "systems", "severity": 881 if hasattr(state, name): 882 kw.setdefault(name, getattr(state, name)) 883 for name in "startDate", "endDate": 884 if hasattr(state, name): 885 kw.setdefault(name, Time.ParseUSDate(getattr(state, name))) 886 kw.setdefault('startDate', 887 time.time() - 60*60*24*self.defaultAvailabilityDays) 888 return Availability.query(self.dmd, **kw)
889 890
891 - def getHeartbeat(self, failures=True, simple=False, limit=0, db=None):
892 """Return all heartbeat issues list of tuples (device, component, secs) 893 """ 894 sel = """select device, component, lastTime from heartbeat """ 895 if failures: 896 sel += "where DATE_ADD(lastTime, INTERVAL timeout SECOND) <= NOW();" 897 898 statusCache = self.checkCache(sel) 899 cleanup = lambda : None 900 if not statusCache: 901 statusCache = [] 902 conn = self.connect() 903 try: 904 curs = conn.cursor() 905 curs.execute(sel) 906 res = list(curs.fetchall()) 907 res.sort(lambda x,y: cmp(x[2],y[2])) 908 devclass = self.getDmdRoot("Devices") 909 for devname, comp, dtime in res: 910 dtime = "%d" % int(time.time()-dtime.timeTime()) 911 dev = devclass.findDevice(devname) 912 if dev and not simple: 913 alink = "<a href='%s'>%s</a>" % ( 914 dev.getPrimaryUrlPath(), dev.id) 915 else: alink = devname 916 statusCache.append([alink, comp, dtime, devname]) 917 if limit: 918 statusCache = statusCache[:limit] 919 cleanup() 920 finally: self.close(conn) 921 return statusCache
922 923
924 - def getHeartbeatObjects(self, failures=True, simple=False, limit=0, db=None):
925 beats = self.getHeartbeat(failures, simple, limit, db) 926 return [{'alink':b[0], 'comp':b[1], 'dtime':b[2], 'devId':b[3]} 927 for b in beats]
928 929
930 - def getAllComponentStatus(self, 931 statclass, 932 countField=None, 933 severity=3, 934 state=1, 935 where=""):
936 "Fetch the counts on all components matching statClass" 937 if countField == None: countField = self.countField 938 select = "select %s, %s, %s from %s where "\ 939 % (self.deviceField, self.componentField, countField, 940 self.statusTable) 941 where = self._wand(where, "%s = '%s'", self.eventClassField, statclass) 942 where = self._wand(where, "%s >= %s", self.severityField, severity) 943 where = self._wand(where, "%s <= %s", self.stateField, state) 944 select += where 945 conn = self.connect() 946 try: 947 curs = conn.cursor() 948 curs.execute(select) 949 result = {} 950 for dev, comp, count in curs.fetchall(): 951 dev = self.cleanstring(dev) 952 comp = self.cleanstring(comp) 953 result[dev,comp] = count 954 return result 955 finally: 956 self.close(conn)
957 958
959 - def getMaxSeverity(self, me):
960 """ Returns the severity of the most severe event. """ 961 where = self.lookupManagedEntityWhere(me.device()) 962 if me.event_key == 'Component': 963 where = self._wand(where, "%s = '%s'", 964 self.componentField, me.id) 965 select = "select max(%s) from %s where " % (self.severityField, 966 self.statusTable) 967 query = select + where 968 conn = self.connect() 969 try: 970 curs = conn.cursor() 971 curs.execute(query) 972 severity = curs.fetchall()[0][0] 973 finally: self.close(conn) 974 return max(severity, 0)
975 976
977 - def getComponentStatus(self, device, component, statclass=None, 978 countField=None, severity=3, state=1, where=""):
979 """see IEventStatus 980 """ 981 if countField == None: countField = self.countField 982 select = "select %s, %s, %s from %s where "\ 983 % (self.deviceField, self.componentField, countField, 984 self.statusTable) 985 where = self._wand(where, "%s = '%s'", self.eventClassField, statclass) 986 where = self._wand(where, "%s >= %s", self.severityField, severity) 987 where = self._wand(where, "%s <= %s", self.stateField, state) 988 select += where 989 statusCache = self.checkCache(select) 990 if not statusCache: 991 conn = self.connect() 992 try: 993 curs = conn.cursor() 994 curs.execute(select) 995 statusCache ={} 996 for dev, comp, count in curs.fetchall(): 997 dev = self.cleanstring(dev) 998 comp = self.cleanstring(comp) 999 statusCache[dev+comp] = count 1000 self.addToCache(select,statusCache) 1001 finally: self.close(conn) 1002 return statusCache.get(device+component, 0)
1003
1004 - def getBatchComponentInfo(self, components):
1005 """ 1006 Given a list of dicts with keys 'device', 'component', make a query to 1007 get an overall severity and device status for the group. 1008 """ 1009 severity, state = 3, 1 1010 components = list(components) # Sometimes a generator. We use it twice. 1011 1012 def doQuery(query): 1013 conn = self.connect() 1014 data = None 1015 try: 1016 curs = conn.cursor() 1017 curs.execute(query) 1018 data = curs.fetchall() 1019 finally: self.close(conn) 1020 return data
1021 1022 select = "select MAX(%s) from %s where " % (self.severityField, 1023 self.statusTable) 1024 where = self._wand("", "%s >= %s", self.severityField, severity) 1025 where = self._wand(where, "%s <= %s", self.stateField, state) 1026 def componentWhere(device, component): 1027 return "device = '%s' and component= '%s'" % (device, component)
1028 cwheres = ' and ' + ' or '.join(['(%s)'% componentWhere(**comp) 1029 for comp in components]) 1030 sevquery = select + where + cwheres 1031 1032 select = "select MAX(%s) from %s where " % (self.countField, 1033 self.statusTable) 1034 where = self._wand("", "%s >= %s", self.severityField, severity) 1035 where = self._wand(where, "%s <= %s", self.stateField, state) 1036 where = self._wand(where, "%s = '%s'", self.eventClassField, 1037 '/Status/Ping') 1038 devwhere = lambda d:"device = '%s'" % d 1039 dwheres = ' and ' + '(%s)' % (' or '.join( 1040 map(devwhere, [x['device'] for x in components]))) 1041 statquery = select + where + dwheres 1042 1043 maxseverity = doQuery(sevquery)[0][0] 1044 maxstatus = doQuery(statquery)[0][0] 1045 return maxseverity, maxstatus 1046
1047 - def getEventOwnerListME(self, me, severity=0, state=1):
1048 """Return list of event owners based on passed in managed entity. 1049 """ 1050 try: 1051 where = self.lookupManagedEntityWhere(me) 1052 return self.getEventOwnerList(where, severity, state) 1053 except: 1054 log.exception("event summary for %s failed" % me.getDmdKey()) 1055 raise
1056 1057
1058 - def getEventOwnerList(self, where="", severity=0, state=1):
1059 """Return a list of userids that correspond to the events in where. 1060 select distinct ownerid from status where 1061 device="win2k.confmon.loc" and eventState > 2 1062 """ 1063 select ="select distinct ownerid from status where " 1064 where = self._wand(where, "%s >= %s", self.severityField, severity) 1065 where = self._wand(where, "%s <= %s", self.stateField, state) 1066 select += where 1067 #print select 1068 statusCache = self.checkCache(select) 1069 if statusCache: return statusCache 1070 conn = self.connect() 1071 try: 1072 curs = conn.cursor() 1073 curs.execute(select) 1074 statusCache = [ uid[0] for uid in curs.fetchall() if uid[0] ] 1075 self.addToCache(select,statusCache) 1076 finally: self.close(conn) 1077 return statusCache
1078 1079
1080 - def lookupManagedEntityWhere(self, me):
1081 """ 1082 Lookup and build where clause for managed entity. 1083 1084 @param me: managed entity 1085 @type me: object 1086 @return: where clause 1087 @rtype: string 1088 """ 1089 key = me.event_key + "Where" 1090 wheretmpl = getattr(aq_base(self), key, False) 1091 if not wheretmpl: 1092 raise ValueError("No 'where' clause found for event_key %s" % me.event_key) 1093 return eval(wheretmpl,{'me':me})
1094 1095
1096 - def lookupManagedEntityField(self, event_key):
1097 """ 1098 Lookup database field for managed entity default 1099 using event_key. 1100 1101 @param event_key: event 1102 @type event_key: string 1103 @return: field for the managed entity 1104 @rtype: object 1105 """ 1106 key = event_key + "Field" 1107 return getattr(aq_base(self), key, event_key)
1108 1109
1110 - def lookupManagedEntityResultFields(self, event_key):
1111 """ 1112 Gets the column names that should be requested in an event query for 1113 this entity type. 1114 1115 Returns a set of result fields predefined for this entity type. If 1116 none have been defined, returns the default result fields. 1117 1118 >>> f = dmd.ZenEventManager.lookupManagedEntityResultFields('Device') 1119 >>> f==dmd.ZenEventManager.DeviceResultFields 1120 True 1121 >>> f = dmd.ZenEventManager.lookupManagedEntityResultFields('Robot') 1122 >>> f==dmd.ZenEventManager.defaultResultFields 1123 True 1124 1125 @param event_key: The event key of a managed entity. 1126 @type event_key: string 1127 @return: A tuple of strings representing columns in the database. 1128 """ 1129 key = event_key + "ResultFields" 1130 return getattr(aq_base(self), key, self.defaultResultFields)
1131 1132
1133 - def _wand(self, where, fmt, field, value):
1134 """ 1135 >>> dmd.ZenEventManager._wand('where 1=1', '%s=%s', 'a', 'b') 1136 'where 1=1 and a=b' 1137 >>> dmd.ZenEventManager._wand('where a=5', '%s=%s', 'a', 'b') 1138 'where a=5' 1139 >>> dmd.ZenEventManager._wand('where b=a', '%s=%s', 'a', 'b') 1140 'where b=a' 1141 """ 1142 if value != None and where.find(field) == -1: 1143 if where: where += " and " 1144 where += fmt % (field, value) 1145 return where
1146
1147 - def _setupDateRange(self, startdate=None, 1148 enddate=None):
1149 """ 1150 Make a start and end date range that is at least one day long. 1151 returns a start and end date as a proper database element. 1152 """ 1153 if enddate is None: 1154 enddate = DateTime.DateTime()-1 1155 if startdate is None: 1156 startdate = DateTime.DateTime() 1157 if type(enddate) == types.StringType: 1158 enddate = DateTime.DateTime(enddate, datefmt='us') 1159 enddate = enddate.latestTime() 1160 if type(startdate) == types.StringType: 1161 startdate = DateTime.DateTime(startdate, datefmt='us') 1162 startdate = startdate.earliestTime() 1163 startdate = self.dateDB(startdate) 1164 enddate = self.dateDB(enddate) 1165 return startdate, enddate
1166
1167 - def getAvgFieldLength(self, fieldname):
1168 conn = self.connect() 1169 try: 1170 curs = conn.cursor() 1171 selstatement = ("SELECT AVG(CHAR_LENGTH(mycol))+20 FROM (SELECT %s AS " 1172 "mycol FROM %s WHERE %s IS NOT NULL LIMIT 500) AS " 1173 "a;") % (fieldname, self.statusTable, fieldname) 1174 curs.execute(selstatement) 1175 avglen = curs.fetchone() 1176 finally: self.close(conn) 1177 try: return float(avglen[0]) 1178 except TypeError: return 10.
1179 1180 1181 #========================================================================== 1182 # Event sending functions 1183 #========================================================================== 1184 1185 security.declareProtected(ZEN_SEND_EVENTS, 'sendEvents')
1186 - def sendEvents(self, events):
1187 """Send a group of events to the backend. 1188 """ 1189 count = 0 1190 for event in events: 1191 try: 1192 self.sendEvent(event) 1193 count += 1 1194 except Exception, ex: 1195 log.exception(ex) 1196 return count
1197 1198 1199 security.declareProtected(ZEN_SEND_EVENTS, 'sendEvent')
1200 - def sendEvent(self, event):
1201 """ 1202 Send an event to the backend. 1203 1204 @param event: event 1205 @type event: event object 1206 @todo: implement 1207 """ 1208 raise NotImplementedError
1209 1210 1211 #========================================================================== 1212 # Schema management functions 1213 #========================================================================== 1214
1215 - def convert(self, field, value):
1216 """Perform convertion of value coming from database value if nessesary. 1217 """ 1218 value = self.cleanstring(value) 1219 #FIXME this is commented out because we often need severity as a 1220 # number (like in ZEvent.getCssClass) and sorting. Need to have 1221 # both fields at some point 1222 #if field == self.severityField: 1223 # idx = len(self.severityConversions) - value 1224 # value = self.severityConversions[idx][0] 1225 #elif field == 'prodState': 1226 # value = self.dmd.convertProdState(value) 1227 if self.isDate(field): 1228 value = self.dateString(value) 1229 return value
1230 1231 1232 security.declareProtected(ZEN_VIEW, "getFieldList")
1233 - def getFieldList(self):
1234 """Return a list of all fields in the status table of the backend. 1235 """ 1236 if not getattr(self, '_fieldlist', None): 1237 self.loadSchema() 1238 return self._fieldlist
1239
1240 - def getEventStates(self):
1241 """Return a list of possible event states. 1242 """ 1243 return self.eventStateConversions
1244
1245 - def getEventActions(self):
1246 """Return a list of possible event actions. 1247 """ 1248 return self.eventActions
1249 1250 security.declareProtected(ZEN_COMMON,'getSeverities')
1251 - def getSeverities(self):
1252 """Return a list of tuples of severities [('Warning', 3), ...] 1253 """ 1254 return self.severityConversions
1255
1256 - def getSeverityString(self, severity):
1257 """Return a string representation of the severity. 1258 """ 1259 try: 1260 return self.severities[severity] 1261 except KeyError: 1262 return "Unknown"
1263
1264 - def getPriorities(self):
1265 """Return a list of tuples of priorities [('Warning', 3), ...] 1266 """ 1267 return self.priorityConversions
1268
1269 - def getPriorityString(self, priority):
1270 """Return the priority name 1271 """ 1272 try: 1273 return self.priorities[priority] 1274 except IndexError: 1275 return "Unknown"
1276
1277 - def convertEventField(self, field, value, default=""):
1278 """ 1279 Convert numeric values commonly found in events to their textual 1280 representation. 1281 """ 1282 if value is None: return default 1283 try: 1284 value = int(value) 1285 except ValueError: 1286 return "unknown (%r)" % (value,) 1287 1288 if field == 'severity' and self.severities.has_key(value): 1289 return "%s (%d)" % (self.severities[value], value) 1290 elif field == "priority" and self.priorities.has_key(value): 1291 return "%s (%d)" % (self.priorities[value], value) 1292 elif field == 'eventState': 1293 if value < len(self.eventStateConversions): 1294 return "%s (%d)" % (self.eventStateConversions[value][0], value) 1295 elif field == "prodState": 1296 prodState = self.dmd.convertProdState(value) 1297 if isinstance(prodState, types.StringType): 1298 return "%s (%d)" % (prodState, value) 1299 elif field == "DevicePriority": 1300 priority = self.dmd.convertPriority(value) 1301 if isinstance(priority, types.StringType): 1302 return "%s (%d)" % (priority, value) 1303 1304 return "unknown (%r)" % (value,)
1305 1306
1307 - def getStatusCssClass(self, status):
1308 if status < 0: status = "unknown" 1309 elif status > 3: status = 3 1310 return "zenstatus_%s" % status
1311 1312
1313 - def getStatusImgSrc(self, status):
1314 ''' Return the img source for a status number 1315 ''' 1316 if status < 0: 1317 src = 'grey' 1318 elif status == 0: 1319 src = 'green' 1320 else: 1321 src = 'red' 1322 return '/zport/dmd/img/%s_dot.png' % src
1323 1324
1325 - def getEventCssClass(self, severity, acked=False):
1326 """return the css class name to be used for this event. 1327 """ 1328 __pychecker__='no-constCond' 1329 value = severity < 0 and "unknown" or severity 1330 acked = acked and "acked" or "noack" 1331 return "zenevents_%s_%s %s" % (value, acked, acked)
1332 1333
1334 - def isDate(self, colName):
1335 """Check to see if a column is of type date. 1336 """ 1337 if not self._schema: 1338 self.getFieldList() 1339 return self._schema.get(colName, False)
1340 1341
1342 - def dateString(self, value):
1343 """Convert a date from database format to string. 1344 """ 1345 if isinstance(value, DateTime.DateTime): 1346 value = value.timeTime() 1347 return Time.LocalDateTime(value)
1348 1349 1350
1351 - def dateDB(self, value):
1352 """Convert a date to its database format. 1353 """ 1354 if isinstance(value, DateTime.DateTime): 1355 return "%.3f" % value.timeTime() 1356 elif type(value) == types.StringTypes: 1357 return "%.3f" % DateTime.DateTime(value).timeTime() 1358 return value
1359 1360
1361 - def escape(self, value):
1362 """ 1363 Prepare string values for db by escaping special characters. 1364 1365 @param value: string 1366 @type value: string 1367 @todo: implement 1368 """ 1369 raise NotImplementedError
1370 1371
1372 - def loadSchema(self):
1373 """Load schema from database. If field is a date set value to true.""" 1374 schema = {} 1375 fieldlist = [] 1376 sql = "describe %s;" % self.statusTable 1377 conn = self.connect() 1378 try: 1379 curs = conn.cursor() 1380 curs.execute(sql) 1381 for row in curs.fetchall(): 1382 fieldlist.append(row[0]) 1383 col = self.cleanstring(row[0]) 1384 if self.backend == "mysql": 1385 type = row[1] in ("datetime", "timestamp", "double") 1386 schema[col] = type 1387 if schema: self._schema = schema 1388 self._fieldlist = fieldlist 1389 finally: self.close(conn)
1390 1391
1392 - def eventControls(self):
1393 """Are there event controls on this event list. 1394 """ 1395 if self.isManager() and self.statusTable in ["status","history"]: 1396 return 1 1397 return 0
1398
1399 - def updateEvents(self, stmt, whereClause, reason, 1400 table="status", toLog=True):
1401 userId = getSecurityManager().getUser().getId() 1402 insert = 'INSERT INTO log (evid, userName, text) ' + \ 1403 'SELECT evid, "%s", "%s" ' % (userId, reason) + \ 1404 'FROM %s ' % table + whereClause 1405 query = stmt + ' ' + whereClause 1406 conn = self.connect() 1407 try: 1408 curs = conn.cursor() 1409 if toLog: curs.execute(insert) 1410 curs.execute(query) 1411 finally: self.close(conn) 1412 self.clearCache() 1413 self.manage_clearCache()
1414 1415 security.declareProtected(ZEN_MANAGE_EVENTS,'manage_addEvent')
1416 - def manage_addEvent(self, REQUEST=None):
1417 ''' Create an event from user supplied data 1418 ''' 1419 eventDict = dict( 1420 summary = REQUEST.get('summary', ''), 1421 message = REQUEST.get('message', ''), 1422 device = REQUEST.get('device', ''), 1423 component = REQUEST.get('component', ''), 1424 severity = REQUEST.get('severity', ''), 1425 eventClassKey = REQUEST.get('eventClassKey', ''), 1426 ) 1427 # We don't want to put empty eventClass into the dict because that 1428 # can keep the event from being mapped to /Unknown correctly. 1429 if REQUEST.get('eclass', None): 1430 eventDict['eventClass'] = REQUEST['eclass'] 1431 # sendEvent insists on a device or a component. Can be bogus. 1432 if not eventDict['device'] and not eventDict['component']: 1433 if REQUEST: 1434 messaging.IMessageSender(self).sendToBrowser( 1435 'Invalid Event', 1436 'You must specify a device and/or a component.', 1437 priority=messaging.WARNING 1438 ) 1439 return self.callZenScreen(REQUEST) 1440 else: 1441 return 1442 self.sendEvent(eventDict) 1443 if REQUEST: 1444 REQUEST['RESPONSE'].redirect('/zport/dmd/Events/viewEvents')
1445 1446
1447 - def deleteEvents(self, whereClause, reason):
1448 self.updateEvents('DELETE FROM status', whereClause, reason)
1449 1450 1451 security.declareProtected(ZEN_MANAGE_EVENTS,'manage_deleteEvents')
1452 - def manage_deleteEvents(self, evids=(), REQUEST=None):
1453 "Delete the given event ids" 1454 if type(evids) == type(''): 1455 evids = [evids] 1456 num = len(evids) 1457 if evids: 1458 evids = ",".join([ "'%s'" % evid for evid in evids]) 1459 whereClause = ' where evid in (%s)' % evids 1460 self.deleteEvents(whereClause, 'Deleted by user') 1461 if REQUEST: 1462 messaging.IMessageSender(self).sendToBrowser( 1463 'Moved To History', 1464 '%s event%s have been moved to history.' % ( 1465 num, (num != 1 and 's') or '') 1466 ) 1467 return self.callZenScreen(REQUEST)
1468
1469 - def undeleteEvents(self, whereClause, reason):
1470 fields = ','.join( self.getFieldList() ) 1471 # We want to blank clearid 1472 fields = fields.replace('clearid','NULL') 1473 self.updateEvents( 'INSERT status ' + \ 1474 'SELECT %s FROM history' % fields, 1475 whereClause + \ 1476 ' ON DUPLICATE KEY UPDATE status.count=status.count+history.count', 1477 reason, 'history', toLog=False) 1478 self.updateEvents( 'DELETE FROM history', whereClause, \ 1479 reason, 'history')
1480 1481 security.declareProtected(ZEN_MANAGE_EVENTS,'manage_undeleteEvents')
1482 - def manage_undeleteEvents(self, evids=(), REQUEST=None):
1483 "Move the given event ids into status and delete from history" 1484 if type(evids) == type(''): 1485 evids = [evids] 1486 num = len(evids) 1487 if evids: 1488 evids = ",".join([ "'%s'" % evid for evid in evids]) 1489 whereClause = ' where evid in (%s)' % evids 1490 self.undeleteEvents(whereClause, 'Undeleted by user') 1491 if REQUEST: 1492 messaging.IMessageSender(self).sendToBrowser( 1493 'Undeleted', 1494 '%s events have been moved out of history.' % num 1495 ) 1496 return self.callZenScreen(REQUEST)
1497 1498 1499 security.declareProtected(ZEN_MANAGE_EVENTS,'manage_deleteAllEvents')
1500 - def manage_deleteAllEvents(self, devname, REQUEST=None):
1501 "Delete the events for a given Device (used for deleting the device" 1502 whereClause = 'where device = "%s"' % devname 1503 self.deleteEvents(whereClause, 'Device deleted') 1504 if REQUEST: 1505 messaging.IMessageSender(self).sendToBrowser( 1506 'Events Deleted', 1507 'Deleted all events for %s' % devname 1508 ) 1509 return self.callZenScreen(REQUEST)
1510 1511 1512 security.declareProtected(ZEN_MANAGE_EVENTS,'manage_deleteHistoricalEvents')
1513 - def manage_deleteHistoricalEvents(self, devname=None, agedDays=None, 1514 REQUEST=None):
1515 """ 1516 Delete historical events. If devices is given then only delete 1517 events for that device. If agedDays is given then only delete 1518 events that are older than that many days. 1519 devname and agedDays are mutually exclusive. No real reason for this 1520 other than there is no current need to use both in same call and I 1521 don't want to test the combination. 1522 This is an option during device deletion. It is also used 1523 by zenactions to keep history table clean. 1524 1525 NB: Device.deleteDevice() is not currently calling this when devices 1526 are deleted. See ticket #2996. 1527 """ 1528 import subprocess 1529 import os 1530 import Products.ZenUtils.Utils as Utils 1531 1532 cmd = Utils.zenPath('Products', 'ZenUtils', 'ZenDeleteHistory.py') 1533 if devname: 1534 args = ['--device=%s' % devname] 1535 elif agedDays: 1536 args = ['--numDays=%s' % agedDays] 1537 else: 1538 return 1539 proc = subprocess.Popen( 1540 [cmd]+args, stdout=subprocess.PIPE, 1541 stderr=subprocess.STDOUT, env=os.environ) 1542 # We are abandoning this proc to do it's thing. or not. We don't 1543 # want to block because we would delay user feedback on a device 1544 # delete when this might take a while to perform. 1545 unused(proc) 1546 if REQUEST: 1547 messaging.IMessageSender(self).sendToBrowser( 1548 'Events Deleted', 1549 'Historical events have been deleted.' 1550 ) 1551 return self.callZenScreen(REQUEST)
1552 1553 1554 security.declareProtected(ZEN_MANAGE_EVENTS,'manage_deleteHeartbeat')
1555 - def manage_deleteHeartbeat(self, devname, REQUEST=None):
1556 if devname: 1557 delete = "delete from heartbeat where device = '%s'" % devname 1558 conn = self.connect() 1559 try: 1560 curs = conn.cursor() 1561 curs.execute(delete); 1562 finally: self.close(conn) 1563 if REQUEST: 1564 messaging.IMessageSender(self).sendToBrowser( 1565 'Heartbeats Cleared', 1566 'Heartbeat events have been moved to the history.' 1567 ) 1568 return self.callZenScreen(REQUEST)
1569 1570 1571 security.declareProtected(ZEN_MANAGE_EVENTS,'manage_ackEvents')
1572 - def manage_ackEvents(self, evids=(), REQUEST=None):
1573 "Ack the given event ids" 1574 if type(evids) == type(''): 1575 evids = [evids] 1576 request = FakeRequest() 1577 self.manage_setEventStates(1 , evids, REQUEST=request) 1578 if REQUEST: 1579 dest = '/zport/dmd/Events/viewEvents' 1580 if request.get('message', ''): 1581 dest += '?message=%s' % request['message'] 1582 if not getattr(REQUEST, 'dontRedirect', False): 1583 REQUEST['RESPONSE'].redirect(dest)
1584 1585 1586 security.declareProtected(ZEN_MANAGE_EVENTS,'manage_setEventStates')
1587 - def manage_setEventStates(self, eventState=None, evids=(), 1588 userid="", REQUEST=None):
1589 reason = None 1590 if eventState and evids: 1591 eventState = int(eventState) 1592 if eventState > 0 and not userid: 1593 userid = getSecurityManager().getUser() 1594 update = "update status set eventState=%s, ownerid='%s' " % ( 1595 eventState, userid) 1596 whereClause = "where evid in (" 1597 whereClause += ",".join([ "'%s'" % evid for evid in evids]) + ")" 1598 reason = 'Event state changed to ' 1599 try: 1600 reason += self.eventStateConversions[eventState][0] 1601 except KeyError: 1602 reason += 'unknown (%d)' % eventState 1603 self.updateEvents(update, whereClause, reason) 1604 if REQUEST: 1605 if not reason: reason = '' 1606 messaging.IMessageSender(self).sendToBrowser( 1607 'Event States Set', 1608 reason 1609 ) 1610 return self.callZenScreen(REQUEST)
1611 1612 1613 security.declareProtected(ZEN_MANAGE_EVENTS,'manage_setEventStates')
1614 - def manage_createEventMap(self, eventClass=None, evids=(), 1615 REQUEST=None):
1616 """Create an event map from an event or list of events. 1617 """ 1618 evclass = None 1619 evmap = None 1620 numCreated = 0 1621 numNotUnknown = 0 1622 numNoKey = 0 1623 if eventClass and evids: 1624 evclass = self.getDmdRoot("Events").getOrganizer(eventClass) 1625 sel = """select eventClassKey, eventClass, message 1626 from %s where evid in ('%s')""" 1627 sel = sel % (self.statusTable, "','".join(evids)) 1628 conn = self.connect() 1629 try: 1630 curs = conn.cursor() 1631 curs.execute(sel); 1632 for row in curs.fetchall(): 1633 evclasskey, curevclass, msg = row 1634 if curevclass != Unknown: 1635 numNotUnknown += 1 1636 continue 1637 if not evclasskey: 1638 numNoKey += 1 1639 continue 1640 evmap = evclass.createInstance(evclasskey) 1641 evmap.eventClassKey = evclasskey 1642 evmap.example = msg 1643 numCreated += 1 1644 evmap.index_object() 1645 finally: self.close(conn) 1646 elif REQUEST: 1647 if not evids: 1648 messaging.IMessageSender(self).sendToBrowser( 1649 'Error', 1650 'No events selected', 1651 priority=messaging.WARNING 1652 ) 1653 elif not eventClass: 1654 messaging.IMessageSender(self).sendToBrowser( 1655 'Error', 1656 'No event class selected', 1657 priority=messaging.WARNING 1658 ) 1659 1660 if REQUEST: 1661 msg = REQUEST.get('message', '') 1662 if numNotUnknown: 1663 msg += ((msg and ' ') + 1664 '%s event%s %s not class /Unknown.' % ( 1665 numNotUnknown, 1666 (numNotUnknown != 1 and 's') or '', 1667 (numNotUnknown != 1 and 'are') or 'is')) 1668 if numNoKey: 1669 msg += ((msg and ' ') + 1670 '%s event%s %s not have an event class key.' % ( 1671 numNoKey, 1672 (numNoKey != 1 and 's') or '', 1673 (numNoKey != 1 and 'do') or 'does')) 1674 msg += (msg and ' ') + 'Created %s event mapping%s.' % ( 1675 numCreated, 1676 (numCreated != 1 and 's') or '') 1677 1678 messaging.IMessageSender(self).sendToBrowser('Event Map', msg) 1679 # EventView might pass a fake Request during an ajax call from 1680 # event console. Don't bother rendering anything in this case. 1681 if getattr(REQUEST, 'dontRender', False): 1682 return '' 1683 if len(evids) == 1 and evmap: 1684 REQUEST['RESPONSE'].redirect(evmap.absolute_url()) 1685 elif evclass and evmap: 1686 REQUEST['RESPONSE'].redirect(evclass.absolute_url())
1687 1688 1689 security.declareProtected(ZEN_MANAGE_EVENTMANAGER,'manage_refreshConversions')
1690 - def manage_refreshConversions(self, REQUEST=None):
1691 """get the conversion information from the database server""" 1692 assert(self == self.dmd.ZenEventManager) 1693 self.loadSchema() 1694 self.dmd.ZenEventHistory.loadSchema() 1695 if REQUEST: 1696 messaging.IMessageSender(self).sendToBrowser( 1697 'Event Schema', 'Event schema has been refreshed.') 1698 return self.callZenScreen(REQUEST)
1699 1700 1701 security.declareProtected(ZEN_MANAGE_EVENTMANAGER,'manage_editCache')
1702 - def manage_editCache(self, timeout=5, clearthresh=20, REQUEST=None):
1703 """Reset cache values""" 1704 self.timeout = int(timeout) 1705 self.clearthresh = int(clearthresh) 1706 if REQUEST: 1707 message = "Cache parameters set" 1708 return self.editCache(self, REQUEST, manage_tabs_message=message)
1709 1710 1711 security.declareProtected(ZEN_MANAGE_EVENTMANAGER,'manage_clearCache')
1712 - def manage_clearCache(self, REQUEST=None):
1713 """Reset cache values""" 1714 assert(self == self.dmd.ZenEventManager) 1715 self.cleanCache(force=1) 1716 self.dmd.ZenEventHistory.cleanCache(force=1) 1717 if REQUEST: 1718 messaging.IMessageSender(self).sendToBrowser( 1719 'Event Cache', 'Event cache has been cleared.') 1720 return self.callZenScreen(REQUEST)
1721 1722 1723 security.declareProtected(ZEN_MANAGE_EVENTMANAGER,'manage_editEventManager')
1724 - def manage_editEventManager(self, REQUEST=None):
1725 ''' Call zmanage_editProperties then take care of saving a few 1726 values to ZenEventHistory 1727 ''' 1728 assert(self == self.dmd.ZenEventManager) 1729 self.zmanage_editProperties(REQUEST) 1730 self.dmd.ZenEventHistory.timeout = REQUEST['history_timeout'] 1731 self.dmd.ZenEventHistory.clearthresh = REQUEST['history_clearthresh'] 1732 self.dmd.ZenEventHistory.username = self.dmd.ZenEventManager.username 1733 self.dmd.ZenEventHistory.password = self.dmd.ZenEventManager.password 1734 self.dmd.ZenEventHistory.database = self.dmd.ZenEventManager.database 1735 self.dmd.ZenEventHistory.host = self.dmd.ZenEventManager.host 1736 self.dmd.ZenEventHistory.port = self.dmd.ZenEventManager.port 1737 if REQUEST: return self.callZenScreen(REQUEST)
1738 1739 1740 security.declareProtected(ZEN_MANAGE_EVENTMANAGER,'manage_clearHeartbeats')
1741 - def manage_clearHeartbeats(self, REQUEST=None):
1742 """truncate heartbeat table""" 1743 conn = self.connect() 1744 try: 1745 curs = conn.cursor() 1746 sql = 'truncate table heartbeat' 1747 curs.execute(sql) 1748 finally: self.close(conn) 1749 if REQUEST: 1750 messaging.IMessageSender(self).sendToBrowser( 1751 'Heartbeats Cleared', 'Heartbeats have been cleared.') 1752 return self.callZenScreen(REQUEST)
1753 1754 security.declareProtected(ZEN_MANAGE_EVENTMANAGER,'zmanage_editProperties')
1755 - def zmanage_editProperties(self, REQUEST=None):
1756 ''' Need to handle editing of history event fields differently 1757 ''' 1758 assert(self == self.dmd.ZenEventManager) 1759 screenName = REQUEST.get('zenScreenName', '') 1760 if screenName == 'editEventManagerHistoryFields': 1761 obj = self.dmd.ZenEventHistory 1762 else: 1763 obj = self 1764 if screenName == 'editEventManager': 1765 # We renamed the password field to try to keep browsers from 1766 # asking user if they wanted to save the password. 1767 if REQUEST.has_key('mysql_pass'): 1768 REQUEST.form['password'] = REQUEST['mysql_pass'] 1769 editProperties = ZenModelRM.zmanage_editProperties 1770 # suppress 'self is not first method argument' from pychecker 1771 editProperties(obj, REQUEST) 1772 if REQUEST: return self.callZenScreen(REQUEST)
1773 1774 security.declareProtected(ZEN_MANAGE_EVENTS, 'manage_addLogMessage')
1775 - def manage_addLogMessage(self, evid=None, message='', REQUEST=None):
1776 """ 1777 Add a log message to an event 1778 """ 1779 if not evid: 1780 return 1781 userId = getSecurityManager().getUser().getId() 1782 conn = self.connect() 1783 try: 1784 curs = conn.cursor() 1785 insert = 'INSERT INTO log (evid, userName, text) ' 1786 insert += 'VALUES ("%s", "%s", "%s")' % (evid, 1787 userId, 1788 conn.escape_string(message)) 1789 curs.execute(insert) 1790 finally: self.close(conn) 1791 self.clearCache('evid' + evid) 1792 self.dmd.ZenEventHistory.clearCache('evid' + evid) 1793 if REQUEST: return self.callZenScreen(REQUEST)
1794 1795 1796 security.declareProtected(ZEN_MANAGE_EVENTMANAGER, 'manage_addCommand')
1797 - def manage_addCommand(self, id, REQUEST=None):
1798 """ 1799 Add a new EventCommand 1800 1801 @param id: new command name 1802 @type id: string 1803 @param REQUEST: Zope REQUEST object 1804 @type REQUEST: Zope REQUEST object 1805 """ 1806 if id is None: 1807 if REQUEST: 1808 return self.callZenScreen(REQUEST) 1809 return 1810 1811 if not isinstance(id, unicode): 1812 id = self.prepId(id) 1813 1814 id = id.strip() 1815 if id == '': 1816 if REQUEST: 1817 return self.callZenScreen(REQUEST) 1818 return 1819 1820 ec = EventCommand(id) 1821 self.commands._setObject(id, ec) 1822 if REQUEST: 1823 return self.callZenScreen(REQUEST)
1824 1825 1826 security.declareProtected(ZEN_MANAGE_EVENTMANAGER, 'manage_deleteCommands')
1827 - def manage_deleteCommands(self, ids=[], REQUEST=None):
1828 """ 1829 Delete an EventCommand 1830 1831 @param ids: commands to delete 1832 @type ids: list of strings 1833 @param REQUEST: Zope REQUEST object 1834 @type REQUEST: Zope REQUEST object 1835 """ 1836 for id in ids: 1837 self.commands._delObject(id) 1838 if REQUEST: return self.callZenScreen(REQUEST)
1839 1840 1841 #========================================================================== 1842 # Utility functions 1843 #========================================================================== 1844
1845 - def installIntoPortal(self):
1846 """Install skins into portal. 1847 """ 1848 from Products.CMFCore.utils import getToolByName 1849 from Products.CMFCore.DirectoryView import addDirectoryViews 1850 from cStringIO import StringIO 1851 import string 1852 1853 out = StringIO() 1854 skinstool = getToolByName(self, 'portal_skins') 1855 if 'zenevents' not in skinstool.objectIds(): 1856 addDirectoryViews(skinstool, 'skins', globals()) 1857 out.write("Added 'zenevents' directory view to portal_skins\n") 1858 skins = skinstool.getSkinSelections() 1859 for skin in skins: 1860 path = skinstool.getSkinPath(skin) 1861 path = map(string.strip, string.split(path,',')) 1862 if 'zenevents' not in path: 1863 try: path.insert(path.index('zenmodel'), 'zenevents') 1864 except ValueError: 1865 path.append('zenevents') 1866 path = string.join(path, ', ') 1867 skinstool.addSkinSelection(skin, path) 1868 out.write("Added 'zenevents' to %s skin\n" % skin) 1869 else: 1870 out.write( 1871 "Skipping %s skin, 'zenevents' is already set up\n" % skin) 1872 return out.getvalue()
1873