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

# Source Code for Module Products.ZenStatus.ZenTcpClient

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