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