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 as published by
     10  # 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
     42  import Globals
     43  from twisted.internet import defer, reactor
     44  from Products.ZenUtils.Timeout import timeout
     45  from Products.ZenEvents.ZenEventClasses import Error, Warning, Info, \
     46      Debug
     47
     48  from twisted.python import failure
     49
     50 -class FakePacket(object):
     51      """
     52      A fake object to make packet replaying feasible.
     53      """
     54 -    def __init__(self):
     55          self.fake = True
     56
     57
     58 -class CaptureReplay(object):
     59      """
     60      Base class for packet capture and replay capability.
     61      Assumes that the other classes provide the following:
     62      self.buildOptions()
     63      self.sendEvent()
     64
     65      Overrides the self.connected() method if called to replay packets.
     66      """
     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
     91 -    def convertPacketToPython(*packetInfo):
     92          """
     93          Convert arguments into an plain object (no functions) suitable
     94          for pickling.
     95          """
     96          pass
     97
     98 -    def capturePacket(self, hostname, *packetInfo):
     99          """
    100          Store the raw packet for later examination and troubleshooting.
    101
    102          @param hostname: packet-sending host's name or IP address
    103          @type hostname: string
    104          @param packetInfo: raw packet and other necessary arguments
    105          @type packetInfo: args
    106          """
    107          # Save the raw data if requested to do so
    108          if not self.options.captureFilePrefix:
    109              return
    110          if not self.options.captureAll and host not in self.captureIps:
    111              self.log.debug( "Received packet from %s, but not in %s" % (host,
    112                              self.captureIps))
    113              return
    114
    115          self.log.debug( "Capturing packet from %s" % hostname )
    116          name = "%s-%s-%d" % (self.options.captureFilePrefix, hostname, self.captureSerialNum)
    117          try:
    118              packet = self.convertPacketToPython(*packetInfo)
    119              capFile = open( name, "wb")
    120              data= cPickle.dumps(packet, cPickle.HIGHEST_PROTOCOL)
    121              capFile.write(data)
    122              capFile.close()
    123              self.captureSerialNum += 1
    124          except:
    125              self.log.exception("Couldn't write capture data to '%s'" % name )
    126
    127
    128 -    def replayAll(self):
    129          """
    130          Replay all captured packets using the files specified in
    131          the --replayFilePrefix option and then exit.
    132
    133          Note that this calls the Twisted stop() method
    134          """
    135
    136          if hasattr(self, 'configure'):
    137              d = self.configure()
    138              d.addCallback(self._replayAll)
    139          else:
    140              self._replayAll()
    141
    142 -    def _replayAll(self, ignored):
    143          # Note what you are about to see below is a direct result of optparse
    144          # adding in the arguments *TWICE* each time --replayFilePrefix is used.
    145          import glob
    146          files = []
    147          for filespec in self.options.replayFilePrefix:
    148              files += glob.glob( filespec + '*' )
    149
    150          self.loaded = 0
    151          self.replayed = 0
    152          from sets import Set
    153          for file in Set(files):
    154              self.log.debug( "Attempting to read packet data from '%s'" % file )
    155              try:
    156                  fp = open( file, "rb" )
    157                  packet= cPickle.load(fp)
    158                  fp.close()
    159                  self.loaded += 1
    160
    161              except (IOError, EOFError):
    162                  fp.close()
    163                  self.log.exception( "Unable to load packet data from %s" % file )
    164                  continue
    165
    166              self.log.debug("Calling application-specific replay() method")
    167              self.replay(packet)
    168
    169          self.replayStop()
    170
    171
    172 -    def replay(self, packet):
    173          """
    174          Replay a captured packet.  This must be overridden.
    175
    176          @param packet: raw packet
    177          @type packet: binary
    178          """
    179          pass
    180
    181
    182 -    def replayStop(self):
    183          """
    184          Twisted method that we use to override the default stop() method
    185          for when we are replaying packets.  This version waits to make
    186          sure that all of our deferreds have exited before pulling the plug.
    187          """
    188          self.log.debug("Event queue size = %d", len(self.eventQueue))
    189          if self.replayed == self.loaded and not self.eventQueue:
    190              self.log.info("Loaded and replayed %d packets" % self.replayed)
    191              self.stop()
    192          else:
    193              reactor.callLater(1, self.replayStop)
    194
    195
    196 -    def buildCaptureReplayOptions(self):
    197          """
    198          This should be called explicitly in the base class' buildOptions
    199          """
    200          self.parser.add_option('--captureFilePrefix',
    201                                 dest='captureFilePrefix',
    202                                 default=None,
    203                                 help="Directory and filename to use as a template" + \
    204                                 "  to store captured raw trap packets.")
    205          self.parser.add_option('--captureAll',
    206                                 dest='captureAll',
    207                                 action='store_true',
    208                                 default=False,
    209                                 help="Capture all packets.")
    210          self.parser.add_option('--captureIps',
    211                                 dest='captureIps',
    212                                 default='',
    213                                 help="Comma-separated list of IP addresses to capture.")
    214          self.parser.add_option('--replayFilePrefix',
    215                                 dest='replayFilePrefix',
    216                                 action='append',
    217                                 default=[],
    218               help="Filename prefix containing captured packet data. Can specify more than once.")
    219