sensor: profiler updates
[oweals/gnunet.git] / src / sensor / profiler.py
1 import argparse
2 import math
3 import networkx
4 import random
5 import tempfile
6 import os
7 import time
8 import matplotlib.pyplot as plt
9 from subprocess import Popen, PIPE, STDOUT
10
11 node_colors = None
12 graph = None
13 pos = None
14
15 def get_args():
16   parser = argparse.ArgumentParser(description="Sensor profiler")
17   parser.add_argument('-p', '--peers', action='store', type=int, required=True,
18                       help='Number of peers to run')
19   parser.add_argument('-l', '--links', action='store', type=int, required=False,
20                       help='Number of links to create')
21   parser.add_argument('-i', '--sensors-interval', action='store', type=int,
22                       required=False,
23                       help='Change the interval of running sensors to given value')
24   return parser.parse_args()
25
26 def generate_topology(peers, links):
27   global graph
28   global node_colors
29   global pos
30   graph = networkx.empty_graph(peers)
31   for i in range(0, links):
32     a = 0
33     b = 0
34     while a == b:
35       a = random.randint(0, peers - 1)
36       b = random.randint(0, peers - 1)
37     graph.add_edge(a, b)
38   node_colors = [0] * peers
39   pos = networkx.layout.spring_layout(graph)
40
41 def create_topology_file():
42   global graph
43   nodes = list()
44   for i in range(len(graph.edge)):
45     nodes.append(list())
46   for e in graph.edges():
47     nodes[e[0]].append(e[1])
48   print nodes
49   f = tempfile.NamedTemporaryFile(delete=False)
50   for i in range(len(nodes)):
51     if len(nodes[i]) == 0:
52       continue
53     f.write('%d:' % i)
54     f.write('|'.join(map(str, nodes[i])))
55     f.write('\n')
56   # f.close()
57   return f.name
58
59 def draw_graph():
60   global graph
61   global node_colors
62   global pos
63   t = int(time.time())
64   inc = 2
65   name = str(t) + '.png'
66   while os.path.exists(name):
67     name = '%d(%d).png' % (t, inc)
68     inc += 1
69   print 'Drawing graph to file: %s' % name
70   plt.clf()
71   anomaly_lbls = {}
72   for i in range(len(graph.node)):
73     if node_colors[i] >= 1:
74       anomaly_lbls[i] = '\n\n\n' + str(node_colors[i] - 1)
75   networkx.draw(graph, pos=pos, node_color=node_colors, with_labels=range(len(graph.node)), cmap=plt.cm.Reds, vmin=0, vmax=2)
76   networkx.draw_networkx_labels(graph, pos, anomaly_lbls)
77   plt.savefig(name)
78
79 def peers_reconnected(p1, p2):
80   global graph
81   if p2 in graph[p1]:
82     print 'Link already exists'
83     return
84   graph.add_edge(p1, p2)
85   draw_graph()
86
87 def peers_disconnected(p1, p2):
88   global graph
89   print 'Disconnected peers %d and %d' % (p1, p2)
90   if p2 not in graph[p1]:
91     print 'Link does not exist'
92     return
93   graph.remove_edge(p1, p2)
94   draw_graph()
95
96 def anomaly_report(report):
97   global node_colors
98   if 0 == report['anomalous']:
99     node_colors[report['peer']] = 0
100   else:
101     clr = 1 + report['neighbors']
102     if node_colors[report['peer']] >= clr:
103       return
104     node_colors[report['peer']] = clr
105   draw_graph()
106
107 def handle_profiler_line(line):
108   if not line:
109     return
110   print line
111   if 'Peer disconnection request sent' in line: # Peers disconnected
112     parts = line.split(':')
113     peers = parts[-1].split(',')
114     peers_disconnected(int(peers[0]), int(peers[1]))
115     return
116   if 'Anomaly report:' in line:
117     parts = line.split('Anomaly report:')
118     anomaly_report(eval(parts[1]))
119     return
120   if 'Peer connection request sent' in line: # Peers reconnected
121     parts = line.split(':')
122     peers = parts[-1].split(',')
123     peers_reconnected(int(peers[0]), int(peers[1]))
124
125 def run_profiler(peers, topology_file, sensors_interval, split_file):
126   cmd1 = "./gnunet-sensor-profiler -p %d -t %s -s %s" % (peers, topology_file, split_file)
127   if sensors_interval:
128     cmd1 += " -i %d" % sensors_interval
129   cmd2 = "> log 2>&1"
130   cmd = "%s %s" % (cmd1, cmd2)
131   print cmd
132   process = Popen([cmd], shell=True)
133   time.sleep(0.5)
134   line = ''
135   f = open('log')
136   while process.poll() is None:
137     for c in f.read():
138       if not c or c == '\n':
139         handle_profiler_line(line)
140         line = ''
141       else:
142         line += c
143   os.remove('log')
144
145 def create_split():
146   global graph
147   f = open('split', 'w+')
148   half_size = len(graph.node) / 2
149   half1 = []
150   half2 = []
151   for n in graph.node:
152     if n < half_size:
153       half1.append(n)
154     else:
155       half2.append(n)
156   for e in graph.edges():
157     if (e[0] in half1 and e[1] in half2) or (e[0] in half2 and e[1] in half1):
158       f.write('%d,%d\n' % (e[0], e[1]))
159   f.close()
160
161 def main():
162   args = vars(get_args())
163   num_peers = args['peers']
164   if num_peers < 3:
165     print 'Min number of peers is 3'
166     return
167   sensors_interval = None
168   if 'sensors_interval' in args:
169     sensors_interval = args['sensors_interval']
170   if 'links' in args:
171     num_links = args['links']
172   else:
173     #num_links = int(math.log(num_peers) * math.log(num_peers) * num_peers / 2)
174     num_links = int(math.log(num_peers) * num_peers)
175   # Generate random topology
176   generate_topology(num_peers, num_links)
177   print 'Generated random topology with %d peers and %d links' % (num_peers, num_links)
178   # Create a file with links to cut to split the topology into two
179   create_split()
180   # Create TESTBED topology file
181   top_file = create_topology_file()
182   print 'Created TESTBED topology file %s' % top_file
183   draw_graph()
184   # Run c profiler
185   if os.path.isfile('log'):
186     os.remove('log')
187   run_profiler(num_peers, top_file, sensors_interval, 'split')
188   
189 if __name__ == "__main__":
190   main()