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 or (at your
      8  # option) any later version as published by 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 -class ZenTcpTest(protocol.Protocol):
     35      """
     36      Twisted class to make a TCP/IP connection to a remote IP service
     37      and report back the result.
     38      """
     39      defer = None
     40      data = ""
     41
     42 -    def connectionMade(self):
     43          """
     44          Connected successfully to the remote device, now test against any
     45          regex that we might have and record the result.
     46          """
     47          log.debug("Connected to %s" % self.transport.getPeer().host)
     48          self.factory.msg = "pass"
     49          self.cfg = self.factory.cfg
     50
     51          if self.cfg.sendString:
     52              sendString = self.cfg.sendString.decode("string_escape")
     53              log.debug("Sending: %s", sendString)
     54              self.transport.write(sendString)
     55
     56          if self.cfg.expectRegex:
     57              log.debug("Waiting for results to check against regex '%s'",
     58                        self.cfg.expectRegex)
     59              self.defer = reactor.callLater(self.cfg.timeout, self.expectTimeout)
     60          else:
     61              self.loseConnection()
     62
     63 -    def dataReceived(self, data):
     64          """
     65          Compare the data from the remote device to what we expect in the
     66          regex.
     67
     68          @parameter data: output from remote service
     69          @type data: string
     70          """
     71          log.debug("%s %s received data: %s", self.cfg.device,
     72                    self.cfg.component, data)
     73          self.data += data
     74          if self.cfg.expectRegex:
     75              if re.search(self.cfg.expectRegex, data):
     76                  log.debug("Found %s in '%s' -- closing connection",
     77                            self.cfg.expectRegex, data)
     78                  self.loseConnection()
     79              else:
     80                  log.debug("No match for %s in '%s' -- looking for more data",
     81                            self.cfg.expectRegex, data)
     82
     83 -    def expectTimeout(self):
     84          """
     85          Called if we timeout waiting for the service to connect or for
     86          receiving a response from the service that matches our regex.
     87          """
     88          msg = "IP Service %s TIMEOUT waiting for '%s'" % (
     89                      self.cfg.component, self.cfg.expectRegex)
     90          log.debug("%s %s", self.cfg.ip, msg)
     91          self.factory.msg = msg
     92          self.loseConnection()
     93
     94 -    def loseConnection(self):
     95          """
     96          Shut down the connection and cleanup.
     97          """
     98          ip, port = self.transport.addr
     99          log.debug("Closed connection to %s on port %s for %s",
    100                    ip, port, self.cfg.component)
    101          self.data = ""
    102          try:
    103              self.defer.cancel()
    104          except:
    105              self.defer = None
    106          self.transport.loseConnection()
    107
    108
    109 -class ZenTcpClient(protocol.ClientFactory):
    110      """
    111      Client class to run TCP tests.
    112      """
    113      protocol = ZenTcpTest
    114      msg = "pass"
    115      deferred = None
    116
    117 -    def __init__(self, svc, status):
    118          self.cfg = svc
    119          self.status = status
    120
    121 -    def clientConnectionLost(self, connector, reason):
    122          """
    123          Record why the connection to the remote device was dropped.
    124
    125          @parameter connector: Twisted protocol object
    126          @type connector: Twisted protocol object
    127          @parameter reason: explanation for the connection loss
    128          @type reason: Twisted error object
    129          """
    130          unused(connector)
    131          errorMsg = reason.getErrorMessage()
    132          if errorMsg != 'Connection was closed cleanly.':
    133              log.debug("Lost connection to %s (%s) port %s: %s",
    134                    self.cfg.device, self.cfg.ip, self.cfg.port,
    135                    reason.getErrorMessage() )
    136          if self.deferred:
    137              self.deferred.callback(self)
    138          self.deferred = None
    139
    140 -    def clientConnectionFailed(self, connector, reason):
    141          """
    142          Record why the connection to the remote device failed.
    143
    144          @parameter connector: Twisted protocol object
    145          @type connector: Twisted protocol object
    146          @parameter reason: explanation for the connection loss
    147          @type reason: Twisted error object
    148          """
    149          log.debug("Connection to %s (%s) port %s failed: %s",
    150                    self.cfg.device, connector.host, self.cfg.port,
    151                    reason.getErrorMessage() )
    152          self.msg = "IP Service %s is down" % self.cfg.component
    153          if self.deferred:
    154              self.deferred.callback(self)
    155          self.deferred = None
    156
    157 -    def getEvent(self):
    158          """
    159          Called by zenstatus to report status information about a service.
    160
    161          @return: event of what happened to our service test
    162          @rtype: dictionary
    163          """
    164          if self.msg == "pass" and self.status > 0:
    165              self.status = sev = 0
    166              self.msg = "IP Service %s back up" % self.cfg.component
    167
    168          elif self.msg != "pass":
    169              self.status += 1
    170              sev = self.cfg.failSeverity
    171
    172          else:
    173              # Send an event even though there's no problem and
    174              # nothing to clear __internally__ to zenstatus.
    175              # The event console might have an event that will
    176              # otherwise never be cleared.
    177              # Code higher up discards duplicate clears.
    178              self.status = sev = 0
    179              self.msg = "IP Service %s back up" % self.cfg.component
    180
    181          return dict(device=self.cfg.device,
    182                      component=self.cfg.component,
    183                      ipAddress=self.cfg.ip,
    184                      summary=self.msg,
    185                      severity=sev,
    186                      eventClass=Status_IpService,
    187                      eventGroup="TCPTest",
    188                      agent="ZenStatus",
    189                      manager=hostname)
    190
    191 -    def start(self, ip_address):
    192          """
    193          Called by zenstatus to make a connection attempt to the service.
    194
    195          @return: Twisted deferred
    196          @rtype: Twisted deferred
    197          """
    198          d = self.deferred = defer.Deferred()
    199          reactor.connectTCP(ip_address, self.cfg.port, self, self.cfg.timeout)
    200          return d
    201