Package Products :: Package ZenStatus :: Module PingTask |
[hide private]
[frames] | no frames]

# Source Code for Module Products.ZenStatus.PingTask

      1  ##############################################################################
      2  #
      3  # Copyright (C) Zenoss, Inc. 2007, 2010, 2011, all rights reserved.
      4  #
      5  # This content is made available according to terms specified in
      6  # License.zenoss under the directory where your Zenoss product is installed.
      7  #
      8  ##############################################################################
      9
     10
     11  __doc__ = """PingTask
     12
     13  Determines the availability of a IP addresses using ping (ICMP).
     14
     15  """
     16
     17  import math
     18  import re
     19  import time
     20  import logging
     21  log = logging.getLogger("zen.zenping")
     22
     23  from twisted.python.failure import Failure
     24  from twisted.internet import defer
     25
     26  import Globals
     27  from zope import interface
     28  from zope import component
     29
     30  from zenoss.protocols.protobufs.zep_pb2 import SEVERITY_CLEAR
     31
     32  from Products.ZenRRD.zencommand import Cmd, ProcessRunner, TimeoutError
     33  from Products.ZenCollector import interfaces
     34  from Products.ZenCollector.tasks import TaskStates, BaseTask
     35
     36  from Products.ZenUtils.Utils import unused
     37  from Products.ZenCollector.services.config import DeviceProxy
     38  unused(DeviceProxy)
     39
     40  from Products.ZenEvents.ZenEventClasses import Status_Ping
     41  from Products.ZenEvents import Event
     42  from Products.ZenEvents import ZenEventClasses
     43  from zenoss.protocols.protobufs import zep_pb2 as events
     44
     45  from Products.ZenUtils.IpUtil import ipunwrap
     46  from interfaces import IPingTask
     47
     48  COLLECTOR_NAME = "zenping"
     49
     50  STATUS_EVENT = {
     51      'eventClass' : Status_Ping,
     52      'component' : 'zenping',
     53  '    eventGroup' : 'Ping'
     54  }
     55  SUPPRESSED = 2
     56  _NAN = float('nan')
     57
     58 -class PingTask(BaseTask):
     59      interface.implements(IPingTask)
     60
     61      STATE_PING_START = 'PING_START'
     62      STATE_PING_STOP  = 'PING_STOP'
     63      STATE_STORE_PERF = 'STORE_PERF_DATA'
     64
     65 -    def __init__(self, taskName, deviceId, scheduleIntervalSeconds, taskConfig):
     66          """
     67          @param deviceId: the Zenoss deviceId to watch
     68          @type deviceId: string
     69          @param taskName: the unique identifier for this task
     70          @type taskName: string
     71          @param scheduleIntervalSeconds: the interval at which this task will be
     72                 collected
     73          @type scheduleIntervalSeconds: int
     74          @param taskConfig: the configuration for this task
     75          """
     76          super(PingTask, self).__init__(
     77                taskName, deviceId,
     78                scheduleIntervalSeconds, taskConfig
     79                )
     80
     81          # Needed for interface
     82          self.name = taskName
     83          self.configId = deviceId
     84          self.state = TaskStates.STATE_IDLE
     85
     86          # The taskConfig corresponds to a DeviceProxy
     87          self._device = taskConfig
     88          self._devId = deviceId
     89          self._manageIp = ipunwrap(self._device.manageIp)
     90          self.interval = scheduleIntervalSeconds
     91          self._pingResult = None
     92
     93          self._trace = tuple()
     94          self._isUp = None
     95          self._daemon = component.queryUtility(interfaces.ICollector)
     96          self._dataService = component.queryUtility(interfaces.IDataService)
     97          self._eventService = component.queryUtility(interfaces.IEventService)
     98          self._preferences = component.queryUtility(interfaces.ICollectorPreferences,
     99                                                          COLLECTOR_NAME)
    100          # Split up so that every interface's IP gets its own ping job
    101          self.config = self._device.monitoredIps[0]
    102          self._iface = self.config.iface
    103          self._lastErrorMsg = ''
    104
    105          # by defautl don't pause after schedule
    106          self.pauseOnScheduled = False
    107          self._rtt =[]
    108
    109 -    def doTask(self):
    110          """
    111          Contact to one device and return a deferred which gathers data from
    112          the device.
    113
    114          @return: A task to ping the device and any of its interfaces.
    115          @rtype: Twisted deferred object
    116          """
    117          raise NotImplementedError()
    118
    119
    120 -    def _getPauseOnScheduled(self):
    121          return self._pauseOnScheduled
    122
    123 -    def _setPauseOnScheduled(self, value):
    124          self._pauseOnScheduled = value
    125
    126      pauseOnScheduled = property(fget=_getPauseOnScheduled, fset=_setPauseOnScheduled)
    127      """Pause this task after it's been scheduled."""
    128
    129 -    def scheduled(self, scheduler):
    130          """
    131          After the task has been scheduled, set the task in to the PAUSED state.
    132
    133          @param scheduler: Collection Framework Scheduler
    134          @type scheduler: IScheduler
    135          """
    136          if self.pauseOnScheduled:
    137              scheduler.pauseTasksForConfig(self.configId)
    138
    139      @property
    140 -    def trace(self):
    141          return self._trace
    142
    143      @property
    144 -    def isUp(self):
    145          """
    146          Determine if the device is up
    147          """
    148          return self._calculateState()
    149
    150 -    def _calculateState(self):
    151          """
    152          Calculate if the device is up or down based on current ping statistics.
    153          Return None if unknown, False if down, and True if up.
    154          """
    155
    156          # if there is not enough data to calulate return unknown
    157          if len(self._rtt) <= 0:
    158              return None
    159
    160          lostPackets = len([ rtt for rtt in self._rtt if math.isnan(rtt)])
    161          totalPackets = len(self._rtt)
    162          receivedPackets = totalPackets - lostPackets
    163
    164          isUp = receivedPackets > 0
    165          return isUp
    166
    167 -    def resetPingResult(self):
    168          """
    169          Clear out current ping statistics.
    170          """
    171          self._rtt =[]
    172
    173 -    def logPingResult(self, pingResult):
    174          """
    175          Log the PingResult; set ping state, log to rrd.
    176          """
    177          if pingResult is None:
    178              raise ValueError("pingResult can not be None")
    179          self._rtt.append(pingResult.rtt)
    180          if pingResult.trace:
    181              self._trace = pingResult.trace
    182
    183 -    def sendPingEvent(self, msgTpl, severity, rootCause=None):
    184          """
    185          Send an event based on a ping job to the event backend.
    186          """
    187          msg = msgTpl % self._devId
    188          evt = dict(
    189              device=self._devId,
    190              ipAddress=self.config.ip,
    191              summary=msg,
    192              severity=severity,
    193              eventClass=ZenEventClasses.Status_Ping,
    194              eventGroup='Ping',
    195              component=self._iface,
    196          )
    197
    198          if self._pingResult is not None:
    199              # explicitly set the event time based on the ping collection's time
    200              if self._pingResult.timestamp:
    201                  evt['lastTime'] = evt['firstTime'] = self._pingResult.timestamp
    202              # include the last traceroute we know of this is not a Clear
    203              if severity and self._pingResult.trace:
    204                  evt['lastTraceroute'] = str(self._pingResult.trace)
    205
    206          if rootCause:
    207              evt['rootCause'] = rootCause
    208              evt['eventState'] = SUPPRESSED
    209
    210          # mark this event with a flag if it applies to the managedIp component
    211          if self.config.ip == self._manageIp:
    212              evt['isManageIp'] = True
    213
    214          self._eventService.sendEvent(evt)
    215
    216          # ZEN-1584: if this proxy is for a component
    217          # that handles the manageIp, send a device level clear
    218          if severity==events.SEVERITY_CLEAR and \
    219              'isManageIp' in evt and evt['component']:
    220              evt['component'] = ''
    221              self._eventService.sendEvent(evt)
    222
    223 -    def sendPingUp(self, msgTpl='%s is UP!'):
    224          """
    225          Send an ping up event to the event backend.
    226          """
    227          return self.sendPingEvent(msgTpl, events.SEVERITY_CLEAR)
    228
    229 -    def sendPingDown(self, msgTpl='%s is DOWN!', rootCause=None):
    230          """
    231          Send an ping down event to the event backend.
    232          """
    233          return self.sendPingEvent(msgTpl, events.SEVERITY_CRITICAL, rootCause)
    234
    235 -    def storeResults(self):
    236          """
    237          Store the datapoint results asked for by the RRD template.
    238          """
    239          if len(self._rtt) == 0:
    240              return
    241
    242          # strip out NAN's
    243          rtts = [ rtt for rtt in self._rtt if math.isnan(rtt) == False ]
    244          if rtts:
    245              received = len(rtts)
    246              pingCount = len(self._rtt)
    247              minRtt = min(rtts)
    248              maxRtt = max(rtts)
    249              avgRtt = sum(rtts) / received
    250              varianceRtt = sum([ math.pow(rtt - avgRtt, 2) for rtt in rtts ]) / received
    251              stddevRtt =  math.sqrt(varianceRtt)
    252              pingLoss = 100.0
    253              if pingCount > 0 :
    254                  pingLoss = (1 - (len(rtts) / pingCount)) * 100.0
    255
    256              datapoints = {
    257                  'rtt' : avgRtt,
    258                  'rtt_avg' : avgRtt,
    259                  'rtt_min' : minRtt,
    260                  'rtt_max' : maxRtt,
    261                  'rtt_losspct': pingLoss,
    262                  'rtt_stddev': stddevRtt,
    263                  'rcvCount': received,
    264              }
    265          else:
    266              pingLoss = 100
    267              datapoints = {
    268                  'rtt_losspct': pingLoss,
    269              }
    270
    271          for rrdMeta in self.config.points:
    272              name, path, rrdType, rrdCommand, rrdMin, rrdMax = rrdMeta
    273              value = datapoints.get(name, None)
    274              if value is None:
    275                  log.debug("No datapoint '%s' found on the %s pingTask",
    276                            name, self)
    277              else:
    278                  self._dataService.writeRRD(
    279                      path, value, rrdType,
    280                      rrdCommand=rrdCommand,
    281                      min=rrdMin, max=rrdMax,
    282                  )
    283