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          self.availability = max(0, 1 - (downtime / total))
     47
     48 -    def floatStr(self):
     49          return '%2.3f%%' % (self.availability * 100)
     50
     51 -    def __str__(self):
     52          return self.floatStr()
     53
     54 -    def __repr__(self):
     55          return '[%s %s %s]' % (self.device, self.component, self.floatStr())
     56
     57 -    def __float__(self):
     58          return float(self.availability)
     59
     60 -    def __int__(self):
     61          return int(self.availability * 100)
     62
     63 -    def __cmp__(self, other):
     64          return cmp((self.availability, self.device, self.component()),
     65                     (other.availability, other.device, other.component()))
     66
     67 -    def getDevice(self, dmd):
     68          return dmd.Devices.findDevice(self.device)
     69
     70 -    def getComponent(self, dmd):
     71          if self.device and self.component:
     72              device = self.getDevice(dmd)
     73              if device:
     74                  return _findComponent(device, self.component)
     75          return None
     76
     77 -    def getDeviceLink(self, dmd):
     78          device = self.getDevice(dmd)
     79          if device:
     80              return device.getDeviceLink()
     81          return None
     82
     83  InitializeClass(Availability)
     84
     85 -class Report:
     86      "Determine availability by counting the amount of time down"
     87
     88 -    def __init__(self,
     89                   startDate = None,
     90                   endDate = None,
     91                   eventClass=Status_Ping,
     92                   severity=5,
     93                   device=None,
     94                   component=''):
     95          self.startDate = _round(startDate)
     96          self.endDate = _round(endDate)
     97          self.eventClass = eventClass
     98          self.severity = severity
     99          self.device = device
    100          self.component = component
    101
    102
    103 -    def tuple(self):
    104          return (self.startDate, self.endDate, self.eventClass,
    105                  self.severity, self.device, self.component)
    106
    107 -    def __hash__(self):
    108          return hash(self.tuple())
    109
    110 -    def __cmp__(self, other):
    111          return cmp(self.tuple(), other.tuple())
    112
    113
    114 -    def run(self, dmd):
    115          """Run the report, returning an Availability object for each device"""
    116          # Note: we don't handle overlapping "down" events, so down
    117          # time could get get double-counted.
    118          __pychecker__='no-local'
    119          zem = dmd.ZenEventManager
    120          cols = 'device, component, firstTime, lastTime'
    121          endDate = self.endDate or time.time()
    122          startDate = self.startDate
    123          if not startDate:
    124              days = zem.defaultAvailabilityDays
    125              startDate = time.time() - days*60*60*24
    126          env = self.__dict__.copy()
    127          env.update(locals())
    128          w =  ' WHERE severity >= %(severity)s '
    129          w += ' AND lastTime > %(startDate)s '
    130          w += ' AND firstTime <= %(endDate)s '
    131          w += ' AND firstTime != lastTime '
    132          w += " AND eventClass = '%(eventClass)s' "
    133          w += " AND prodState >= 1000 "
    134          if self.device:
    135              w += " AND device = '%(device)s' "
    136          if self.component:
    137              w += " AND component like '%%%(component)s%%' "
    138          env['w'] = w % env
    139          s = ('SELECT %(cols)s FROM ( '
    140               ' SELECT %(cols)s FROM history %(w)s '
    141               '  UNION '
    142               ' SELECT %(cols)s FROM status %(w)s '
    143               ') AS U  ' % env)
    144
    145          devices = {}
    146          conn = zem.connect()
    147          try:
    148              curs = conn.cursor()
    149              curs.execute(s)
    150              while 1:
    151                  rows = curs.fetchmany()
    152                  if not rows: break
    153                  for row in rows:
    154                      device, component, first, last = row
    155                      last = min(last, endDate)
    156                      first = max(first, startDate)
    157                      k = (device, component)
    158                      try:
    159                          devices[k] += last - first
    160                      except KeyError:
    161                          devices[k] = last - first
    162          finally: zem.close(conn)
    163          total = endDate - startDate
    164          if self.device:
    165              deviceList = []
    166              device = dmd.Devices.findDevice(self.device)
    167              if device:
    168                  deviceList = [device]
    169                  devices.setdefault( (self.device, self.component), 0)
    170          else:
    171              deviceList = [d for d in dmd.Devices.getSubDevices()]
    172              if not self.component:
    173                  for d in dmd.Devices.getSubDevices():
    174                      devices.setdefault( (d.id, self.component), 0)
    175          deviceLookup = dict([(d.id, d) for d in deviceList])
    176          result = []
    177          for (d, c), v in devices.items():
    178              dev = deviceLookup.get(d, None)
    179              sys = (dev and dev.getSystemNamesString()) or ''
    180              result.append( Availability(d, c, v, total, sys) )
    181          # add in the devices that have the component, but no events
    182          if self.component:
    183              for d in deviceList:
    184                  for c in d.getMonitoredComponents():
    185                      if c.name().find(self.component) >= 0:
    186                          a = Availability(d.id, c.name(), 0, total,
    187                                                          d.getSystemNamesString())
    188                          result.append(a)
    189          return result
    190
    191
    192 -def query(dmd, *args, **kwargs):
    193      r = Report(*args, **kwargs)
    194      try:
    195          return _cache[r.tuple()]
    196      except KeyError:
    197          result = r.run(dmd)
    198          _cache[r.tuple()] = result
    199          return result
    200
    201
    202  if __name__ == '__main__':
    203      import pprint
    204      r = Report(time.time() - 60*60*24*30)
    205      start = time.time() - 60*60*24*30
    206      # r.component = 'snmp'
    207      r.component = None
    208      r.eventClass = Status_Snmp
    209      r.severity = 3
    210      from Products.ZenUtils.ZCmdBase import ZCmdBase
    211      z = ZCmdBase()
    212      pprint.pprint(r.run(z.dmd))
    213      a = query(z.dmd, start, device='gate.zenoss.loc', eventClass=Status_Ping)
    214      assert 0 <= float(a[0]) <= 1.
    215      b = query(z.dmd, start, device='gate.zenoss.loc', eventClass=Status_Ping)
    216      assert a == b
    217      assert id(a) == id(b)
    218      pprint.pprint(r.run(z.dmd))
    219      r.component = 'httpd'
    220      r.eventClass = Status_OSProcess
    221      r.severity = 4
    222      pprint.pprint(r.run(z.dmd))
    223      r.device = 'gate.zenoss.loc'
    224      r.component = ''
    225      r.eventClass = Status_Ping
    226      r.severity = 4
    227      pprint.pprint(r.run(z.dmd))
    228