Package Products :: Package ZenEvents :: Module Availability
[hide private]
[frames] | no frames]

Source Code for Module Products.ZenEvents.Availability

  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  import time 
 15   
 16  from Globals import InitializeClass 
 17  from Products.ZenUtils import Map 
 18  from Products.ZenEvents.ZenEventClasses import Status_Ping, Status_Snmp 
 19  from Products.ZenEvents.ZenEventClasses import Status_OSProcess 
 20   
 21  from AccessControl import ClassSecurityInfo 
 22   
 23  CACHE_TIME = 60. 
 24   
 25  _cache = Map.Locked(Map.Timed({}, CACHE_TIME)) 
 26   
27 -def _round(value):
28 if value is None: return None 29 return (value // CACHE_TIME) * CACHE_TIME
30
31 -def _findComponent(device, name):
32 for c in device.getMonitoredComponents(): 33 if c.name() == name: 34 return c 35 return None
36
37 -class Availability:
38 security = ClassSecurityInfo() 39 security.setDefaultAccess('allow') 40 41 "Simple record for holding availability information"
42 - def __init__(self, device, component, downtime, total, systems=''):
43 self.device = device 44 self.systems = systems 45 self.component = component 46 47 # Guard against endDate being equal to or less than startDate. 48 if total <= 0: 49 self.availability = 0 if downtime else 1 50 else: 51 self.availability = max(0, 1 - (downtime / total))
52
53 - def floatStr(self):
54 return '%2.3f%%' % (self.availability * 100)
55
56 - def __str__(self):
57 return self.floatStr()
58
59 - def __repr__(self):
60 return '[%s %s %s]' % (self.device, self.component, self.floatStr())
61
62 - def __float__(self):
63 return float(self.availability)
64
65 - def __int__(self):
66 return int(self.availability * 100)
67
68 - def __cmp__(self, other):
69 return cmp((self.availability, self.device, self.component()), 70 (other.availability, other.device, other.component()))
71
72 - def getDevice(self, dmd):
73 return dmd.Devices.findDevice(self.device)
74
75 - def getComponent(self, dmd):
76 if self.device and self.component: 77 device = self.getDevice(dmd) 78 if device: 79 return _findComponent(device, self.component) 80 return None
81
87 88 InitializeClass(Availability) 89
90 -class Report:
91 "Determine availability by counting the amount of time down" 92
93 - def __init__(self, 94 startDate = None, 95 endDate = None, 96 eventClass=Status_Ping, 97 severity=5, 98 device=None, 99 component='', 100 prodState=1000, 101 manager=None, 102 agent=None, 103 DeviceClass=None, 104 Location=None, 105 System=None, 106 DeviceGroup=None, 107 DevicePriority=None, 108 monitor=None):
109 self.startDate = _round(startDate) 110 self.endDate = _round(endDate) 111 self.eventClass = eventClass 112 self.severity = severity 113 self.device = device 114 self.component = component 115 self.prodState = prodState 116 self.manager = manager 117 self.agent = agent 118 self.DeviceClass = DeviceClass 119 self.Location = Location 120 self.System = System 121 self.DeviceGroup = DeviceGroup 122 self.DevicePriority = DevicePriority 123 self.monitor = monitor
124
125 - def tuple(self):
126 return ( 127 self.startDate, self.endDate, self.eventClass, self.severity, 128 self.device, self.component, self.prodState, self.manager, 129 self.agent, self.DeviceClass, self.Location, self.System, 130 self.DeviceGroup, self.DevicePriority, self.monitor)
131
132 - def __hash__(self):
133 return hash(self.tuple())
134
135 - def __cmp__(self, other):
136 return cmp(self.tuple(), other.tuple())
137 138
139 - def run(self, dmd):
140 """Run the report, returning an Availability object for each device""" 141 # Note: we don't handle overlapping "down" events, so down 142 # time could get get double-counted. 143 __pychecker__='no-local' 144 zem = dmd.ZenEventManager 145 cols = 'device, component, firstTime, lastTime' 146 endDate = self.endDate or time.time() 147 startDate = self.startDate 148 if not startDate: 149 days = zem.defaultAvailabilityDays 150 startDate = time.time() - days*60*60*24 151 env = self.__dict__.copy() 152 env.update(locals()) 153 w = ' WHERE severity >= %(severity)s ' 154 w += ' AND lastTime > %(startDate)s ' 155 w += ' AND firstTime <= %(endDate)s ' 156 w += ' AND firstTime != lastTime ' 157 w += " AND (eventClass = '%s' OR eventClass LIKE '%s/%%%%') " % (self.eventClass, 158 self.eventClass.rstrip('/')) 159 w += " AND prodState >= %(prodState)s " 160 if self.device: 161 w += " AND device = '%(device)s' " 162 if self.component: 163 w += " AND component like '%%%(component)s%%' " 164 if self.manager is not None: 165 w += " AND manager = '%(manager)s' " 166 if self.agent is not None: 167 w += " AND agent = '%(agent)s' " 168 if self.DeviceClass is not None: 169 w += " AND (DeviceClass = '%s' " % self.DeviceClass 170 w += " OR DeviceClass LIKE '%s/%%%%') " % self.DeviceClass.rstrip('/') 171 if self.Location is not None: 172 w += " AND (Location = '%s' " % self.Location 173 w += " OR Location LIKE '%s/%%%%') " % self.Location.rstrip('/') 174 if self.System is not None: 175 w += " AND Systems LIKE '%%%(System)s%%' " 176 if self.DeviceGroup is not None: 177 w += " AND DeviceGroups LIKE '%%%(DeviceGroup)s%%' " 178 if self.DevicePriority is not None: 179 w += " AND DevicePriority = %(DevicePriority)s " 180 if self.monitor is not None: 181 w += " AND monitor = '%(monitor)s' " 182 env['w'] = w % env 183 s = ('SELECT %(cols)s FROM ( ' 184 ' SELECT %(cols)s FROM history %(w)s ' 185 ' UNION ' 186 ' SELECT %(cols)s FROM status %(w)s ' 187 ') AS U ' % env) 188 189 devices = {} 190 conn = zem.connect() 191 try: 192 curs = conn.cursor() 193 curs.execute(s) 194 while 1: 195 rows = curs.fetchmany() 196 if not rows: break 197 for row in rows: 198 device, component, first, last = row 199 last = min(last, endDate) 200 first = max(first, startDate) 201 202 # Only treat component specially if a component filter was 203 # specified. 204 k = None 205 if self.component: 206 k = (device, component) 207 else: 208 k = (device, '') 209 210 try: 211 devices[k] += last - first 212 except KeyError: 213 devices[k] = last - first 214 finally: zem.close(conn) 215 total = endDate - startDate 216 if self.device: 217 deviceList = [] 218 device = dmd.Devices.findDevice(self.device) 219 if device: 220 deviceList = [device] 221 devices.setdefault( (self.device, self.component), 0) 222 else: 223 deviceList = [] 224 if not self.DeviceClass and not self.Location \ 225 and not self.System and not self.DeviceGroup: 226 deviceList = dmd.Devices.getSubDevices() 227 else: 228 allDevices = {} 229 for d in dmd.Devices.getSubDevices(): 230 allDevices[d.id] = d 231 232 deviceClassDevices = set() 233 if self.DeviceClass: 234 try: 235 org = dmd.Devices.getOrganizer(self.DeviceClass) 236 for d in org.getSubDevices(): 237 deviceClassDevices.add(d.id) 238 except KeyError: 239 pass 240 else: 241 deviceClassDevices = set(allDevices.keys()) 242 243 locationDevices = set() 244 if self.Location: 245 try: 246 org = dmd.Locations.getOrganizer(self.Location) 247 for d in org.getSubDevices(): 248 locationDevices.add(d.id) 249 except KeyError: 250 pass 251 else: 252 locationDevices = set(allDevices.keys()) 253 254 systemDevices = set() 255 if self.System: 256 try: 257 org = dmd.Systems.getOrganizer(self.System) 258 for d in org.getSubDevices(): 259 systemDevices.add(d.id) 260 except KeyError: 261 pass 262 else: 263 systemDevices = set(allDevices.keys()) 264 265 deviceGroupDevices = set() 266 if self.DeviceGroup: 267 try: 268 org = dmd.Groups.getOrganizer(self.DeviceGroup) 269 for d in org.getSubDevices(): 270 deviceGroupDevices.add(d.id) 271 except KeyError: 272 pass 273 else: 274 deviceGroupDevices = set(allDevices.keys()) 275 276 # Intersect all of the organizers. 277 for deviceId in (deviceClassDevices & locationDevices & \ 278 systemDevices & deviceGroupDevices): 279 deviceList.append(allDevices[deviceId]) 280 281 if not self.component: 282 for d in dmd.Devices.getSubDevices(): 283 devices.setdefault( (d.id, self.component), 0) 284 deviceLookup = dict([(d.id, d) for d in deviceList]) 285 result = [] 286 for (d, c), v in devices.items(): 287 dev = deviceLookup.get(d, None) 288 if dev is None: 289 continue 290 sys = dev.getSystemNamesString() 291 result.append( Availability(d, c, v, total, sys) ) 292 # add in the devices that have the component, but no events 293 if self.component: 294 for d in deviceList: 295 for c in d.getMonitoredComponents(): 296 if c.name().find(self.component) >= 0: 297 a = Availability(d.id, c.name(), 0, total, 298 d.getSystemNamesString()) 299 result.append(a) 300 return result
301 302
303 -def query(dmd, *args, **kwargs):
304 r = Report(*args, **kwargs) 305 try: 306 return _cache[r.tuple()] 307 except KeyError: 308 result = r.run(dmd) 309 _cache[r.tuple()] = result 310 return result
311 312 313 if __name__ == '__main__': 314 import pprint 315 r = Report(time.time() - 60*60*24*30) 316 start = time.time() - 60*60*24*30 317 # r.component = 'snmp' 318 r.component = None 319 r.eventClass = Status_Snmp 320 r.severity = 3 321 from Products.ZenUtils.ZCmdBase import ZCmdBase 322 z = ZCmdBase() 323 pprint.pprint(r.run(z.dmd)) 324 a = query(z.dmd, start, device='gate.zenoss.loc', eventClass=Status_Ping) 325 assert 0 <= float(a[0]) <= 1. 326 b = query(z.dmd, start, device='gate.zenoss.loc', eventClass=Status_Ping) 327 assert a == b 328 assert id(a) == id(b) 329 pprint.pprint(r.run(z.dmd)) 330 r.component = 'httpd' 331 r.eventClass = Status_OSProcess 332 r.severity = 4 333 pprint.pprint(r.run(z.dmd)) 334 r.device = 'gate.zenoss.loc' 335 r.component = '' 336 r.eventClass = Status_Ping 337 r.severity = 4 338 pprint.pprint(r.run(z.dmd)) 339