Package Products :: Package ZenUtils :: Module captureReplay |
[hide private]
[frames] | no frames]

# Source Code for Module Products.ZenUtils.captureReplay

      1  #! /usr/bin/env python
      2  # -*- coding: utf-8 -*-
      3  # ##########################################################################
      4  #
      5  # This program is part of Zenoss Core, an open source monitoring platform.
      6  # Copyright (C) 2009 Zenoss Inc.
      7  #
      8  # This program is free software; you can redistribute it and/or modify it
      9  # under the terms of the GNU General Public License version 2 or (at your
     10  # option) any later version as published by the Free Software Foundation.
     11  #
     12  # For complete information please visit: http://www.zenoss.com/oss/
     13  #
     14  # ##########################################################################
     15
     16  __doc__ = """captureReplay
     17      Common code to capture and replay packets.
     18
     19      To use:
     20  1. Add the captureReplay mixin to the list of base classes
     21
     22  2. Add the following to the buildOptions method of the base class, after other
     23     initialization:
     24      captureReplay.buildOptions()
     25
     26  3. Add the following to the __init__ of the base class, before any other
     27     option processing:
     28     self.processCaptureReplayOptions()
     29
     30  4. Define a convertPacketToPython() method to convert a 'raw' packet into a
     31     Python serializable object.
     32
     33  5. Add a call to the capturePacket() method to capture the packet.
     34
     35  6. Define a replay() method to replay the packet.
     36  """
     37
     38  import sys
     39  import cPickle
     40  from exceptions import EOFError, IOError
     41  import glob
     42
     43  import Globals
     44  from twisted.internet import defer, reactor
     45  from Products.ZenUtils.Timeout import timeout
     46  from Products.ZenEvents.ZenEventClasses import Error, Warning, Info, \
     47      Debug
     48
     49  from twisted.python import failure
     50
     51 -class FakePacket(object):
     52      """
     53      A fake object to make packet replaying feasible.
     54      """
     55 -    def __init__(self):
     56          self.fake = True
     57
     58
     59 -class CaptureReplay(object):
     60      """
     61      Base class for packet capture and replay capability.
     62      Assumes that the other classes provide the following:
     63      self.buildOptions()
     64      self.sendEvent()
     65
     66      Overrides the self.connected() method if called to replay packets.
     67      """
     68
     69 -    def processCaptureReplayOptions(self):
     70          """
     71          Inside of the initializing class, call these functions first.
     72          """
     73          if self.options.captureFilePrefix and len(self.options.replayFilePrefix) > 0:
     74              self.log.error( "Can't specify both --captureFilePrefix and -replayFilePrefix" \
     75                   " at the same time.  Exiting" )
     76              sys.exit(1)
     77
     78          if self.options.captureFilePrefix and not self.options.captureAll and \
     79              self.options.captureIps == '':
     80              self.log.warn( "Must specify either --captureIps or --captureAll for" + \
     81                   " --capturePrefix to take effect.  Ignoring option --capturePrefix" )
     82
     83          if len(self.options.replayFilePrefix) > 0:
     84              self.connected = self.replayAll
     85              return
     86
     87          self.captureSerialNum = 0
     88          self.captureIps = self.options.captureIps.split(',')
     89
     90 -    def convertPacketToPython(*packetInfo):
     91          """
     92          Convert arguments into an plain object (no functions) suitable
     93          for pickling.
     94          """
     95          pass
     96
     97 -    def capturePacket(self, hostname, *packetInfo):
     98          """
     99          Store the raw packet for later examination and troubleshooting.
    100
    101          @param hostname: packet-sending host's name or IP address
    102          @type hostname: string
    103          @param packetInfo: raw packet and other necessary arguments
    104          @type packetInfo: args
    105          """
    106          # Save the raw data if requested to do so
    107          if not self.options.captureFilePrefix:
    108              return
    109          if not self.options.captureAll and hostname not in self.captureIps:
    110              self.log.debug( "Received packet from %s, but not in %s" % (hostname,
    111                              self.captureIps))
    112              return
    113
    114          self.log.debug( "Capturing packet from %s" % hostname )
    115          name = "%s-%s-%d" % (self.options.captureFilePrefix, hostname, self.captureSerialNum)
    116          try:
    117              packet = self.convertPacketToPython(*packetInfo)
    118              capFile = open( name, "wb")
    119              data= cPickle.dumps(packet, cPickle.HIGHEST_PROTOCOL)
    120              capFile.write(data)
    121              capFile.close()
    122              self.captureSerialNum += 1
    123          except:
    124              self.log.exception("Couldn't write capture data to '%s'" % name )
    125
    126 -    def replayAll(self):
    127          """
    128          Replay all captured packets using the files specified in
    129          the --replayFilePrefix option and then exit.
    130
    131          Note that this calls the Twisted stop() method
    132          """
    133          if hasattr(self, 'configure'):
    134              d = self.configure()
    135              d.addCallback(self._replayAll)
    136          else:
    137              self._replayAll()
    138
    139 -    def _replayAll(self, ignored):
    140          # Note what you are about to see below is a direct result of optparse
    141          # adding in the arguments *TWICE* each time --replayFilePrefix is used.
    142          files = []
    143          for filespec in self.options.replayFilePrefix:
    144              files += glob.glob( filespec + '*' )
    145
    146          self.loaded = 0
    147          self.replayed = 0
    148          for file in set(files):
    149              self.log.debug( "Attempting to read packet data from '%s'" % file )
    150              try:
    151                  fp = open( file, "rb" )
    152                  packet= cPickle.load(fp)
    153                  fp.close()
    154                  self.loaded += 1
    155
    156              except (IOError, EOFError):
    157                  fp.close()
    158                  self.log.exception( "Unable to load packet data from %s" % file )
    159                  continue
    160
    161              self.log.debug("Calling application-specific replay() method")
    162              self.replay(packet)
    163
    164          self.replayStop()
    165
    166 -    def replay(self, packet):
    167          """
    168          Replay a captured packet.  This must be overridden.
    169
    170          @param packet: raw packet
    171          @type packet: binary
    172          """
    173          pass
    174
    175 -    def replayStop(self):
    176          """
    177          Twisted method that we use to override the default stop() method
    178          for when we are replaying packets.  This version waits to make
    179          sure that all of our deferreds have exited before pulling the plug.
    180          """
    181          self.log.debug("Event queue size = %d", len(self.eventQueue))
    182          if self.replayed == self.loaded and not self.eventQueue:
    183              self.log.info("Loaded and replayed %d packets" % self.replayed)
    184              self.stop()
    185          else:
    186              reactor.callLater(1, self.replayStop)
    187
    188 -    def buildCaptureReplayOptions(self, parser):
    189          """
    190          This should be called explicitly in the base class' buildOptions
    191          """
    192          parser.add_option('--captureFilePrefix',
    193                                 dest='captureFilePrefix',
    194                                 default=None,
    195                                 help="Directory and filename to use as a template" + \
    196                                 "  to store captured raw trap packets.")
    197          parser.add_option('--captureAll',
    198                                 dest='captureAll',
    199                                 action='store_true',
    200                                 default=False,
    201                                 help="Capture all packets.")
    202          parser.add_option('--captureIps',
    203                                 dest='captureIps',
    204                                 default='',
    205                                 help="Comma-separated list of IP addresses to capture.")
    206          parser.add_option('--replayFilePrefix',
    207                                 dest='replayFilePrefix',
    208                                 action='append',
    209                                 default=[],
    210               help="Filename prefix containing captured packet data. Can specify more than once.")
    211