3 # NEPI, a framework to manage network experiments
4 # Copyright (C) 2013 INRIA
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;
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.
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/>.
18 # Author: Alina Quereilhac <alina.quereilhac@inria.fr>
20 from __future__ import print_function
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
31 from scipy import stats
32 from matplotlib import pyplot
36 from optparse import OptionParser
38 usage = ("usage: %prog -s <pl-slice> -u <pl-user> -p <pl-password> "
39 "-k <pl-ssh-key> -N <nodes>")
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",
51 parser.add_option("-N", "--nodes", dest="nodes",
52 help="Comma separated list of Planetlab nodes",
55 (options, args) = parser.parse_args()
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(",")
63 def avg_interest_rtt(ec, run):
66 # Parse downloaded CCND logs
69 interest_expiry_count,
70 interest_dupnonce_count,
72 content_count) = ccn_parser.process_content_history_logs(
73 logs_dir, ec.netgraph.topology)
76 rtts = [content_names[content_name]["rtt"] \
77 for content_name in content_names.keys()]
79 # sample mean and standard deviation
80 sample = numpy.array(rtts)
81 n, min_max, mean, var, skew, kurt = stats.describe(sample)
83 ci = stats.t.interval(0.95, n-1, loc = mean,
84 scale = std/math.sqrt(n))
87 metrics.append((mean, ci[0], ci[1]))
91 def normal_law(ec, run, sample):
92 print("SAMPLE", sample)
94 x = numpy.array(sample)
97 se = std / math.sqrt(n)
101 return m * 0.05 >= se95
103 def post_process(ec, runs):
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)
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')
121 pyplot.savefig("plot.png")
124 content_name = "ccnx:/test/bunny.ts"
126 repofile = os.path.join(
127 os.path.dirname(os.path.realpath(__file__)),
130 def get_simulator(ec):
131 simulator = ec.filter_resources("linux::ns3::Simulation")
134 node = ec.register_resource("linux::Node")
135 ec.set(node, "hostname", "localhost")
137 simu = ec.register_resource("linux::ns3::Simulation")
138 ec.register_connection(simu, node)
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)
148 ec.set(collector, "rename", newname)
152 def add_dce_host(ec, nid):
153 simu = get_simulator(ec)
155 host = ec.register_resource("ns3::Node")
156 ec.set(host, "enableStack", True)
157 ec.register_connection(host, simu)
160 ec.netgraph.annotate_node(nid, "host", host)
162 def add_dce_ccnd(ec, nid):
163 # Retrieve annotation from netgraph
164 host = ec.netgraph.node_annotation(nid, "host")
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)
175 # Collector to retrieve ccnd log
176 collector = add_collector(ec, "stderr", str(nid), "log")
177 ec.register_connection(collector, ccnd)
180 ec.netgraph.annotate_node(nid, "ccnd", ccnd)
182 def add_dce_ccnr(ec, nid):
183 # Retrieve annotation from netgraph
184 host = ec.netgraph.node_annotation(nid, "host")
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)
194 def add_dce_ccncat(ec, nid):
195 # Retrieve annotation from netgraph
196 host = ec.netgraph.node_annotation(nid, "host")
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)
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)
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)
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)
227 prefix = net["prefix"]
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)
235 queue = ec.register_resource("ns3::DropTailQueue")
236 ec.register_connection(dev, queue)
240 def add_pl_host(ec, nid):
241 hostname = NODES[nid]
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)
252 ec.netgraph.annotate_node(nid, "hostname", hostname)
253 ec.netgraph.annotate_node(nid, "host", host)
255 # Annotate the graph node with an ip address
256 ip = socket.gethostbyname(hostname)
257 ec.netgraph.annotate_node_ip(nid, ip)
259 def add_pl_ccnd(ec, nid):
260 # Retrieve annotation from netgraph
261 host = ec.netgraph.node_annotation(nid, "host")
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)
268 # Collector to retrieve ccnd log
269 collector = add_collector(ec, "stderr", str(nid), "log")
270 ec.register_connection(collector, ccnd)
273 ec.netgraph.annotate_node(nid, "ccnd", ccnd)
275 def add_pl_ccnr(ec, nid):
276 # Retrieve annotation from netgraph
277 ccnd = ec.netgraph.node_annotation(nid, "ccnd")
279 # Add a CCN content repository to the planetlab node
280 ccnr = ec.register_resource("linux::CCNR")
282 ec.set(ccnr, "repoFile1", repofile)
283 ec.register_connection(ccnr, ccnd)
285 def add_pl_ccncat(ec, nid):
286 # Retrieve annotation from netgraph
287 ccnd = ec.netgraph.node_annotation(nid, "ccnd")
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)
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")
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)
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)
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)
316 if nid == ec.netgraph.targets()[0]:
317 add_dce_ccnr(ec, nid)
319 if nid == ec.netgraph.sources()[0]:
320 add_dce_ccncat(ec, nid)
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)
327 def add_node(ec, nid):
328 ### Add CCN nodes (ec.netgraph holds the topology graph)
332 if nid == ec.netgraph.targets()[0]:
335 if nid == ec.netgraph.sources()[0]:
336 add_pl_ccncat(ec, nid)
339 return ec.filter_resources("linux::CCNCat")
341 if __name__ == '__main__':
345 # topology translation to NEPI model
346 ec = ExperimentController("pl_4n_linear",
347 topo_type = TopologyType.LINEAR,
351 add_node_callback = add_node,
352 add_edge_callback = add_edge)
354 #### Run experiment until metric convergence
355 rnr = ExperimentRunner()
359 compute_metric_callback = avg_interest_rtt,
360 evaluate_convergence_callback = normal_law,
361 wait_guids = wait_guids(ec))
364 post_process(ec, runs)