Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(250)

Side by Side Diff: third_party/pexpect/examples/topip.py

Issue 1398903002: Add third_party/pexpect (Closed) Base URL: https://chromium.googlesource.com/crashpad/crashpad@end-to-end-test
Patch Set: Created 5 years, 2 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « third_party/pexpect/examples/table_test.html ('k') | third_party/pexpect/examples/uptime.py » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 #!/usr/bin/env python
2
3 ''' This runs netstat on a local or remote server. It calculates some simple
4 statistical information on the number of external inet connections. It groups
5 by IP address. This can be used to detect if one IP address is taking up an
6 excessive number of connections. It can also send an email alert if a given IP
7 address exceeds a threshold between runs of the script. This script can be used
8 as a drop-in Munin plugin or it can be used stand-alone from cron. I used this
9 on a busy web server that would sometimes get hit with denial of service
10 attacks. This made it easy to see if a script was opening many multiple
11 connections. A typical browser would open fewer than 10 connections at once.
12 A script might open over 100 simultaneous connections.
13
14 ./topip.py [-s server_hostname] [-u username] [-p password]
15 {-a from_addr,to_addr} {-n N} {-v} {--ipv6}
16
17 -s : hostname of the remote server to login to.
18 -u : username to user for login.
19 -p : password to user for login.
20 -n : print stddev for the the number of the top 'N' ipaddresses.
21 -v : verbose - print stats and list of top ipaddresses.
22 -a : send alert if stddev goes over 20.
23 -l : to log message to /var/log/topip.log
24 --ipv6 : this parses netstat output that includes ipv6 format.
25 Note that this actually only works with ipv4 addresses, but for
26 versions of netstat that print in ipv6 format.
27 --stdev=N : Where N is an integer. This sets the trigger point
28 for alerts and logs. Default is to trigger if the
29 max value is over 5 standard deviations.
30
31 Example:
32
33 This will print stats for the top IP addresses connected to the given host:
34
35 ./topip.py -s www.example.com -u mylogin -p mypassword -n 10 -v
36
37 This will send an alert email if the maxip goes over the stddev trigger
38 value and the the current top ip is the same as the last top ip
39 (/tmp/topip.last):
40
41 ./topip.py -s www.example.com -u mylogin -p mypassword \\
42 -n 10 -v -a alert@example.com,user@example.com
43
44 This will print the connection stats for the localhost in Munin format:
45
46 ./topip.py
47
48 PEXPECT LICENSE
49
50 This license is approved by the OSI and FSF as GPL-compatible.
51 http://opensource.org/licenses/isc-license.txt
52
53 Copyright (c) 2012, Noah Spurrier <noah@noah.org>
54 PERMISSION TO USE, COPY, MODIFY, AND/OR DISTRIBUTE THIS SOFTWARE FOR ANY
55 PURPOSE WITH OR WITHOUT FEE IS HEREBY GRANTED, PROVIDED THAT THE ABOVE
56 COPYRIGHT NOTICE AND THIS PERMISSION NOTICE APPEAR IN ALL COPIES.
57 THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
58 WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
59 MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
60 ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
61 WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
62 ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
63 OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
64
65 '''
66
67 from __future__ import absolute_import
68 from __future__ import print_function
69 from __future__ import unicode_literals
70
71 # See http://pexpect.sourceforge.net/
72 import pexpect
73 import pxssh
74 import os
75 import sys
76 import time
77 import getopt
78 import pickle
79 import getpass
80 import smtplib
81 from pprint import pprint
82
83
84 try:
85 raw_input
86 except NameError:
87 raw_input = input
88
89
90 TOPIP_LOG_FILE = '/var/log/topip.log'
91 TOPIP_LAST_RUN_STATS = '/var/run/topip.last'
92
93 def exit_with_usage():
94
95 print(globals()['__doc__'])
96 os._exit(1)
97
98 def stats(r):
99
100 '''This returns a dict of the median, average, standard deviation,
101 min and max of the given sequence.
102
103 >>> from topip import stats
104 >>> print stats([5,6,8,9])
105 {'med': 8, 'max': 9, 'avg': 7.0, 'stddev': 1.5811388300841898, 'min': 5}
106 >>> print stats([1000,1006,1008,1014])
107 {'med': 1008, 'max': 1014, 'avg': 1007.0, 'stddev': 5.0, 'min': 1000}
108 >>> print stats([1,3,4,5,18,16,4,3,3,5,13])
109 {'med': 4, 'max': 18, 'avg': 6.8181818181818183, 'stddev': 5.621681757723747 5, 'min': 1}
110 >>> print stats([1,3,4,5,18,16,4,3,3,5,13,14,5,6,7,8,7,6,6,7,5,6,4,14,7])
111 {'med': 6, 'max': 18, 'avg': 7.0800000000000001, 'stddev': 4.325921867070647 4, 'min': 1}
112 '''
113
114 total = sum(r)
115 avg = float(total)/float(len(r))
116 sdsq = sum([(i-avg)**2 for i in r])
117 s = sorted(list(r))
118 return dict(list(zip(['med', 'avg', 'stddev', 'min', 'max'],
119 (s[len(s)//2], avg, (sdsq/len(r))**.5, min(r), max(r)))))
120
121 def send_alert (message, subject, addr_from, addr_to, smtp_server='localhost'):
122
123 '''This sends an email alert.
124 '''
125
126 message = ( 'From: %s\r\nTo: %s\r\nSubject: %s\r\n\r\n'
127 % (addr_from, addr_to, subject) + message )
128 server = smtplib.SMTP(smtp_server)
129 server.sendmail(addr_from, addr_to, message)
130 server.quit()
131
132 def main():
133
134 # Parse the options, arguments, etc.
135 try:
136 optlist, args = getopt.getopt(sys.argv[1:],
137 'h?valqs:u:p:n:', ['help','h','?','ipv6','stddev='])
138 except Exception as e:
139 print(str(e))
140 exit_with_usage()
141 options = dict(optlist)
142
143 munin_flag = False
144 if len(args) > 0:
145 if args[0] == 'config':
146 print('graph_title Netstat Connections per IP')
147 print('graph_vlabel Socket connections per IP')
148 print('connections_max.label max')
149 print('connections_max.info Maximum number of connections per IP')
150 print('connections_avg.label avg')
151 print('connections_avg.info Average number of connections per IP')
152 print('connections_stddev.label stddev')
153 print('connections_stddev.info Standard deviation')
154 return 0
155 elif args[0] != '':
156 print(args, len(args))
157 return 0
158 exit_with_usage()
159 if [elem for elem in options if elem in ['-h','--h','-?','--?','--help']]:
160 print('Help:')
161 exit_with_usage()
162 if '-s' in options:
163 hostname = options['-s']
164 else:
165 # if host was not specified then assume localhost munin plugin.
166 munin_flag = True
167 hostname = 'localhost'
168 # If localhost then don't ask for username/password.
169 if hostname != 'localhost' and hostname != '127.0.0.1':
170 if '-u' in options:
171 username = options['-u']
172 else:
173 username = raw_input('username: ')
174 if '-p' in options:
175 password = options['-p']
176 else:
177 password = getpass.getpass('password: ')
178 use_localhost = False
179 else:
180 use_localhost = True
181
182 if '-l' in options:
183 log_flag = True
184 else:
185 log_flag = False
186 if '-n' in options:
187 average_n = int(options['-n'])
188 else:
189 average_n = None
190 if '-v' in options:
191 verbose = True
192 else:
193 verbose = False
194 if '-a' in options:
195 alert_flag = True
196 (alert_addr_from, alert_addr_to) = tuple(options['-a'].split(','))
197 else:
198 alert_flag = False
199 if '--ipv6' in options:
200 ipv6_flag = True
201 else:
202 ipv6_flag = False
203 if '--stddev' in options:
204 stddev_trigger = float(options['--stddev'])
205 else:
206 stddev_trigger = 5
207
208 if ipv6_flag:
209 netstat_pattern = '(\S+)\s+(\S+)\s+(\S+)\s+(\S+)\s+::ffff:(\S+):(\S+)\s+ .*?\r'
210 else:
211 netstat_pattern = '(\S+)\s+(\S+)\s+(\S+)\s+(\S+)\s+(?:::ffff:)*(\S+):(\S +)\s+.*?\r'
212 #netstat_pattern = '(\S+)\s+(\S+)\s+(\S+)\s+(\S+)\s+(\S+):(\S+)\s+.*?\r'
213
214 # run netstat (either locally or via SSH).
215 if use_localhost:
216 p = pexpect.spawn('netstat -n -t')
217 PROMPT = pexpect.TIMEOUT
218 else:
219 p = pxssh.pxssh()
220 p.login(hostname, username, password)
221 p.sendline('netstat -n -t')
222 PROMPT = p.PROMPT
223
224 # For each matching netstat_pattern put the ip address in the list.
225 ip_list = {}
226 try:
227 while 1:
228 i = p.expect([PROMPT, netstat_pattern])
229 if i == 0:
230 break
231 k = p.match.groups()[4].decode('utf-8')
232 if k in ip_list:
233 ip_list[k] = ip_list[k] + 1
234 else:
235 ip_list[k] = 1
236 except:
237 pass
238
239 # remove a few common, uninteresting addresses from the dictionary.
240 ip_list = dict([ (key,value) for key,value in ip_list.items() if '192.168.' not in key])
241 ip_list = dict([ (key,value) for key,value in ip_list.items() if '127.0.0.1' not in key])
242
243 ip_list = list(ip_list.items())
244 if len(ip_list) < 1:
245 if verbose: print('Warning: no networks connections worth looking at.')
246 return 0
247 ip_list.sort(key=lambda x:x[1])
248
249 # generate some stats for the ip addresses found.
250 if average_n is not None and average_n <= 1:
251 average_n = None
252 # Reminder: the * unary operator treats the list elements as arguments.
253 zipped = zip(*ip_list[0:average_n])
254 s = stats(list(zipped)[1])
255 s['maxip'] = ip_list[0]
256
257 # print munin-style or verbose results for the stats.
258 if munin_flag:
259 print('connections_max.value', s['max'])
260 print('connections_avg.value', s['avg'])
261 print('connections_stddev.value', s['stddev'])
262 return 0
263 if verbose:
264 pprint (s)
265 print()
266 pprint (ip_list[0:average_n])
267
268 # load the stats from the last run.
269 try:
270 last_stats = pickle.load(file(TOPIP_LAST_RUN_STATS))
271 except:
272 last_stats = {'maxip':None}
273
274 if ( s['maxip'][1] > (s['stddev'] * stddev_trigger)
275 and s['maxip']==last_stats['maxip'] ):
276 if verbose: print('The maxip has been above trigger for two consecutive samples.')
277 if alert_flag:
278 if verbose: print('SENDING ALERT EMAIL')
279 send_alert(str(s), 'ALERT on %s'
280 % hostname, alert_addr_from, alert_addr_to)
281 if log_flag:
282 if verbose: print('LOGGING THIS EVENT')
283 fout = file(TOPIP_LOG_FILE,'a')
284 #dts = time.strftime('%Y:%m:%d:%H:%M:%S', time.localtime())
285 dts = time.asctime()
286 fout.write ('%s - %d connections from %s\n'
287 % (dts,s['maxip'][1],str(s['maxip'][0])))
288 fout.close()
289
290 # save state to TOPIP_LAST_RUN_STATS
291 try:
292 pickle.dump(s, file(TOPIP_LAST_RUN_STATS,'w'))
293 os.chmod (TOPIP_LAST_RUN_STATS, 0o664)
294 except:
295 pass
296 # p.logout()
297
298 if __name__ == '__main__':
299 main()
OLDNEW
« no previous file with comments | « third_party/pexpect/examples/table_test.html ('k') | third_party/pexpect/examples/uptime.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698