diff options
Diffstat (limited to 'netsec.py')
-rwxr-xr-x | netsec.py | 223 |
1 files changed, 223 insertions, 0 deletions
diff --git a/netsec.py b/netsec.py new file mode 100755 index 0000000..b1f4f30 --- /dev/null +++ b/netsec.py | |||
@@ -0,0 +1,223 @@ | |||
1 | #!/usr/bin/env python | ||
2 | import defapp | ||
3 | |||
4 | from plot import decode | ||
5 | from util import load_csv_file, write_csv_file | ||
6 | |||
7 | from math import ceil | ||
8 | |||
9 | from numpy import amin, amax, mean, median, std, histogram, zeros, arange | ||
10 | |||
11 | from os.path import splitext, basename | ||
12 | from optparse import make_option as o | ||
13 | |||
14 | from gnuplot import gnuplot, FORMATS, Plot, label, curve | ||
15 | |||
16 | options = [ | ||
17 | # output options | ||
18 | o('-f', '--format', action='store', dest='format', type='choice', | ||
19 | choices=FORMATS, help='output format'), | ||
20 | o(None, '--save-script', action='store_true', dest='save_script'), | ||
21 | o('-p', '--prefix', action='store', dest='prefix'), | ||
22 | |||
23 | # formatting options | ||
24 | # These may or may not be supported by a particular experiment plotter. | ||
25 | o(None, '--smooth', action='store_true', dest='smooth'), | ||
26 | o(None, '--hist', action='store_true', dest='histogram'), | ||
27 | ] | ||
28 | |||
29 | defaults = { | ||
30 | # output options | ||
31 | 'format' : 'pdf', | ||
32 | 'save_script' : False, | ||
33 | 'prefix' : '', | ||
34 | |||
35 | # formatting options | ||
36 | 'histogram' : False, | ||
37 | 'smooth' : False, | ||
38 | } | ||
39 | |||
40 | def get_stats_label(samples): | ||
41 | avg = mean(samples) | ||
42 | med = median(samples) | ||
43 | dev = std(samples) | ||
44 | max = amax(samples) | ||
45 | min = amin(samples) | ||
46 | return "min=%.2f max=%.2f avg=%.2f median=%.2f std=%.2f" \ | ||
47 | % (min, max, avg, med, dev) | ||
48 | |||
49 | class NetsecPlotter(defapp.App): | ||
50 | def __init__(self): | ||
51 | defapp.App.__init__(self, options, defaults, no_std_opts=True) | ||
52 | self.tmpfiles = [] | ||
53 | |||
54 | def make_plot(self, fname=None): | ||
55 | p = Plot() | ||
56 | p.output = "%s%s.%s" % (self.options.prefix, fname, self.options.format) | ||
57 | p.format = self.options.format | ||
58 | return p | ||
59 | |||
60 | def setup_png(self, plot): | ||
61 | # standard png options; usually correct; never tweaked for paper | ||
62 | if self.options.format == 'png': | ||
63 | plot.font_size = 'large' | ||
64 | plot.size = (1024, 768) | ||
65 | plot.xticks = (0, 1) | ||
66 | plot.yticks = (0, 0.1) | ||
67 | plot.default_style = "linespoints" | ||
68 | return True | ||
69 | else: | ||
70 | return False | ||
71 | |||
72 | def write(self, data, name, ext='data'): | ||
73 | if self.options.save_script: | ||
74 | fname = "%s.%s" % (name, ext) | ||
75 | write_csv_file(fname, data) | ||
76 | return fname | ||
77 | else: | ||
78 | tmp = write_csv_file(None, data) | ||
79 | # keep a reference so that it isn't deleted | ||
80 | self.tmpfiles.append(tmp) | ||
81 | return tmp.name | ||
82 | |||
83 | def load(self, datafile): | ||
84 | data = load_csv_file(datafile) | ||
85 | print "loaded %d lines" % len(data) | ||
86 | return data | ||
87 | |||
88 | def write_histogram(self, samples, name): | ||
89 | max = amax(samples) | ||
90 | (hist, edges) = histogram(samples, bins=max, | ||
91 | range=(0.5,max+.5)) | ||
92 | data = zeros((len(edges)-1, 3)) | ||
93 | cumulative = 0 | ||
94 | for i in xrange(len(hist)): | ||
95 | data[i, 0] = (edges[i] + edges[i + 1]) / 2.0 | ||
96 | data[i, 1] = hist[i] | ||
97 | cumulative += hist[i] | ||
98 | data[i, 2] = cumulative | ||
99 | |||
100 | if len(hist) > 20: | ||
101 | label_freq = 10 | ||
102 | else: | ||
103 | label_freq = 1 | ||
104 | |||
105 | for_file = [] | ||
106 | for i, row in enumerate(data): | ||
107 | label = '%d' % row[0] if row[0] % label_freq == 0 else '' | ||
108 | for_file.append([row[0], row[1], row[2], label]) | ||
109 | |||
110 | return (data, self.write(for_file, name, ext='hist')) | ||
111 | |||
112 | def render(self, p): | ||
113 | if self.options.save_script: | ||
114 | p.gnuplot_save(p.output + '.plot') | ||
115 | else: | ||
116 | p.gnuplot_exec() | ||
117 | |||
118 | def plot_hchisto(self, datafile, name, conf): | ||
119 | data = self.load(datafile) | ||
120 | |||
121 | #max_val = amax(data[:,1]) | ||
122 | |||
123 | if self.options.histogram: | ||
124 | name += '_hist' | ||
125 | |||
126 | p = self.make_plot(name) | ||
127 | |||
128 | # place a label on the graph | ||
129 | p.labels = [label(0.5, 0.9, get_stats_label(data[:,1]), | ||
130 | coord=['graph', 'screen'], align='center')] | ||
131 | |||
132 | (data, fname) = self.write_histogram(data[:,1], name) | ||
133 | p.xlabel = "hop count" | ||
134 | |||
135 | p.ylabel = "number of sources" | ||
136 | p.setup_histogram(gap=1, boxwidth=1.0) | ||
137 | p.title = "hop counts;" | ||
138 | |||
139 | if 'per-host' in conf: | ||
140 | p.title += " HC per-host: %s" % conf['per-host'] | ||
141 | |||
142 | if 'as-num' in conf: | ||
143 | p.title += " AS=%s, IP/mask=%s/%s MB=%s Unique Hosts=%s" % \ | ||
144 | (conf['as-num'], conf['as-str'], conf['as-mask'], \ | ||
145 | conf['megabytes'], conf['num-hosts']) | ||
146 | |||
147 | # p.xrange = (0, ceil(max_cost)) | ||
148 | p.xticks = (0, 10) | ||
149 | # p.yticks = (0, 1) | ||
150 | #p.yrange = (0, (ceil(amax(data[:,1]) / 100) * 100)) | ||
151 | |||
152 | |||
153 | ymax = amax(data[:,1]) | ||
154 | p.curves = [curve(histogram=fname, col=2, labels_col=4)] | ||
155 | p.yrange = (1, amax(data[:,1]) + 2) | ||
156 | |||
157 | if ymax > 10000: | ||
158 | p.ylog = True | ||
159 | |||
160 | #print "not implemented yet..." | ||
161 | #return | ||
162 | ## plot raw samples | ||
163 | #p.title = "raw decoding cost; input=%s; host=%s" \ | ||
164 | # % (conf['file'], conf['host']) | ||
165 | |||
166 | #p.ylabel = "decoding cost (ms)" | ||
167 | #p.xlabel = "frame number" | ||
168 | #p.xrange = (0, len(data)) | ||
169 | ##p.xticks = (0, 100) | ||
170 | #p.yticks = (0, 1) | ||
171 | #p.yrange = (1, ceil(max_cost) + 2) | ||
172 | |||
173 | #p.curves = [curve(fname=fname, xcol=2, ycol=1, title="decoding cost")] | ||
174 | |||
175 | |||
176 | #### Styling. | ||
177 | |||
178 | if not self.setup_png(p): | ||
179 | p.rounded_caps = True | ||
180 | p.font = 'Helvetica' | ||
181 | |||
182 | p.font_size = '10' | ||
183 | p.size = ('20cm', '10cm') | ||
184 | p.monochrome = False | ||
185 | p.dashed_lines = False | ||
186 | p.key = 'off' | ||
187 | p.default_style = 'points lw 1' | ||
188 | |||
189 | if self.options.smooth: | ||
190 | p.default_style += " smooth bezier" | ||
191 | |||
192 | self.render(p) | ||
193 | |||
194 | |||
195 | def plot_file(self, datafile): | ||
196 | bname = basename(datafile) | ||
197 | name, ext = splitext(bname) | ||
198 | conf = decode(name) | ||
199 | |||
200 | if 'per-host' in conf or 'as-num' in conf: | ||
201 | self.plot_hchisto(datafile, name, conf) | ||
202 | |||
203 | #for plot_type in plotters: | ||
204 | # if plot_type in conf: | ||
205 | # try: | ||
206 | # plotters[plot_type](datafile, name, conf) | ||
207 | # except IOError as err: | ||
208 | # self.err("Skipped '%s' (%s)." % err) | ||
209 | # break | ||
210 | #else: | ||
211 | # self.err("Skipped '%s'; unkown experiment type." | ||
212 | # % bname) | ||
213 | |||
214 | # release all tmp files | ||
215 | self.tmpfiles = [] | ||
216 | |||
217 | def default(self, _): | ||
218 | for i, datafile in enumerate(self.args): | ||
219 | self.out("[%d/%d] Processing %s ..." % (i + 1, len(self.args), datafile)) | ||
220 | self.plot_file(datafile) | ||
221 | |||
222 | if __name__ == "__main__": | ||
223 | NetsecPlotter().launch() | ||