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
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
51 """
52 A fake object to make packet replaying feasible.
53 """
56
57
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
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
92 """
93 Convert arguments into an plain object (no functions) suitable
94 for pickling.
95 """
96 pass
97
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
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
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
143
144
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
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
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
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