Adding LINEAR examples for ccn_emu_live
authorAlina Quereilhac <alina.quereilhac@inria.fr>
Tue, 23 Sep 2014 22:58:36 +0000 (00:58 +0200)
committerAlina Quereilhac <alina.quereilhac@inria.fr>
Tue, 23 Sep 2014 22:58:36 +0000 (00:58 +0200)
examples/ccn_emu_live/dce_4_nodes_linear.py [new file with mode: 0644]
examples/ccn_emu_live/planetlab_4_nodes_linear.py [new file with mode: 0644]

diff --git a/examples/ccn_emu_live/dce_4_nodes_linear.py b/examples/ccn_emu_live/dce_4_nodes_linear.py
new file mode 100644 (file)
index 0000000..1658ebe
--- /dev/null
@@ -0,0 +1,269 @@
+#!/usr/bin/env python\r
+\r
+###############################################################################\r
+#\r
+#    NEPI, a framework to manage network experiments\r
+#\r
+#    This program is free software: you can redistribute it and/or modify\r
+#    it under the terms of the GNU General Public License as published by\r
+#    the Free Software Foundation, either version 3 of the License, or\r
+#    (at your option) any later version.\r
+#\r
+#    This program is distributed in the hope that it will be useful,\r
+#    but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
+#    GNU General Public License for more details.\r
+#\r
+#    You should have received a copy of the GNU General Public License\r
+#    along with this program.  If not, see <http://www.gnu.org/licenses/>.\r
+#\r
+# Author: Alina Quereilhac <alina.quereilhac@inria.fr>\r
+#\r
+###############################################################################\r
+from nepi.execution.ec import ExperimentController \r
+from nepi.execution.runner import ExperimentRunner\r
+from nepi.util.netgraph import TopologyType\r
+import nepi.data.processing.ccn.parser as ccn_parser\r
+\r
+import networkx\r
+import socket\r
+import os\r
+import numpy\r
+from scipy import stats\r
+from matplotlib import pyplot\r
+import math\r
+import random\r
+\r
+def avg_interest_rtt(ec, run):\r
+    logs_dir = ec.run_dir\r
+    \r
+    # Parse downloaded CCND logs\r
+    (graph,\r
+      content_names,\r
+      interest_expiry_count,\r
+      interest_dupnonce_count,\r
+      interest_count,\r
+      content_count) = ccn_parser.process_content_history_logs(\r
+        logs_dir, ec.netgraph.topology)\r
+\r
+    # statistics on RTT\r
+    rtts = [content_names[content_name]["rtt"] \\r
+            for content_name in content_names.keys()]\r
+\r
+    # sample mean and standard deviation\r
+    sample = numpy.array(rtts)\r
+    n, min_max, mean, var, skew, kurt = stats.describe(sample)\r
+    std = math.sqrt(var)\r
+    ci = stats.t.interval(0.95, n-1, loc = mean, \r
+            scale = std/math.sqrt(n))\r
+\r
+    global metrics\r
+    metrics.append((mean, ci[0], ci[1]))\r
+    \r
+    return mean\r
+\r
+def normal_law(ec, run, sample):\r
+    x = numpy.array(sample)\r
+    n = len(sample)\r
+    std = x.std()\r
+    se = std / math.sqrt(n)\r
+    m = x.mean()\r
+    se95 = se * 2\r
+    \r
+    return m * 0.05 >= se95\r
+\r
+def post_process(ec, runs):\r
+    global metrics\r
+    \r
+    # plot convergence graph\r
+    y = numpy.array([float(m[0]) for m in metrics])\r
+    low = numpy.array([float(m[1]) for m in metrics])\r
+    high = numpy.array([float(m[2]) for m in metrics])\r
+    error = [y - low, high - y]\r
+    x = range(1,runs + 1)\r
+\r
+    # plot average RTT and confidence interval for each iteration\r
+    pyplot.errorbar(x, y, yerr = error, fmt='o')\r
+    pyplot.plot(x, y, 'r-')\r
+    pyplot.xlim([0.5, runs + 0.5])\r
+    pyplot.xticks(numpy.arange(1, len(y)+1, 1))\r
+    pyplot.xlabel('Iteration')\r
+    pyplot.ylabel('Average RTT')\r
+    pyplot.grid()\r
+    pyplot.savefig("plot.png")\r
+    pyplot.show()\r
+\r
+content_name = "ccnx:/test/bunny.ts"\r
+\r
+STOP_TIME = "5000s"\r
+\r
+repofile = os.path.join(\r
+        os.path.dirname(os.path.realpath(__file__)), \r
+        "repoFile1.0.8.2")\r
+\r
+def get_simulator(ec):\r
+    simulator = ec.filter_resources("LinuxNS3Simulation")\r
+\r
+    if not simulator:\r
+        node = ec.register_resource("LinuxNode")\r
+        ec.set(node, "hostname", "localhost")\r
+\r
+        simu = ec.register_resource("LinuxNS3Simulation")\r
+        ec.register_connection(simu, node)\r
+        return simu\r
+\r
+    return simulator[0]\r
+\r
+def add_collector(ec, trace_name, subdir, newname = None):\r
+    collector = ec.register_resource("Collector")\r
+    ec.set(collector, "traceName", trace_name)\r
+    ec.set(collector, "subDir", subdir)\r
+    if newname:\r
+        ec.set(collector, "rename", newname)\r
+\r
+    return collector\r
+\r
+def add_dce_host(ec, nid):\r
+    simu = get_simulator(ec)\r
+    \r
+    host = ec.register_resource("ns3::Node")\r
+    ec.set(host, "enableStack", True)\r
+    ec.register_connection(host, simu)\r
+\r
+    # Annotate the graph\r
+    ec.netgraph.annotate_node(nid, "host", host)\r
+    \r
+def add_dce_ccnd(ec, nid):\r
+    # Retrieve annotation from netgraph\r
+    host = ec.netgraph.node_annotation(nid, "host")\r
+    \r
+    # Add dce ccnd to the dce node\r
+    ccnd = ec.register_resource("ns3::LinuxDceCCND")\r
+    ec.set (ccnd, "stackSize", 1<<20)\r
+    ec.set (ccnd, "debug", 7)\r
+    ec.set (ccnd, "capacity", 50000)\r
+    ec.set (ccnd, "StartTime", "1s")\r
+    ec.set (ccnd, "StopTime", STOP_TIME)\r
+    ec.register_connection(ccnd, host)\r
+\r
+    # Collector to retrieve ccnd log\r
+    collector = add_collector(ec, "stderr", str(nid), "log")\r
+    ec.register_connection(collector, ccnd)\r
+\r
+    # Annotate the graph\r
+    ec.netgraph.annotate_node(nid, "ccnd", ccnd)\r
+\r
+def add_dce_ccnr(ec, nid):\r
+    # Retrieve annotation from netgraph\r
+    host = ec.netgraph.node_annotation(nid, "host")\r
+    \r
+    # Add a CCN content repository to the dce node\r
+    ccnr = ec.register_resource("ns3::LinuxDceCCNR")\r
+    ec.set (ccnr, "repoFile1", repofile) \r
+    ec.set (ccnr, "stackSize", 1<<20)\r
+    ec.set (ccnr, "StartTime", "2s")\r
+    ec.set (ccnr, "StopTime", STOP_TIME)\r
+    ec.register_connection(ccnr, host)\r
+\r
+def add_dce_ccncat(ec, nid):\r
+    # Retrieve annotation from netgraph\r
+    host = ec.netgraph.node_annotation(nid, "host")\r
+   \r
+    # Add a ccncat application to the dce host\r
+    ccncat = ec.register_resource("ns3::LinuxDceCCNCat")\r
+    ec.set (ccncat, "contentName", content_name)\r
+    ec.set (ccncat, "stackSize", 1<<20)\r
+    ec.set (ccncat, "StartTime", "8s")\r
+    ec.set (ccncat, "StopTime", STOP_TIME)\r
+    ec.register_connection(ccncat, host)\r
+\r
+def add_dce_fib_entry(ec, nid1, nid2):\r
+    # Retrieve annotations from netgraph\r
+    host1 = ec.netgraph.node_annotation(nid1, "host")\r
+    net = ec.netgraph.edge_net_annotation(nid1, nid2)\r
+    ip2 = net[nid2]\r
+\r
+    # Add FIB entry between peer hosts\r
+    ccndc = ec.register_resource("ns3::LinuxDceFIBEntry")\r
+    ec.set (ccndc, "protocol", "udp") \r
+    ec.set (ccndc, "uri", "ccnx:/") \r
+    ec.set (ccndc, "host", ip2)\r
+    ec.set (ccndc, "stackSize", 1<<20)\r
+    ec.set (ccndc, "StartTime", "2s")\r
+    ec.set (ccndc, "StopTime", STOP_TIME)\r
+    ec.register_connection(ccndc, host1)\r
+\r
+def add_dce_net_iface(ec, nid1, nid2):\r
+    # Retrieve annotations from netgraph\r
+    host = ec.netgraph.node_annotation(nid1, "host")\r
+    net = ec.netgraph.edge_net_annotation(nid1, nid2)\r
+    ip1 = net[nid1]\r
+    prefix = net["prefix"]\r
+\r
+    dev = ec.register_resource("ns3::PointToPointNetDevice")\r
+    ec.set(dev,"DataRate", "5Mbps")\r
+    ec.set(dev, "ip", ip1)\r
+    ec.set(dev, "prefix", prefix)\r
+    ec.register_connection(host, dev)\r
+\r
+    queue = ec.register_resource("ns3::DropTailQueue")\r
+    ec.register_connection(dev, queue)\r
+\r
+    return dev\r
+\r
+def add_edge(ec, nid1, nid2):\r
+    ### Add network interfaces to hosts\r
+    p2p1 = add_dce_net_iface(ec, nid1, nid2)\r
+    p2p2 = add_dce_net_iface(ec, nid2, nid1)\r
+\r
+    # Create point to point link between interfaces\r
+    chan = ec.register_resource("ns3::PointToPointChannel")\r
+    ec.set(chan, "Delay", "0ms")\r
+\r
+    ec.register_connection(chan, p2p1)\r
+    ec.register_connection(chan, p2p2)\r
+\r
+    #### Add routing between CCN nodes\r
+    add_dce_fib_entry(ec, nid1, nid2)\r
+    add_dce_fib_entry(ec, nid2, nid1)\r
+\r
+def add_node(ec, nid):\r
+    ### Add CCN nodes (ec.netgraph holds the topology graph)\r
+    add_dce_host(ec, nid)\r
+    add_dce_ccnd(ec, nid)\r
+        \r
+    if nid == ec.netgraph.targets()[0]:\r
+        add_dce_ccnr(ec, nid)\r
+\r
+    if nid == ec.netgraph.sources()[0]:\r
+        add_dce_ccncat(ec, nid)\r
+\r
+def wait_guids(ec):\r
+    return ec.filter_resources("ns3::LinuxDceCCNCat")\r
+\r
+if __name__ == '__main__':\r
+\r
+    metrics = []\r
+\r
+    # topology translation to NEPI model\r
+    ec = ExperimentController("dce_4n_linear",\r
+        topo_type = TopologyType.LINEAR, \r
+        node_count = 4,\r
+        assign_st = True,\r
+        assign_ips = True,\r
+        add_node_callback = add_node,\r
+       add_edge_callback = add_edge)\r
+\r
+    #### Run experiment until metric convergence\r
+    rnr = ExperimentRunner()\r
+    runs = rnr.run(ec,\r
+            min_runs = 10,\r
+            max_runs = 100, \r
+            compute_metric_callback = avg_interest_rtt,\r
+            evaluate_convergence_callback = normal_law,\r
+            wait_guids = wait_guids(ec))\r
+   \r
+    ### post processing\r
+    post_process(ec, runs)\r
+\r
+\r
diff --git a/examples/ccn_emu_live/planetlab_4_nodes_linear.py b/examples/ccn_emu_live/planetlab_4_nodes_linear.py
new file mode 100644 (file)
index 0000000..d027a59
--- /dev/null
@@ -0,0 +1,368 @@
+#!/usr/bin/env python\r
+\r
+###############################################################################\r
+#\r
+#    NEPI, a framework to manage network experiments\r
+#\r
+#    This program is free software: you can redistribute it and/or modify\r
+#    it under the terms of the GNU General Public License as published by\r
+#    the Free Software Foundation, either version 3 of the License, or\r
+#    (at your option) any later version.\r
+#\r
+#    This program is distributed in the hope that it will be useful,\r
+#    but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
+#    GNU General Public License for more details.\r
+#\r
+#    You should have received a copy of the GNU General Public License\r
+#    along with this program.  If not, see <http://www.gnu.org/licenses/>.\r
+#\r
+# Author: Alina Quereilhac <alina.quereilhac@inria.fr>\r
+#\r
+###############################################################################\r
+\r
+from nepi.execution.ec import ExperimentController \r
+from nepi.execution.runner import ExperimentRunner\r
+from nepi.util.netgraph import TopologyType\r
+import nepi.data.processing.ccn.parser as ccn_parser\r
+\r
+import networkx\r
+import socket\r
+import os\r
+import numpy\r
+from scipy import stats\r
+from matplotlib import pyplot\r
+import math\r
+import random\r
+\r
+from optparse import OptionParser\r
+\r
+usage = ("usage: %prog -s <pl-slice> -u <pl-user> -p <pl-password> "\r
+     "-k <pl-ssh-key> -N <nodes>")\r
+\r
+parser = OptionParser(usage = usage)\r
+parser.add_option("-s", "--pl-slice", dest="pl_slice",\r
+        help="PlanetLab slicename", type="str")\r
+parser.add_option("-u", "--pl-user", dest="pl_user",\r
+        help="PlanetLab web username", type="str")\r
+parser.add_option("-p", "--pl-password", dest="pl_password",\r
+        help="PlanetLab web password", type="str")\r
+parser.add_option("-k", "--pl-ssh-key", dest="pl_ssh_key",\r
+        help="Path to private SSH key associated with the PL account",\r
+        type="str")\r
+parser.add_option("-N", "--nodes", dest="nodes",\r
+        help="Comma separated list of Planetlab nodes",\r
+        type="str")\r
+\r
+(options, args) = parser.parse_args()\r
+\r
+pl_slice = options.pl_slice\r
+pl_ssh_key = options.pl_ssh_key\r
+pl_user = options.pl_user\r
+pl_password = options.pl_password\r
+NODES = options.nodes.strip().split(",")\r
+\r
+def avg_interest_rtt(ec, run):\r
+    logs_dir = ec.run_dir\r
+    \r
+    # Parse downloaded CCND logs\r
+    (graph,\r
+      content_names,\r
+      interest_expiry_count,\r
+      interest_dupnonce_count,\r
+      interest_count,\r
+      content_count) = ccn_parser.process_content_history_logs(\r
+        logs_dir, ec.netgraph.topology)\r
+\r
+    # statistics on RTT\r
+    rtts = [content_names[content_name]["rtt"] \\r
+            for content_name in content_names.keys()]\r
+\r
+    # sample mean and standard deviation\r
+    sample = numpy.array(rtts)\r
+    n, min_max, mean, var, skew, kurt = stats.describe(sample)\r
+    std = math.sqrt(var)\r
+    ci = stats.t.interval(0.95, n-1, loc = mean, \r
+            scale = std/math.sqrt(n))\r
+\r
+    global metrics\r
+    metrics.append((mean, ci[0], ci[1]))\r
+    \r
+    return mean\r
+\r
+def normal_law(ec, run, sample):\r
+    print "SAMPLE", sample\r
+\r
+    x = numpy.array(sample)\r
+    n = len(sample)\r
+    std = x.std()\r
+    se = std / math.sqrt(n)\r
+    m = x.mean()\r
+    se95 = se * 2\r
+    \r
+    return m * 0.05 >= se95\r
+\r
+def post_process(ec, runs):\r
+    global metrics\r
+    \r
+    # plot convergence graph\r
+    y = numpy.array([float(m[0]) for m in metrics])\r
+    low = numpy.array([float(m[1]) for m in metrics])\r
+    high = numpy.array([float(m[2]) for m in metrics])\r
+    error = [y - low, high - y]\r
+    x = range(1,runs + 1)\r
+\r
+    # plot average RTT and confidence interval for each iteration\r
+    pyplot.errorbar(x, y, yerr = error, fmt='o')\r
+    pyplot.plot(x, y, 'r-')\r
+    pyplot.xlim([0.5, runs + 0.5])\r
+    pyplot.xticks(numpy.arange(1, len(y)+1, 1))\r
+    pyplot.xlabel('Iteration')\r
+    pyplot.ylabel('Average RTT')\r
+    pyplot.grid()\r
+    pyplot.savefig("plot.png")\r
+    pyplot.show()\r
+\r
+content_name = "ccnx:/test/bunny.ts"\r
+\r
+repofile = os.path.join(\r
+        os.path.dirname(os.path.realpath(__file__)), \r
+        "repoFile1.0.8.2")\r
+\r
+def get_simulator(ec):\r
+    simulator = ec.filter_resources("LinuxNS3Simulation")\r
+\r
+    if not simulator:\r
+        node = ec.register_resource("LinuxNode")\r
+        ec.set(node, "hostname", "localhost")\r
+\r
+        simu = ec.register_resource("LinuxNS3Simulation")\r
+        ec.register_connection(simu, node)\r
+        return simu\r
+\r
+    return simulator[0]\r
+\r
+def add_collector(ec, trace_name, subdir, newname = None):\r
+    collector = ec.register_resource("Collector")\r
+    ec.set(collector, "traceName", trace_name)\r
+    ec.set(collector, "subDir", subdir)\r
+    if newname:\r
+        ec.set(collector, "rename", newname)\r
+\r
+    return collector\r
+\r
+def add_dce_host(ec, nid):\r
+    simu = get_simulator(ec)\r
+    \r
+    host = ec.register_resource("ns3::Node")\r
+    ec.set(host, "enableStack", True)\r
+    ec.register_connection(host, simu)\r
+\r
+    # Annotate the graph\r
+    ec.netgraph.annotate_node(nid, "host", host)\r
+    \r
+def add_dce_ccnd(ec, nid):\r
+    # Retrieve annotation from netgraph\r
+    host = ec.netgraph.node_annotation(nid, "host")\r
+    \r
+    # Add dce ccnd to the dce node\r
+    ccnd = ec.register_resource("ns3::LinuxDceCCND")\r
+    ec.set (ccnd, "stackSize", 1<<20)\r
+    ec.set (ccnd, "debug", 7)\r
+    ec.set (ccnd, "capacity", 50000)\r
+    ec.set (ccnd, "StartTime", "1s")\r
+    ec.set (ccnd, "StopTime", STOP_TIME)\r
+    ec.register_connection(ccnd, host)\r
+\r
+    # Collector to retrieve ccnd log\r
+    collector = add_collector(ec, "stderr", str(nid), "log")\r
+    ec.register_connection(collector, ccnd)\r
+\r
+    # Annotate the graph\r
+    ec.netgraph.annotate_node(nid, "ccnd", ccnd)\r
+\r
+def add_dce_ccnr(ec, nid):\r
+    # Retrieve annotation from netgraph\r
+    host = ec.netgraph.node_annotation(nid, "host")\r
+    \r
+    # Add a CCN content repository to the dce node\r
+    ccnr = ec.register_resource("ns3::LinuxDceCCNR")\r
+    ec.set (ccnr, "repoFile1", repofile) \r
+    ec.set (ccnr, "stackSize", 1<<20)\r
+    ec.set (ccnr, "StartTime", "2s")\r
+    ec.set (ccnr, "StopTime", STOP_TIME)\r
+    ec.register_connection(ccnr, host)\r
+\r
+def add_dce_ccncat(ec, nid):\r
+    # Retrieve annotation from netgraph\r
+    host = ec.netgraph.node_annotation(nid, "host")\r
+   \r
+    # Add a ccncat application to the dce host\r
+    ccncat = ec.register_resource("ns3::LinuxDceCCNCat")\r
+    ec.set (ccncat, "contentName", content_name)\r
+    ec.set (ccncat, "stackSize", 1<<20)\r
+    ec.set (ccncat, "StartTime", "8s")\r
+    ec.set (ccncat, "StopTime", STOP_TIME)\r
+    ec.register_connection(ccncat, host)\r
+\r
+def add_dce_fib_entry(ec, nid1, nid2):\r
+    # Retrieve annotations from netgraph\r
+    host1 = ec.netgraph.node_annotation(nid1, "host")\r
+    net = ec.netgraph.edge_net_annotation(nid1, nid2)\r
+    ip2 = net[nid2]\r
+\r
+    # Add FIB entry between peer hosts\r
+    ccndc = ec.register_resource("ns3::LinuxDceFIBEntry")\r
+    ec.set (ccndc, "protocol", "udp") \r
+    ec.set (ccndc, "uri", "ccnx:/") \r
+    ec.set (ccndc, "host", ip2)\r
+    ec.set (ccndc, "stackSize", 1<<20)\r
+    ec.set (ccndc, "StartTime", "2s")\r
+    ec.set (ccndc, "StopTime", STOP_TIME)\r
+    ec.register_connection(ccndc, host1)\r
+\r
+def add_dce_net_iface(ec, nid1, nid2):\r
+    # Retrieve annotations from netgraph\r
+    host = ec.netgraph.node_annotation(nid1, "host")\r
+    net = ec.netgraph.edge_net_annotation(nid1, nid2)\r
+    ip1 = net[nid1]\r
+    prefix = net["prefix"]\r
+\r
+    dev = ec.register_resource("ns3::PointToPointNetDevice")\r
+    ec.set(dev,"DataRate", "5Mbps")\r
+    ec.set(dev, "ip", ip1)\r
+    ec.set(dev, "prefix", prefix)\r
+    ec.register_connection(host, dev)\r
+\r
+    queue = ec.register_resource("ns3::DropTailQueue")\r
+    ec.register_connection(dev, queue)\r
+\r
+    return dev\r
+\r
+def add_pl_host(ec, nid):\r
+    hostname = NODES[nid]\r
+\r
+    # Add a planetlab host to the experiment description\r
+    host = ec.register_resource("PlanetlabNode")\r
+    ec.set(host, "hostname", hostname)\r
+    ec.set(host, "username", pl_slice)\r
+    ec.set(host, "identity", pl_ssh_key)\r
+    ec.set(host, "cleanExperiment", True)\r
+    ec.set(host, "cleanProcesses", True)\r
+\r
+    # Annotate the graph\r
+    ec.netgraph.annotate_node(nid, "hostname", hostname)\r
+    ec.netgraph.annotate_node(nid, "host", host)\r
+    \r
+    # Annotate the graph node with an ip address\r
+    ip = socket.gethostbyname(hostname)\r
+    ec.netgraph.annotate_node_ip(nid, ip)\r
+\r
+def add_pl_ccnd(ec, nid):\r
+    # Retrieve annotation from netgraph\r
+    host = ec.netgraph.node_annotation(nid, "host")\r
+    \r
+    # Add a CCN daemon to the planetlab node\r
+    ccnd = ec.register_resource("LinuxCCND")\r
+    ec.set(ccnd, "debug", 7)\r
+    ec.register_connection(ccnd, host)\r
+    \r
+    # Collector to retrieve ccnd log\r
+    collector = add_collector(ec, "stderr", str(nid), "log")\r
+    ec.register_connection(collector, ccnd)\r
+\r
+    # Annotate the graph\r
+    ec.netgraph.annotate_node(nid, "ccnd", ccnd)\r
+\r
+def add_pl_ccnr(ec, nid):\r
+    # Retrieve annotation from netgraph\r
+    ccnd = ec.netgraph.node_annotation(nid, "ccnd")\r
+    \r
+    # Add a CCN content repository to the planetlab node\r
+    ccnr = ec.register_resource("LinuxCCNR")\r
+\r
+    ec.set(ccnr, "repoFile1", repofile)\r
+    ec.register_connection(ccnr, ccnd)\r
+\r
+def add_pl_ccncat(ec, nid):\r
+    # Retrieve annotation from netgraph\r
+    ccnd = ec.netgraph.node_annotation(nid, "ccnd")\r
+    \r
+    # Add a CCN cat application to the planetlab node\r
+    ccncat = ec.register_resource("LinuxCCNCat")\r
+    ec.set(ccncat, "contentName", content_name)\r
+    ec.register_connection(ccncat, ccnd)\r
+\r
+def add_pl_fib_entry(ec, nid1, nid2):\r
+    # Retrieve annotations from netgraph\r
+    ccnd1 = ec.netgraph.node_annotation(nid1, "ccnd")\r
+    hostname2 = ec.netgraph.node_annotation(nid2, "hostname")\r
+    \r
+    # Add a FIB entry between one planetlab node and its peer\r
+    entry = ec.register_resource("LinuxFIBEntry")\r
+    ec.set(entry, "host", hostname2)\r
+    ec.register_connection(entry, ccnd1)\r
+\r
+    # Collector to retrieve peering ping output (to measure neighbors delay)\r
+    ec.enable_trace(entry, "ping")\r
+    collector = add_collector(ec, "ping", str(nid1))\r
+    ec.register_connection(collector, entry)\r
+\r
+    return entry\r
+\r
+def add_node(ec, nid):\r
+    ### Add CCN nodes (ec.netgraph holds the topology graph)\r
+    add_dce_host(ec, nid)\r
+    add_dce_ccnd(ec, nid)\r
+        \r
+    if nid == ec.netgraph.targets()[0]:\r
+        add_dce_ccnr(ec, nid)\r
+\r
+    if nid == ec.netgraph.sources()[0]:\r
+        add_dce_ccncat(ec, nid)\r
+\r
+def add_edge(ec, nid1, nid2):\r
+    #### Add connections between CCN nodes\r
+    add_pl_fib_entry(ec, nid1, nid2)\r
+    add_pl_fib_entry(ec, nid2, nid1)\r
+\r
+def add_node(ec, nid):\r
+    ### Add CCN nodes (ec.netgraph holds the topology graph)\r
+    add_pl_host(ec, nid)\r
+    add_pl_ccnd(ec, nid)\r
+        \r
+    if nid == ec.netgraph.targets()[0]:\r
+        add_pl_ccnr(ec, nid)\r
+\r
+    if nid == ec.netgraph.sources()[0]:\r
+        add_pl_ccncat(ec, nid)\r
+\r
+def wait_guids(ec):\r
+    return ec.filter_resources("LinuxCCNCat")\r
+\r
+if __name__ == '__main__':\r
+\r
+    metrics = []\r
+\r
+    # topology translation to NEPI model\r
+    ec = ExperimentController("pl_4n_linear",\r
+        topo_type = TopologyType.LINEAR, \r
+        node_count = 4,\r
+        assign_st = True,\r
+        assign_ips = True,\r
+        add_node_callback = add_node,\r
+       add_edge_callback = add_edge)\r
+\r
+    #### Run experiment until metric convergence\r
+    rnr = ExperimentRunner()\r
+    runs = rnr.run(ec,\r
+            min_runs = 10,\r
+            max_runs = 100, \r
+            compute_metric_callback = avg_interest_rtt,\r
+            evaluate_convergence_callback = normal_law,\r
+            wait_guids = wait_guids(ec))\r
+   \r
+    ### post processing\r
+    post_process(ec, runs)\r
+\r
+\r