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