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": 881 if hasattr(state, name): 882 kw.setdefault(name, getattr(state, name)) 883 try: 884 kw.setdefault('severity', 885 dict(self.severityConversions)[state.severity]) 886 except (ValueError, KeyError): 887 pass 888 for name in "startDate", "endDate": 889 if hasattr(state, name): 890 kw.setdefault(name, Time.ParseUSDate(getattr(state, name))) 891 kw.setdefault('startDate', 892 time.time() - 60*60*24*self.defaultAvailabilityDays) 893 return Availability.query(self.dmd, **kw)
894 895
896 - def getHeartbeat(self, failures=True, simple=False, limit=0, db=None):
897 """Return all heartbeat issues list of tuples (device, component, secs) 898 """ 899 sel = """select device, component, lastTime from heartbeat """ 900 if failures: 901 sel += "where DATE_ADD(lastTime, INTERVAL timeout SECOND) <= NOW();" 902 903 statusCache = self.checkCache(sel) 904 cleanup = lambda : None 905 if not statusCache: 906 statusCache = [] 907 conn = self.connect() 908 try: 909 curs = conn.cursor() 910 curs.execute(sel) 911 res = list(curs.fetchall()) 912 res.sort(lambda x,y: cmp(x[2],y[2])) 913 devclass = self.getDmdRoot("Devices") 914 for devname, comp, dtime in res: 915 dtime = "%d" % int(time.time()-dtime.timeTime()) 916 dev = devclass.findDevice(devname) 917 if dev and not simple: 918 alink = "<a href='%s'>%s</a>" % ( 919 dev.getPrimaryUrlPath(), dev.id) 920 else: alink = devname 921 statusCache.append([alink, comp, dtime, devname]) 922 if limit: 923 statusCache = statusCache[:limit] 924 cleanup() 925 finally: self.close(conn) 926 return statusCache
927 928
929 - def getHeartbeatObjects(self, failures=True, simple=False, limit=0, db=None):
930 beats = self.getHeartbeat(failures, simple, limit, db) 931 return [{'alink':b[0], 'comp':b[1], 'dtime':b[2], 'devId':b[3]} 932 for b in beats]
933 934
935 - def getAllComponentStatus(self, 936 statclass, 937 countField=None, 938 severity=3, 939 state=1, 940 where=""):
941 "Fetch the counts on all components matching statClass" 942 if countField == None: countField = self.countField 943 select = "select %s, %s, %s from %s where "\ 944 % (self.deviceField, self.componentField, countField, 945 self.statusTable) 946 where = self._wand(where, "%s = '%s'", self.eventClassField, statclass) 947 where = self._wand(where, "%s >= %s", self.severityField, severity) 948 where = self._wand(where, "%s <= %s", self.stateField, state) 949 select += where 950 conn = self.connect() 951 try: 952 curs = conn.cursor() 953 curs.execute(select) 954 result = {} 955 for dev, comp, count in curs.fetchall(): 956 dev = self.cleanstring(dev) 957 comp = self.cleanstring(comp) 958 result[dev,comp] = count 959 return result 960 finally: 961 self.close(conn)
962 963
964 - def getMaxSeverity(self, me):
965 """ Returns the severity of the most severe event. """ 966 where = self.lookupManagedEntityWhere(me.device()) 967 if me.event_key == 'Component': 968 where = self._wand(where, "%s = '%s'", 969 self.componentField, me.id) 970 select = "select max(%s) from %s where " % (self.severityField, 971 self.statusTable) 972 query = select + where 973 conn = self.connect() 974 try: 975 curs = conn.cursor() 976 curs.execute(query) 977 severity = curs.fetchall()[0][0] 978 finally: self.close(conn) 979 return max(severity, 0)
980 981
982 - def getComponentStatus(self, device, component, statclass=None, 983 countField=None, severity=3, state=1, where=""):
984 """see IEventStatus 985 """ 986 if countField == None: countField = self.countField 987 select = "select %s, %s, %s from %s where "\ 988 % (self.deviceField, self.componentField, countField, 989 self.statusTable) 990 where = self._wand(where, "%s = '%s'", self.eventClassField, statclass) 991 where = self._wand(where, "%s >= %s", self.severityField, severity) 992 where = self._wand(where, "%s <= %s", self.stateField, state) 993 select += where 994 statusCache = self.checkCache(select) 995 if not statusCache: 996 conn = self.connect() 997 try: 998 curs = conn.cursor() 999 curs.execute(select) 1000 statusCache ={} 1001 for dev, comp, count in curs.fetchall(): 1002 dev = self.cleanstring(dev) 1003 comp = self.cleanstring(comp) 1004 statusCache[dev+comp] = count 1005 self.addToCache(select,statusCache) 1006 finally: self.close(conn) 1007 return statusCache.get(device+component, 0)
1008
1009 - def getBatchComponentInfo(self, components):
1010 """ 1011 Given a list of dicts with keys 'device', 'component', make a query to 1012 get an overall severity and device status for the group. 1013 """ 1014 severity, state = 3, 1 1015 components = list(components) # Sometimes a generator. We use it twice. 1016 1017 def doQuery(query): 1018 conn = self.connect() 1019 data = None 1020 try: 1021 curs = conn.cursor() 1022 curs.execute(query) 1023 data = curs.fetchall() 1024 finally: self.close(conn) 1025 return data
1026 1027 select = "select MAX(%s) from %s where " % (self.severityField, 1028 self.statusTable) 1029 where = self._wand("", "%s >= %s", self.severityField, severity) 1030 where = self._wand(where, "%s <= %s", self.stateField, state) 1031 def componentWhere(device, component): 1032 return "device = '%s' and component= '%s'" % (device, component)
1033 cwheres = ' and ' + ' or '.join(['(%s)'% componentWhere(**comp) 1034 for comp in components]) 1035 sevquery = select + where + cwheres 1036 1037 select = "select MAX(%s) from %s where " % (self.countField, 1038 self.statusTable) 1039 where = self._wand("", "%s >= %s", self.severityField, severity) 1040 where = self._wand(where, "%s <= %s", self.stateField, state) 1041 where = self._wand(where, "%s = '%s'", self.eventClassField, 1042 '/Status/Ping') 1043 devwhere = lambda d:"device = '%s'" % d 1044 dwheres = ' and ' + '(%s)' % (' or '.join( 1045 map(devwhere, [x['device'] for x in components]))) 1046 statquery = select + where + dwheres 1047 1048 maxseverity = doQuery(sevquery)[0][0] 1049 maxstatus = doQuery(statquery)[0][0] 1050 return maxseverity, maxstatus 1051
1052 - def getEventOwnerListME(self, me, severity=0, state=1):
1053 """Return list of event owners based on passed in managed entity. 1054 """ 1055 try: 1056 where = self.lookupManagedEntityWhere(me) 1057 return self.getEventOwnerList(where, severity, state) 1058 except: 1059 log.exception("event summary for %s failed" % me.getDmdKey()) 1060 raise
1061 1062
1063 - def getEventOwnerList(self, where="", severity=0, state=1):
1064 """Return a list of userids that correspond to the events in where. 1065 select distinct ownerid from status where 1066 device="win2k.confmon.loc" and eventState > 2 1067 """ 1068 select ="select distinct ownerid from status where " 1069 where = self._wand(where, "%s >= %s", self.severityField, severity) 1070 where = self._wand(where, "%s <= %s", self.stateField, state) 1071 select += where 1072 #print select 1073 statusCache = self.checkCache(select) 1074 if statusCache: return statusCache 1075 conn = self.connect() 1076 try: 1077 curs = conn.cursor() 1078 curs.execute(select) 1079 statusCache = [ uid[0] for uid in curs.fetchall() if uid[0] ] 1080 self.addToCache(select,statusCache) 1081 finally: self.close(conn) 1082 return statusCache
1083 1084
1085 - def lookupManagedEntityWhere(self, me):
1086 """ 1087 Lookup and build where clause for managed entity. 1088 1089 @param me: managed entity 1090 @type me: object 1091 @return: where clause 1092 @rtype: string 1093 """ 1094 key = me.event_key + "Where" 1095 wheretmpl = getattr(aq_base(self), key, False) 1096 if not wheretmpl: 1097 raise ValueError("No 'where' clause found for event_key %s" % me.event_key) 1098 return eval(wheretmpl,{'me':me})
1099 1100
1101 - def lookupManagedEntityField(self, event_key):
1102 """ 1103 Lookup database field for managed entity default 1104 using event_key. 1105 1106 @param event_key: event 1107 @type event_key: string 1108 @return: field for the managed entity 1109 @rtype: object 1110 """ 1111 key = event_key + "Field" 1112 return getattr(aq_base(self), key, event_key)
1113 1114
1115 - def lookupManagedEntityResultFields(self, event_key):
1116 """ 1117 Gets the column names that should be requested in an event query for 1118 this entity type. 1119 1120 Returns a set of result fields predefined for this entity type. If 1121 none have been defined, returns the default result fields. 1122 1123 >>> f = dmd.ZenEventManager.lookupManagedEntityResultFields('Device') 1124 >>> f==dmd.ZenEventManager.DeviceResultFields 1125 True 1126 >>> f = dmd.ZenEventManager.lookupManagedEntityResultFields('Robot') 1127 >>> f==dmd.ZenEventManager.defaultResultFields 1128 True 1129 1130 @param event_key: The event key of a managed entity. 1131 @type event_key: string 1132 @return: A tuple of strings representing columns in the database. 1133 """ 1134 key = event_key + "ResultFields" 1135 return getattr(aq_base(self), key, self.defaultResultFields)
1136 1137
1138 - def _wand(self, where, fmt, field, value):
1139 """ 1140 >>> dmd.ZenEventManager._wand('where 1=1', '%s=%s', 'a', 'b') 1141 'where 1=1 and a=b' 1142 >>> dmd.ZenEventManager._wand('where a=5', '%s=%s', 'a', 'b') 1143 'where a=5' 1144 >>> dmd.ZenEventManager._wand('where b=a', '%s=%s', 'a', 'b') 1145 'where b=a' 1146 """ 1147 if value != None and where.find(field) == -1: 1148 if where: where += " and " 1149 where += fmt % (field, value) 1150 return where
1151
1152 - def _setupDateRange(self, startdate=None, 1153 enddate=None):
1154 """ 1155 Make a start and end date range that is at least one day long. 1156 returns a start and end date as a proper database element. 1157 """ 1158 if enddate is None: 1159 enddate = DateTime.DateTime()-1 1160 if startdate is None: 1161 startdate = DateTime.DateTime() 1162 if type(enddate) == types.StringType: 1163 enddate = DateTime.DateTime(enddate, datefmt='us') 1164 enddate = enddate.latestTime() 1165 if type(startdate) == types.StringType: 1166 startdate = DateTime.DateTime(startdate, datefmt='us') 1167 startdate = startdate.earliestTime() 1168 startdate = self.dateDB(startdate) 1169 enddate = self.dateDB(enddate) 1170 return startdate, enddate
1171
1172 - def getAvgFieldLength(self, fieldname):
1173 conn = self.connect() 1174 try: 1175 curs = conn.cursor() 1176 selstatement = ("SELECT AVG(CHAR_LENGTH(mycol))+20 FROM (SELECT %s AS " 1177 "mycol FROM %s WHERE %s IS NOT NULL LIMIT 500) AS " 1178 "a;") % (fieldname, self.statusTable, fieldname) 1179 curs.execute(selstatement) 1180 avglen = curs.fetchone() 1181 finally: self.close(conn) 1182 try: return float(avglen[0]) 1183 except TypeError: return 10.
1184 1185 1186 #========================================================================== 1187 # Event sending functions 1188 #========================================================================== 1189 1190 security.declareProtected(ZEN_SEND_EVENTS, 'sendEvents')
1191 - def sendEvents(self, events):
1192 """Send a group of events to the backend. 1193 """ 1194 count = 0 1195 for event in events: 1196 try: 1197 self.sendEvent(event) 1198 count += 1 1199 except Exception, ex: 1200 log.exception(ex) 1201 return count
1202 1203 1204 security.declareProtected(ZEN_SEND_EVENTS, 'sendEvent')
1205 - def sendEvent(self, event):
1206 """ 1207 Send an event to the backend. 1208 1209 @param event: event 1210 @type event: event object 1211 @todo: implement 1212 """ 1213 raise NotImplementedError
1214 1215 1216 #========================================================================== 1217 # Schema management functions 1218 #========================================================================== 1219
1220 - def convert(self, field, value):
1221 """Perform convertion of value coming from database value if nessesary. 1222 """ 1223 value = self.cleanstring(value) 1224 #FIXME this is commented out because we often need severity as a 1225 # number (like in ZEvent.getCssClass) and sorting. Need to have 1226 # both fields at some point 1227 #if field == self.severityField: 1228 # idx = len(self.severityConversions) - value 1229 # value = self.severityConversions[idx][0] 1230 if self.isDate(field): 1231 value = self.dateString(value) 1232 return value
1233 1234 1235 security.declareProtected(ZEN_VIEW, "getFieldList")
1236 - def getFieldList(self):
1237 """Return a list of all fields in the status table of the backend. 1238 """ 1239 if not getattr(self, '_fieldlist', None): 1240 self.loadSchema() 1241 return self._fieldlist
1242
1243 - def getEventStates(self):
1244 """Return a list of possible event states. 1245 """ 1246 return self.eventStateConversions
1247
1248 - def getEventActions(self):
1249 """Return a list of possible event actions. 1250 """ 1251 return self.eventActions
1252 1253 security.declareProtected(ZEN_COMMON,'getSeverities')
1254 - def getSeverities(self):
1255 """Return a list of tuples of severities [('Warning', 3), ...] 1256 """ 1257 return self.severityConversions
1258
1259 - def getSeverityString(self, severity):
1260 """Return a string representation of the severity. 1261 """ 1262 try: 1263 return self.severities[severity] 1264 except KeyError: 1265 return "Unknown"
1266
1267 - def getPriorities(self):
1268 """Return a list of tuples of priorities [('Warning', 3), ...] 1269 """ 1270 return self.priorityConversions
1271
1272 - def getPriorityString(self, priority):
1273 """Return the priority name 1274 """ 1275 try: 1276 return self.priorities[priority] 1277 except IndexError: 1278 return "Unknown"
1279 1280
1281 - def convertEventField(self, field, value, default=""):
1282 """ 1283 Convert numeric values commonly found in events to their textual 1284 representation. 1285 """ 1286 if value is None: return default 1287 try: 1288 value = int(value) 1289 except ValueError: 1290 return "unknown (%r)" % (value,) 1291 1292 if field == 'severity' and self.severities.has_key(value): 1293 return "%s (%d)" % (self.severities[value], value) 1294 elif field == "priority" and self.priorities.has_key(value): 1295 return "%s (%d)" % (self.priorities[value], value) 1296 elif field == 'eventState': 1297 if value < len(self.eventStateConversions): 1298 return "%s (%d)" % (self.eventStateConversions[value][0], value) 1299 elif field == "prodState": 1300 prodState = self.dmd.convertProdState(value) 1301 if isinstance(prodState, types.StringType): 1302 return "%s (%d)" % (prodState, value) 1303 elif field == "DevicePriority": 1304 priority = self.dmd.convertPriority(value) 1305 if isinstance(priority, types.StringType): 1306 return "%s (%d)" % (priority, value) 1307 1308 return "unknown (%r)" % (value,)
1309 1310
1311 - def getStatusCssClass(self, status):
1312 if status < 0: status = "unknown" 1313 elif status > 3: status = 3 1314 return "zenstatus_%s" % status
1315 1316
1317 - def getStatusImgSrc(self, status):
1318 ''' Return the img source for a status number 1319 ''' 1320 if status < 0: 1321 src = 'grey' 1322 elif status == 0: 1323 src = 'green' 1324 else: 1325 src = 'red' 1326 return '/zport/dmd/img/%s_dot.png' % src
1327 1328
1329 - def getEventCssClass(self, severity, acked=False):
1330 """return the css class name to be used for this event. 1331 """ 1332 __pychecker__='no-constCond' 1333 value = severity < 0 and "unknown" or severity 1334 acked = acked and "acked" or "noack" 1335 return "zenevents_%s_%s %s" % (value, acked, acked)
1336 1337
1338 - def isDate(self, colName):
1339 """Check to see if a column is of type date. 1340 """ 1341 if not self._schema: 1342 self.getFieldList() 1343 return self._schema.get(colName, False)
1344 1345
1346 - def dateString(self, value):
1347 """Convert a date from database format to string. 1348 """ 1349 if isinstance(value, DateTime.DateTime): 1350 value = value.timeTime() 1351 return Time.LocalDateTime(value)
1352 1353 1354
1355 - def dateDB(self, value):
1356 """Convert a date to its database format. 1357 """ 1358 if isinstance(value, DateTime.DateTime): 1359 return "%.3f" % value.timeTime() 1360 elif type(value) == types.StringTypes: 1361 return "%.3f" % DateTime.DateTime(value).timeTime() 1362 return value
1363 1364
1365 - def escape(self, value):
1366 """ 1367 Prepare string values for db by escaping special characters. 1368 1369 @param value: string 1370 @type value: string 1371 @todo: implement 1372 """ 1373 raise NotImplementedError
1374 1375
1376 - def loadSchema(self):
1377 """Load schema from database. If field is a date set value to true.""" 1378 schema = {} 1379 fieldlist = [] 1380 sql = "describe %s;" % self.statusTable 1381 conn = self.connect() 1382 try: 1383 curs = conn.cursor() 1384 curs.execute(sql) 1385 for row in curs.fetchall(): 1386 fieldlist.append(row[0]) 1387 col = self.cleanstring(row[0]) 1388 if self.backend == "mysql": 1389 type = row[1] in ("datetime", "timestamp", "double") 1390 schema[col] = type 1391 if schema: self._schema = schema 1392 self._fieldlist = fieldlist 1393 finally: self.close(conn)
1394 1395
1396 - def eventControls(self):
1397 """Are there event controls on this event list. 1398 """ 1399 if self.isManager() and self.statusTable in ["status","history"]: 1400 return 1 1401 return 0
1402
1403 - def updateEvents(self, stmt, whereClause, reason, 1404 table="status", toLog=True):
1405 userId = getSecurityManager().getUser().getId() 1406 insert = 'INSERT INTO log (evid, userName, text) ' + \ 1407 'SELECT evid, "%s", "%s" ' % (userId, reason) + \ 1408 'FROM %s ' % table + whereClause 1409 query = stmt + ' ' + whereClause 1410 conn = self.connect() 1411 try: 1412 curs = conn.cursor() 1413 if toLog: curs.execute(insert) 1414 curs.execute(query) 1415 finally: self.close(conn) 1416 self.clearCache() 1417 self.manage_clearCache()
1418 1419 security.declareProtected(ZEN_MANAGE_EVENTS,'manage_addEvent')
1420 - def manage_addEvent(self, REQUEST=None):
1421 ''' Create an event from user supplied data 1422 ''' 1423 eventDict = dict( 1424 summary = REQUEST.get('summary', ''), 1425 message = REQUEST.get('message', ''), 1426 device = REQUEST.get('device', ''), 1427 component = REQUEST.get('component', ''), 1428 severity = REQUEST.get('severity', ''), 1429 eventClassKey = REQUEST.get('eventClassKey', ''), 1430 ) 1431 # We don't want to put empty eventClass into the dict because that 1432 # can keep the event from being mapped to /Unknown correctly. 1433 if REQUEST.get('eclass', None): 1434 eventDict['eventClass'] = REQUEST['eclass'] 1435 # sendEvent insists on a device or a component. Can be bogus. 1436 if not eventDict['device'] and not eventDict['component']: 1437 if REQUEST: 1438 messaging.IMessageSender(self).sendToBrowser( 1439 'Invalid Event', 1440 'You must specify a device and/or a component.', 1441 priority=messaging.WARNING 1442 ) 1443 return self.callZenScreen(REQUEST) 1444 else: 1445 return 1446 self.sendEvent(eventDict) 1447 if REQUEST: 1448 REQUEST['RESPONSE'].redirect('/zport/dmd/Events/viewEvents')
1449 1450
1451 - def deleteEvents(self, whereClause, reason):
1452 self.updateEvents('DELETE FROM status', whereClause, reason)
1453 1454 1455 security.declareProtected(ZEN_MANAGE_EVENTS,'manage_deleteEvents')
1456 - def manage_deleteEvents(self, evids=(), REQUEST=None):
1457 "Delete the given event ids" 1458 if type(evids) == type(''): 1459 evids = [evids] 1460 num = len(evids) 1461 if evids: 1462 evids = ",".join([ "'%s'" % evid for evid in evids]) 1463 whereClause = ' where evid in (%s)' % evids 1464 self.deleteEvents(whereClause, 'Deleted by user') 1465 if REQUEST: 1466 messaging.IMessageSender(self).sendToBrowser( 1467 'Moved To History', 1468 '%s event%s have been moved to history.' % ( 1469 num, (num != 1 and 's') or '') 1470 ) 1471 return self.callZenScreen(REQUEST)
1472
1473 - def undeleteEvents(self, whereClause, reason):
1474 fields = ','.join( self.getFieldList() ) 1475 # We want to blank clearid 1476 fields = fields.replace('clearid','NULL') 1477 self.updateEvents( 'INSERT status ' + \ 1478 'SELECT %s FROM history' % fields, 1479 whereClause + \ 1480 ' ON DUPLICATE KEY UPDATE status.count=status.count+history.count', 1481 reason, 'history', toLog=False) 1482 self.updateEvents( 'DELETE FROM history', whereClause, \ 1483 reason, 'history')
1484 1485 security.declareProtected(ZEN_MANAGE_EVENTS,'manage_undeleteEvents')
1486 - def manage_undeleteEvents(self, evids=(), REQUEST=None):
1487 "Move the given event ids into status and delete from history" 1488 if type(evids) == type(''): 1489 evids = [evids] 1490 num = len(evids) 1491 if evids: 1492 evids = ",".join([ "'%s'" % evid for evid in evids]) 1493 whereClause = ' where evid in (%s)' % evids 1494 self.undeleteEvents(whereClause, 'Undeleted by user') 1495 if REQUEST: 1496 messaging.IMessageSender(self).sendToBrowser( 1497 'Undeleted', 1498 '%s events have been moved out of history.' % num 1499 ) 1500 return self.callZenScreen(REQUEST)
1501 1502 1503 security.declareProtected(ZEN_MANAGE_EVENTS,'manage_deleteAllEvents')
1504 - def manage_deleteAllEvents(self, devname, REQUEST=None):
1505 "Delete the events for a given Device (used for deleting the device" 1506 whereClause = 'where device = "%s"' % devname 1507 self.deleteEvents(whereClause, 'Device deleted') 1508 if REQUEST: 1509 messaging.IMessageSender(self).sendToBrowser( 1510 'Events Deleted', 1511 'Deleted all events for %s' % devname 1512 ) 1513 return self.callZenScreen(REQUEST)
1514 1515 1516 security.declareProtected(ZEN_MANAGE_EVENTS,'manage_deleteHistoricalEvents')
1517 - def manage_deleteHistoricalEvents(self, devname=None, agedDays=None, 1518 REQUEST=None):
1519 """ 1520 Delete historical events. If devices is given then only delete 1521 events for that device. If agedDays is given then only delete 1522 events that are older than that many days. 1523 devname and agedDays are mutually exclusive. No real reason for this 1524 other than there is no current need to use both in same call and I 1525 don't want to test the combination. 1526 This is an option during device deletion. It is also used 1527 by zenactions to keep history table clean. 1528 1529 NB: Device.deleteDevice() is not currently calling this when devices 1530 are deleted. See ticket #2996. 1531 """ 1532 import subprocess 1533 import os 1534 import Products.ZenUtils.Utils as Utils 1535 1536 cmd = Utils.zenPath('Products', 'ZenUtils', 'ZenDeleteHistory.py') 1537 if devname: 1538 args = ['--device=%s' % devname] 1539 elif agedDays: 1540 args = ['--numDays=%s' % agedDays] 1541 else: 1542 return 1543 proc = subprocess.Popen( 1544 [cmd]+args, stdout=subprocess.PIPE, 1545 stderr=subprocess.STDOUT, env=os.environ) 1546 # We are abandoning this proc to do it's thing. or not. We don't 1547 # want to block because we would delay user feedback on a device 1548 # delete when this might take a while to perform. 1549 unused(proc) 1550 if REQUEST: 1551 messaging.IMessageSender(self).sendToBrowser( 1552 'Events Deleted', 1553 'Historical events have been deleted.' 1554 ) 1555 return self.callZenScreen(REQUEST)
1556 1557 1558 security.declareProtected(ZEN_MANAGE_EVENTS,'manage_deleteHeartbeat')
1559 - def manage_deleteHeartbeat(self, devname, REQUEST=None):
1560 if devname: 1561 delete = "delete from heartbeat where device = '%s'" % devname 1562 conn = self.connect() 1563 try: 1564 curs = conn.cursor() 1565 curs.execute(delete); 1566 finally: self.close(conn) 1567 if REQUEST: 1568 messaging.IMessageSender(self).sendToBrowser( 1569 'Heartbeats Cleared', 1570 'Heartbeat events have been moved to the history.' 1571 ) 1572 return self.callZenScreen(REQUEST)
1573 1574 1575 security.declareProtected(ZEN_MANAGE_EVENTS,'manage_ackEvents')
1576 - def manage_ackEvents(self, evids=(), REQUEST=None):
1577 "Ack the given event ids" 1578 if type(evids) == type(''): 1579 evids = [evids] 1580 request = FakeRequest() 1581 self.manage_setEventStates(1 , evids, REQUEST=request) 1582 if REQUEST: 1583 dest = '/zport/dmd/Events/viewEvents' 1584 if request.get('message', ''): 1585 dest += '?message=%s' % request['message'] 1586 if not getattr(REQUEST, 'dontRedirect', False): 1587 REQUEST['RESPONSE'].redirect(dest)
1588 1589 1590 security.declareProtected(ZEN_MANAGE_EVENTS,'manage_setEventStates')
1591 - def manage_setEventStates(self, eventState=None, evids=(), 1592 userid="", REQUEST=None):
1593 reason = None 1594 if eventState and evids: 1595 eventState = int(eventState) 1596 if eventState > 0 and not userid: 1597 userid = getSecurityManager().getUser() 1598 update = "update status set eventState=%s, ownerid='%s' " % ( 1599 eventState, userid) 1600 whereClause = "where evid in (" 1601 whereClause += ",".join([ "'%s'" % evid for evid in evids]) + ")" 1602 reason = 'Event state changed to ' 1603 try: 1604 reason += self.eventStateConversions[eventState][0] 1605 except KeyError: 1606 reason += 'unknown (%d)' % eventState 1607 self.updateEvents(update, whereClause, reason) 1608 if REQUEST: 1609 if not reason: reason = '' 1610 messaging.IMessageSender(self).sendToBrowser( 1611 'Event States Set', 1612 reason 1613 ) 1614 return self.callZenScreen(REQUEST)
1615 1616 1617 security.declareProtected(ZEN_MANAGE_EVENTS,'manage_setEventStates')
1618 - def manage_createEventMap(self, eventClass=None, evids=(), 1619 REQUEST=None):
1620 """Create an event map from an event or list of events. 1621 """ 1622 evclass = None 1623 evmap = None 1624 numCreated = 0 1625 numNotUnknown = 0 1626 numNoKey = 0 1627 if eventClass and evids: 1628 evclass = self.getDmdRoot("Events").getOrganizer(eventClass) 1629 sel = """select eventClassKey, eventClass, message 1630 from %s where evid in ('%s')""" 1631 sel = sel % (self.statusTable, "','".join(evids)) 1632 conn = self.connect() 1633 try: 1634 curs = conn.cursor() 1635 curs.execute(sel); 1636 for row in curs.fetchall(): 1637 evclasskey, curevclass, msg = row 1638 if curevclass != Unknown: 1639 numNotUnknown += 1 1640 continue 1641 if not evclasskey: 1642 numNoKey += 1 1643 continue 1644 evmap = evclass.createInstance(evclasskey) 1645 evmap.eventClassKey = evclasskey 1646 evmap.example = msg 1647 numCreated += 1 1648 evmap.index_object() 1649 finally: self.close(conn) 1650 elif REQUEST: 1651 if not evids: 1652 messaging.IMessageSender(self).sendToBrowser( 1653 'Error', 1654 'No events selected', 1655 priority=messaging.WARNING 1656 ) 1657 elif not eventClass: 1658 messaging.IMessageSender(self).sendToBrowser( 1659 'Error', 1660 'No event class selected', 1661 priority=messaging.WARNING 1662 ) 1663 1664 if REQUEST: 1665 msg = REQUEST.get('message', '') 1666 if numNotUnknown: 1667 msg += ((msg and ' ') + 1668 '%s event%s %s not class /Unknown.' % ( 1669 numNotUnknown, 1670 (numNotUnknown != 1 and 's') or '', 1671 (numNotUnknown != 1 and 'are') or 'is')) 1672 if numNoKey: 1673 msg += ((msg and ' ') + 1674 '%s event%s %s not have an event class key.' % ( 1675 numNoKey, 1676 (numNoKey != 1 and 's') or '', 1677 (numNoKey != 1 and 'do') or 'does')) 1678 msg += (msg and ' ') + 'Created %s event mapping%s.' % ( 1679 numCreated, 1680 (numCreated != 1 and 's') or '') 1681 1682 messaging.IMessageSender(self).sendToBrowser('Event Map', msg) 1683 # EventView might pass a fake Request during an ajax call from 1684 # event console. Don't bother rendering anything in this case. 1685 if getattr(REQUEST, 'dontRender', False): 1686 return '' 1687 if len(evids) == 1 and evmap: 1688 REQUEST['RESPONSE'].redirect(evmap.absolute_url()) 1689 elif evclass and evmap: 1690 REQUEST['RESPONSE'].redirect(evclass.absolute_url())
1691 1692 1693 security.declareProtected(ZEN_MANAGE_EVENTMANAGER,'manage_refreshConversions')
1694 - def manage_refreshConversions(self, REQUEST=None):
1695 """get the conversion information from the database server""" 1696 assert(self == self.dmd.ZenEventManager) 1697 self.loadSchema() 1698 self.dmd.ZenEventHistory.loadSchema() 1699 if REQUEST: 1700 messaging.IMessageSender(self).sendToBrowser( 1701 'Event Schema', 'Event schema has been refreshed.') 1702 return self.callZenScreen(REQUEST)
1703 1704 1705 security.declareProtected(ZEN_MANAGE_EVENTMANAGER,'manage_editCache')
1706 - def manage_editCache(self, timeout=5, clearthresh=20, REQUEST=None):
1707 """Reset cache values""" 1708 self.timeout = int(timeout) 1709 self.clearthresh = int(clearthresh) 1710 if REQUEST: 1711 message = "Cache parameters set" 1712 return self.editCache(self, REQUEST, manage_tabs_message=message)
1713 1714 1715 security.declareProtected(ZEN_MANAGE_EVENTMANAGER,'manage_clearCache')
1716 - def manage_clearCache(self, REQUEST=None):
1717 """Reset cache values""" 1718 assert(self == self.dmd.ZenEventManager) 1719 self.cleanCache(force=1) 1720 self.dmd.ZenEventHistory.cleanCache(force=1) 1721 if REQUEST: 1722 messaging.IMessageSender(self).sendToBrowser( 1723 'Event Cache', 'Event cache has been cleared.') 1724 return self.callZenScreen(REQUEST)
1725 1726 1727 security.declareProtected(ZEN_MANAGE_EVENTMANAGER,'manage_editEventManager')
1728 - def manage_editEventManager(self, REQUEST=None):
1729 ''' Call zmanage_editProperties then take care of saving a few 1730 values to ZenEventHistory 1731 ''' 1732 assert(self == self.dmd.ZenEventManager) 1733 self.zmanage_editProperties(REQUEST) 1734 self.dmd.ZenEventHistory.timeout = REQUEST['history_timeout'] 1735 self.dmd.ZenEventHistory.clearthresh = REQUEST['history_clearthresh'] 1736 self.dmd.ZenEventHistory.username = self.dmd.ZenEventManager.username 1737 self.dmd.ZenEventHistory.password = self.dmd.ZenEventManager.password 1738 self.dmd.ZenEventHistory.database = self.dmd.ZenEventManager.database 1739 self.dmd.ZenEventHistory.host = self.dmd.ZenEventManager.host 1740 self.dmd.ZenEventHistory.port = self.dmd.ZenEventManager.port 1741 if REQUEST: return self.callZenScreen(REQUEST)
1742 1743 1744 security.declareProtected(ZEN_MANAGE_EVENTMANAGER,'manage_clearHeartbeats')
1745 - def manage_clearHeartbeats(self, REQUEST=None):
1746 """truncate heartbeat table""" 1747 conn = self.connect() 1748 try: 1749 curs = conn.cursor() 1750 sql = 'truncate table heartbeat' 1751 curs.execute(sql) 1752 finally: self.close(conn) 1753 if REQUEST: 1754 messaging.IMessageSender(self).sendToBrowser( 1755 'Heartbeats Cleared', 'Heartbeats have been cleared.') 1756 return self.callZenScreen(REQUEST)
1757 1758 security.declareProtected(ZEN_MANAGE_EVENTMANAGER,'zmanage_editProperties')
1759 - def zmanage_editProperties(self, REQUEST=None):
1760 ''' Need to handle editing of history event fields differently 1761 ''' 1762 assert(self == self.dmd.ZenEventManager) 1763 screenName = REQUEST.get('zenScreenName', '') 1764 if screenName == 'editEventManagerHistoryFields': 1765 obj = self.dmd.ZenEventHistory 1766 else: 1767 obj = self 1768 if screenName == 'editEventManager': 1769 # We renamed the password field to try to keep browsers from 1770 # asking user if they wanted to save the password. 1771 if REQUEST.has_key('mysql_pass'): 1772 REQUEST.form['password'] = REQUEST['mysql_pass'] 1773 editProperties = ZenModelRM.zmanage_editProperties 1774 # suppress 'self is not first method argument' from pychecker 1775 editProperties(obj, REQUEST) 1776 if REQUEST: return self.callZenScreen(REQUEST)
1777 1778 security.declareProtected(ZEN_MANAGE_EVENTS, 'manage_addLogMessage')
1779 - def manage_addLogMessage(self, evid=None, message='', REQUEST=None):
1780 """ 1781 Add a log message to an event 1782 """ 1783 if not evid: 1784 return 1785 userId = getSecurityManager().getUser().getId() 1786 conn = self.connect() 1787 try: 1788 curs = conn.cursor() 1789 insert = 'INSERT INTO log (evid, userName, text) ' 1790 insert += 'VALUES ("%s", "%s", "%s")' % (evid, 1791 userId, 1792 conn.escape_string(message)) 1793 curs.execute(insert) 1794 finally: self.close(conn) 1795 self.clearCache('evid' + evid) 1796 self.dmd.ZenEventHistory.clearCache('evid' + evid) 1797 if REQUEST: return self.callZenScreen(REQUEST)
1798 1799 1800 security.declareProtected(ZEN_MANAGE_EVENTMANAGER, 'manage_addCommand')
1801 - def manage_addCommand(self, id, REQUEST=None):
1802 """ 1803 Add a new EventCommand 1804 1805 @param id: new command name 1806 @type id: string 1807 @param REQUEST: Zope REQUEST object 1808 @type REQUEST: Zope REQUEST object 1809 """ 1810 if id is None: 1811 if REQUEST: 1812 return self.callZenScreen(REQUEST) 1813 return 1814 1815 if not isinstance(id, unicode): 1816 id = self.prepId(id) 1817 1818 id = id.strip() 1819 if id == '': 1820 if REQUEST: 1821 return self.callZenScreen(REQUEST) 1822 return 1823 1824 ec = EventCommand(id) 1825 self.commands._setObject(id, ec) 1826 if REQUEST: 1827 return self.callZenScreen(REQUEST)
1828 1829 1830 security.declareProtected(ZEN_MANAGE_EVENTMANAGER, 'manage_deleteCommands')
1831 - def manage_deleteCommands(self, ids=[], REQUEST=None):
1832 """ 1833 Delete an EventCommand 1834 1835 @param ids: commands to delete 1836 @type ids: list of strings 1837 @param REQUEST: Zope REQUEST object 1838 @type REQUEST: Zope REQUEST object 1839 """ 1840 for id in ids: 1841 self.commands._delObject(id) 1842 if REQUEST: return self.callZenScreen(REQUEST)
1843 1844 1845 #========================================================================== 1846 # Utility functions 1847 #========================================================================== 1848
1849 - def installIntoPortal(self):
1850 """Install skins into portal. 1851 """ 1852 from Products.CMFCore.utils import getToolByName 1853 from Products.CMFCore.DirectoryView import addDirectoryViews 1854 from cStringIO import StringIO 1855 import string 1856 1857 out = StringIO() 1858 skinstool = getToolByName(self, 'portal_skins') 1859 if 'zenevents' not in skinstool.objectIds(): 1860 addDirectoryViews(skinstool, 'skins', globals()) 1861 out.write("Added 'zenevents' directory view to portal_skins\n") 1862 skins = skinstool.getSkinSelections() 1863 for skin in skins: 1864 path = skinstool.getSkinPath(skin) 1865 path = map(string.strip, string.split(path,',')) 1866 if 'zenevents' not in path: 1867 try: path.insert(path.index('zenmodel'), 'zenevents') 1868 except ValueError: 1869 path.append('zenevents') 1870 path = string.join(path, ', ') 1871 skinstool.addSkinSelection(skin, path) 1872 out.write("Added 'zenevents' to %s skin\n" % skin) 1873 else: 1874 out.write( 1875 "Skipping %s skin, 'zenevents' is already set up\n" % skin) 1876 return out.getvalue()
1877