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