1
2
3
4
5
6
7
8
9
10
11
12
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
52 """
53 A fake object to make packet replaying feasible.
54 """
57
58
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
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
91 """
92 Convert arguments into an plain object (no functions) suitable
93 for pickling.
94 """
95 pass
96
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
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
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
140
141
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
167 """
168 Replay a captured packet. This must be overridden.
169
170 @param packet: raw packet
171 @type packet: binary
172 """
173 pass
174
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
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