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 reactor
44 from Products.ZenUtils.Driver import drive
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
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
137 import glob
138 files = []
139 for filespec in self.options.replayFilePrefix:
140 files += glob.glob( filespec + '*' )
141
142 self.loaded = 0
143 self.replayed = 0
144 from sets import Set
145 for file in Set(files):
146 self.log.debug( "Attempting to read packet data from '%s'" % file )
147 try:
148 fp = open( file, "rb" )
149 packet= cPickle.load(fp)
150 fp.close()
151 self.loaded += 1
152
153 except (IOError, EOFError):
154 fp.close()
155 self.log.exception( "Unable to load packet data from %s" % file )
156 continue
157
158 self.log.debug("Calling application-specific replay() method")
159 self.replay(packet)
160
161 self.replayStop()
162
163
165 """
166 Replay a captured packet. This must be overridden.
167
168 @param packet: raw packet
169 @type packet: binary
170 """
171 pass
172
173
175 """
176 Twisted method that we use to override the default stop() method
177 for when we are replaying packets. This version waits to make
178 sure that all of our deferreds have exited before pulling the plug.
179 """
180 self.log.debug( "Replayed %d of %d packets" % (self.replayed, self.loaded ) )
181 if self.replayed == self.loaded:
182 self.log.info( "Loaded and replayed %d packets" % self.replayed )
183 self.stop()
184 else:
185 reactor.callLater( 1, self.replayStop )
186
187
189 """
190 This should be called explicitly in the base class' buildOptions
191 """
192 self.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 self.parser.add_option('--captureAll',
198 dest='captureAll',
199 action='store_true',
200 default=False,
201 help="Capture all packets.")
202 self.parser.add_option('--captureIps',
203 dest='captureIps',
204 default='',
205 help="Comma-separated list of IP addresses to capture.")
206 self.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