use print() - import print_function - should be fine for both py2 and py3
[nepi.git] / examples / ccn_emu_live / planetlab_4_nodes_linear.py
1 #!/usr/bin/env python
2 #
3 #    NEPI, a framework to manage network experiments
4 #    Copyright (C) 2013 INRIA
5 #
6 #    This program is free software: you can redistribute it and/or modify
7 #    it under the terms of the GNU General Public License version 2 as
8 #    published by the Free Software Foundation;
9 #
10 #    This program is distributed in the hope that it will be useful,
11 #    but WITHOUT ANY WARRANTY; without even the implied warranty of
12 #    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 #    GNU General Public License for more details.
14 #
15 #    You should have received a copy of the GNU General Public License
16 #    along with this program.  If not, see <http://www.gnu.org/licenses/>.
17 #
18 # Author: Alina Quereilhac <alina.quereilhac@inria.fr>
19
20 from __future__ import print_function
21
22 from nepi.execution.ec import ExperimentController 
23 from nepi.execution.runner import ExperimentRunner
24 from nepi.util.netgraph import TopologyType
25 import nepi.data.processing.ccn.parser as ccn_parser
26
27 import networkx
28 import socket
29 import os
30 import numpy
31 from scipy import stats
32 from matplotlib import pyplot
33 import math
34 import random
35
36 from optparse import OptionParser
37
38 usage = ("usage: %prog -s <pl-slice> -u <pl-user> -p <pl-password> "
39      "-k <pl-ssh-key> -N <nodes>")
40
41 parser = OptionParser(usage = usage)
42 parser.add_option("-s", "--pl-slice", dest="pl_slice",
43         help="PlanetLab slicename", type="str")
44 parser.add_option("-u", "--pl-user", dest="pl_user",
45         help="PlanetLab web username", type="str")
46 parser.add_option("-p", "--pl-password", dest="pl_password",
47         help="PlanetLab web password", type="str")
48 parser.add_option("-k", "--pl-ssh-key", dest="pl_ssh_key",
49         help="Path to private SSH key associated with the PL account",
50         type="str")
51 parser.add_option("-N", "--nodes", dest="nodes",
52         help="Comma separated list of Planetlab nodes",
53         type="str")
54
55 (options, args) = parser.parse_args()
56
57 pl_slice = options.pl_slice
58 pl_ssh_key = options.pl_ssh_key
59 pl_user = options.pl_user
60 pl_password = options.pl_password
61 NODES = options.nodes.strip().split(",")
62
63 def avg_interest_rtt(ec, run):
64     logs_dir = ec.run_dir
65     
66     # Parse downloaded CCND logs
67     (graph,
68       content_names,
69       interest_expiry_count,
70       interest_dupnonce_count,
71       interest_count,
72       content_count) = ccn_parser.process_content_history_logs(
73         logs_dir, ec.netgraph.topology)
74
75     # statistics on RTT
76     rtts = [content_names[content_name]["rtt"] \
77             for content_name in content_names.keys()]
78
79     # sample mean and standard deviation
80     sample = numpy.array(rtts)
81     n, min_max, mean, var, skew, kurt = stats.describe(sample)
82     std = math.sqrt(var)
83     ci = stats.t.interval(0.95, n-1, loc = mean, 
84             scale = std/math.sqrt(n))
85
86     global metrics
87     metrics.append((mean, ci[0], ci[1]))
88     
89     return mean
90
91 def normal_law(ec, run, sample):
92     print("SAMPLE", sample)
93
94     x = numpy.array(sample)
95     n = len(sample)
96     std = x.std()
97     se = std / math.sqrt(n)
98     m = x.mean()
99     se95 = se * 2
100     
101     return m * 0.05 >= se95
102
103 def post_process(ec, runs):
104     global metrics
105     
106     # plot convergence graph
107     y = numpy.array([float(m[0]) for m in metrics])
108     low = numpy.array([float(m[1]) for m in metrics])
109     high = numpy.array([float(m[2]) for m in metrics])
110     error = [y - low, high - y]
111     x = range(1,runs + 1)
112
113     # plot average RTT and confidence interval for each iteration
114     pyplot.errorbar(x, y, yerr = error, fmt='o')
115     pyplot.plot(x, y, 'r-')
116     pyplot.xlim([0.5, runs + 0.5])
117     pyplot.xticks(numpy.arange(1, len(y)+1, 1))
118     pyplot.xlabel('Iteration')
119     pyplot.ylabel('Average RTT')
120     pyplot.grid()
121     pyplot.savefig("plot.png")
122     pyplot.show()
123
124 content_name = "ccnx:/test/bunny.ts"
125
126 repofile = os.path.join(
127         os.path.dirname(os.path.realpath(__file__)), 
128         "repoFile1.0.8.2")
129
130 def get_simulator(ec):
131     simulator = ec.filter_resources("linux::ns3::Simulation")
132
133     if not simulator:
134         node = ec.register_resource("linux::Node")
135         ec.set(node, "hostname", "localhost")
136
137         simu = ec.register_resource("linux::ns3::Simulation")
138         ec.register_connection(simu, node)
139         return simu
140
141     return simulator[0]
142
143 def add_collector(ec, trace_name, subdir, newname = None):
144     collector = ec.register_resource("Collector")
145     ec.set(collector, "traceName", trace_name)
146     ec.set(collector, "subDir", subdir)
147     if newname:
148         ec.set(collector, "rename", newname)
149
150     return collector
151
152 def add_dce_host(ec, nid):
153     simu = get_simulator(ec)
154     
155     host = ec.register_resource("ns3::Node")
156     ec.set(host, "enableStack", True)
157     ec.register_connection(host, simu)
158
159     # Annotate the graph
160     ec.netgraph.annotate_node(nid, "host", host)
161     
162 def add_dce_ccnd(ec, nid):
163     # Retrieve annotation from netgraph
164     host = ec.netgraph.node_annotation(nid, "host")
165     
166     # Add dce ccnd to the dce node
167     ccnd = ec.register_resource("linux::ns3::dce::CCND")
168     ec.set (ccnd, "stackSize", 1<<20)
169     ec.set (ccnd, "debug", 7)
170     ec.set (ccnd, "capacity", 50000)
171     ec.set (ccnd, "StartTime", "1s")
172     ec.set (ccnd, "StopTime", STOP_TIME)
173     ec.register_connection(ccnd, host)
174
175     # Collector to retrieve ccnd log
176     collector = add_collector(ec, "stderr", str(nid), "log")
177     ec.register_connection(collector, ccnd)
178
179     # Annotate the graph
180     ec.netgraph.annotate_node(nid, "ccnd", ccnd)
181
182 def add_dce_ccnr(ec, nid):
183     # Retrieve annotation from netgraph
184     host = ec.netgraph.node_annotation(nid, "host")
185     
186     # Add a CCN content repository to the dce node
187     ccnr = ec.register_resource("linux::ns3::dce::CCNR")
188     ec.set (ccnr, "repoFile1", repofile) 
189     ec.set (ccnr, "stackSize", 1<<20)
190     ec.set (ccnr, "StartTime", "2s")
191     ec.set (ccnr, "StopTime", STOP_TIME)
192     ec.register_connection(ccnr, host)
193
194 def add_dce_ccncat(ec, nid):
195     # Retrieve annotation from netgraph
196     host = ec.netgraph.node_annotation(nid, "host")
197    
198     # Add a ccncat application to the dce host
199     ccncat = ec.register_resource("linux::ns3::dce::CCNCat")
200     ec.set (ccncat, "contentName", content_name)
201     ec.set (ccncat, "stackSize", 1<<20)
202     ec.set (ccncat, "StartTime", "8s")
203     ec.set (ccncat, "StopTime", STOP_TIME)
204     ec.register_connection(ccncat, host)
205
206 def add_dce_fib_entry(ec, nid1, nid2):
207     # Retrieve annotations from netgraph
208     host1 = ec.netgraph.node_annotation(nid1, "host")
209     net = ec.netgraph.edge_net_annotation(nid1, nid2)
210     ip2 = net[nid2]
211
212     # Add FIB entry between peer hosts
213     ccndc = ec.register_resource("linux::ns3::dce::FIBEntry")
214     ec.set (ccndc, "protocol", "udp") 
215     ec.set (ccndc, "uri", "ccnx:/") 
216     ec.set (ccndc, "host", ip2)
217     ec.set (ccndc, "stackSize", 1<<20)
218     ec.set (ccndc, "StartTime", "2s")
219     ec.set (ccndc, "StopTime", STOP_TIME)
220     ec.register_connection(ccndc, host1)
221
222 def add_dce_net_iface(ec, nid1, nid2):
223     # Retrieve annotations from netgraph
224     host = ec.netgraph.node_annotation(nid1, "host")
225     net = ec.netgraph.edge_net_annotation(nid1, nid2)
226     ip1 = net[nid1]
227     prefix = net["prefix"]
228
229     dev = ec.register_resource("ns3::PointToPointNetDevice")
230     ec.set(dev,"DataRate", "5Mbps")
231     ec.set(dev, "ip", ip1)
232     ec.set(dev, "prefix", prefix)
233     ec.register_connection(host, dev)
234
235     queue = ec.register_resource("ns3::DropTailQueue")
236     ec.register_connection(dev, queue)
237
238     return dev
239
240 def add_pl_host(ec, nid):
241     hostname = NODES[nid]
242
243     # Add a planetlab host to the experiment description
244     host = ec.register_resource("planetlab::Node")
245     ec.set(host, "hostname", hostname)
246     ec.set(host, "username", pl_slice)
247     ec.set(host, "identity", pl_ssh_key)
248     ec.set(host, "cleanExperiment", True)
249     ec.set(host, "cleanProcesses", True)
250
251     # Annotate the graph
252     ec.netgraph.annotate_node(nid, "hostname", hostname)
253     ec.netgraph.annotate_node(nid, "host", host)
254     
255     # Annotate the graph node with an ip address
256     ip = socket.gethostbyname(hostname)
257     ec.netgraph.annotate_node_ip(nid, ip)
258
259 def add_pl_ccnd(ec, nid):
260     # Retrieve annotation from netgraph
261     host = ec.netgraph.node_annotation(nid, "host")
262     
263     # Add a CCN daemon to the planetlab node
264     ccnd = ec.register_resource("linux::CCND")
265     ec.set(ccnd, "debug", 7)
266     ec.register_connection(ccnd, host)
267     
268     # Collector to retrieve ccnd log
269     collector = add_collector(ec, "stderr", str(nid), "log")
270     ec.register_connection(collector, ccnd)
271
272     # Annotate the graph
273     ec.netgraph.annotate_node(nid, "ccnd", ccnd)
274
275 def add_pl_ccnr(ec, nid):
276     # Retrieve annotation from netgraph
277     ccnd = ec.netgraph.node_annotation(nid, "ccnd")
278     
279     # Add a CCN content repository to the planetlab node
280     ccnr = ec.register_resource("linux::CCNR")
281
282     ec.set(ccnr, "repoFile1", repofile)
283     ec.register_connection(ccnr, ccnd)
284
285 def add_pl_ccncat(ec, nid):
286     # Retrieve annotation from netgraph
287     ccnd = ec.netgraph.node_annotation(nid, "ccnd")
288     
289     # Add a CCN cat application to the planetlab node
290     ccncat = ec.register_resource("linux::CCNCat")
291     ec.set(ccncat, "contentName", content_name)
292     ec.register_connection(ccncat, ccnd)
293
294 def add_pl_fib_entry(ec, nid1, nid2):
295     # Retrieve annotations from netgraph
296     ccnd1 = ec.netgraph.node_annotation(nid1, "ccnd")
297     hostname2 = ec.netgraph.node_annotation(nid2, "hostname")
298     
299     # Add a FIB entry between one planetlab node and its peer
300     entry = ec.register_resource("linux::FIBEntry")
301     ec.set(entry, "host", hostname2)
302     ec.register_connection(entry, ccnd1)
303
304     # Collector to retrieve peering ping output (to measure neighbors delay)
305     ec.enable_trace(entry, "ping")
306     collector = add_collector(ec, "ping", str(nid1))
307     ec.register_connection(collector, entry)
308
309     return entry
310
311 def add_node(ec, nid):
312     ### Add CCN nodes (ec.netgraph holds the topology graph)
313     add_dce_host(ec, nid)
314     add_dce_ccnd(ec, nid)
315         
316     if nid == ec.netgraph.targets()[0]:
317         add_dce_ccnr(ec, nid)
318
319     if nid == ec.netgraph.sources()[0]:
320         add_dce_ccncat(ec, nid)
321
322 def add_edge(ec, nid1, nid2):
323     #### Add connections between CCN nodes
324     add_pl_fib_entry(ec, nid1, nid2)
325     add_pl_fib_entry(ec, nid2, nid1)
326
327 def add_node(ec, nid):
328     ### Add CCN nodes (ec.netgraph holds the topology graph)
329     add_pl_host(ec, nid)
330     add_pl_ccnd(ec, nid)
331         
332     if nid == ec.netgraph.targets()[0]:
333         add_pl_ccnr(ec, nid)
334
335     if nid == ec.netgraph.sources()[0]:
336         add_pl_ccncat(ec, nid)
337
338 def wait_guids(ec):
339     return ec.filter_resources("linux::CCNCat")
340
341 if __name__ == '__main__':
342
343     metrics = []
344
345     # topology translation to NEPI model
346     ec = ExperimentController("pl_4n_linear",
347         topo_type = TopologyType.LINEAR, 
348         node_count = 4,
349         assign_st = True,
350         assign_ips = True,
351         add_node_callback = add_node,
352         add_edge_callback = add_edge)
353
354     #### Run experiment until metric convergence
355     rnr = ExperimentRunner()
356     runs = rnr.run(ec,
357             min_runs = 10,
358             max_runs = 100, 
359             compute_metric_callback = avg_interest_rtt,
360             evaluate_convergence_callback = normal_law,
361             wait_guids = wait_guids(ec))
362    
363     ### post processing
364     post_process(ec, runs)
365
366