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

# Source Code for Module Products.ZenStatus.ZenTcpClient

      1  ###########################################################################
      2  #
      3  # This program is part of Zenoss Core, an open source monitoring platform.
      4  # Copyright (C) 2007, 2009 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  __doc__ = """ZenTcpClient
     14  Connect to the remote service and (optionally) test the output from
     15  the service against what we expect.
     16
     17  Error types:
     18
     19      1. timeout (no connection)
     20      2. connection refused - port not available on remote end
     21      3. bad value - value returned did not match expectRegex
     22
     23  """
     24  import re
     25  import logging
     26  log = logging.getLogger("zen.ZenTcpClient")
     27  from socket import getfqdn
     28  hostname = getfqdn()
     29
     30  from twisted.internet import reactor, protocol, defer
     31  from Products.ZenEvents.ZenEventClasses import Status_IpService
     32  from Products.ZenUtils.Utils import unused
     33
     34
     35  # needed for pb/jelly
     36  from Products.ZenHub.services.StatusConfig import ServiceConfig
     37  unused(ServiceConfig)
     38
     39 -class ZenTcpTest(protocol.Protocol):
     40      """
     41      Twisted class to make a TCP/IP connection to a remote IP service
     42      and report back the result.
     43      """
     44      defer = None
     45      data = ""
     46
     47 -    def connectionMade(self):
     48          """
     49          Connected successfully to the remote device, now test against any
     50          regex that we might have and record the result.
     51          """
     52          log.debug("Connected to %s" % self.transport.getPeer().host)
     53          self.factory.msg = "pass"
     54          self.cfg = self.factory.cfg
     55
     56          if self.cfg.sendString:
     57              sendString = self.cfg.sendString.decode("string_escape")
     58              log.debug("Sending: %s" % sendString)
     59              self.transport.write(sendString)
     60
     61          if self.cfg.expectRegex:
     62              log.debug("Waiting for results to check against regex '%s'" % (
     63                        self.cfg.expectRegex ))
     64              self.defer = reactor.callLater(self.cfg.timeout, self.expectTimeout)
     65          else:
     66              self.loseConnection()
     67
     68
     69 -    def dataReceived(self, data):
     70          """
     71          Compare the data from the remote device to what we expect in the
     72          regex.
     73
     74          @parameter data: output from remote service
     75          @type data: string
     76          """
     77          log.debug("%s %s received data: %s" % (self.cfg.device,
     78                    self.cfg.component, data))
     79          self.data += data
     80          if self.cfg.expectRegex:
     81              if re.search(self.cfg.expectRegex, data):
     82                  log.debug("Found %s in '%s' -- closing connection" % (
     83                            self.cfg.expectRegex, data))
     84                  self.loseConnection()
     85              else:
     86                  log.debug("No match for %s in '%s' -- looking for more data" % (
     87                            self.cfg.expectRegex, data))
     88
     89
     90 -    def expectTimeout(self):
     91          """
     92          Called if we timeout waiting for the service to connect or for
     93          receiving a response from the service that matches our regex.
     94          """
     95          msg = "IP Service %s TIMEOUT waiting for '%s'" % (
     96                      self.cfg.component, self.cfg.expectRegex)
     97          log.debug( "%s %s" % (self.cfg.ip, msg) )
     98          self.factory.msg = msg
     99          self.loseConnection()
    100
    101
    102 -    def loseConnection(self):
    103          """
    104          Shut down the connection and cleanup.
    105          """
    106          ip, port = self.transport.addr
    107          log.debug("Closed connection to %s on port %s for %s" % (
    108                    ip, port, self.cfg.component))
    109          self.data = ""
    110          try:
    111              self.defer.cancel()
    112          except:
    113              self.defer = None
    114          self.transport.loseConnection()
    115
    116
    117
    118 -class ZenTcpClient(protocol.ClientFactory):
    119      """
    120      Client class to run TCP tests.
    121      """
    122      protocol = ZenTcpTest
    123      msg = "pass"
    124      deferred = None
    125
    126 -    def __init__(self, svc, status):
    127          self.cfg = svc
    128          self.status = status
    129
    130 -    def clientConnectionLost(self, connector, reason):
    131          """
    132          Record why the connection to the remote device was dropped.
    133
    134          @parameter connector: Twisted protocol object
    135          @type connector: Twisted protocol object
    136          @parameter reason: explanation for the connection loss
    137          @type reason: Twisted error object
    138          """
    139          unused(connector)
    140          log.debug("Lost connection to %s (%s) port %s : %s" % (
    141                    self.cfg.device, self.cfg.ip, self.cfg.port,
    142                    reason.getErrorMessage() ))
    143          if self.deferred:
    144              self.deferred.callback(self)
    145          self.deferred = None
    146
    147
    148 -    def clientConnectionFailed(self, connector, reason):
    149          """
    150          Record why the connection to the remote device failed.
    151
    152          @parameter connector: Twisted protocol object
    153          @type connector: Twisted protocol object
    154          @parameter reason: explanation for the connection loss
    155          @type reason: Twisted error object
    156          """
    157          unused(connector)
    158          log.debug("Connection to %s (%s) port %s  failed: %s" % (
    159                    self.cfg.device, self.cfg.ip, self.cfg.port,
    160                    reason.getErrorMessage() ))
    161          log.debug(reason.type)
    162          self.msg = "IP Service %s is down" % self.cfg.component
    163          if self.deferred:
    164              self.deferred.callback(self)
    165          self.deferred = None
    166
    167
    168 -    def getEvent(self):
    169          """
    170          Called by zenstatus to report status information about a service.
    171
    172          @return: event of what happened to our service test
    173          @rtype: dictionary
    174          """
    175          log.debug("status:%s msg:%s", self.status, self.msg)
    176          if self.msg == "pass" and self.status > 0:
    177              self.status = sev = 0
    178              self.msg = "IP Service %s back up" % self.cfg.component
    179              log.info(self.msg)
    180
    181          elif self.msg != "pass":
    182              self.status += 1
    183              sev = self.cfg.failSeverity
    184              log.warn(self.msg)
    185
    186          else:
    187              # Don't send an event as there's no problem and
    188              # nothing to clear.
    189              return None
    190
    191          return dict(device=self.cfg.device,
    192                      component=self.cfg.component,
    193                      ipAddress=self.cfg.ip,
    194                      summary=self.msg,
    195                      severity=sev,
    196                      eventClass=Status_IpService,
    197                      eventGroup="TCPTest",
    198                      agent="ZenStatus",
    199                      manager=hostname)
    200
    201 -    def start(self):
    202          """
    203          Called by zenstatus to make a connection attempt to the service.
    204
    205          @return: Twisted deferred
    206          @rtype: Twisted deferred
    207          """
    208          d = self.deferred = defer.Deferred()
    209          reactor.connectTCP(self.cfg.ip, self.cfg.port, self, self.cfg.timeout)
    210          return d
    211