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
     82 -    def getDeviceLink(self, dmd):
     83          device = self.getDevice(dmd)
     84          if device:
     85              return device.getDeviceLink()
     86          return None
     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