**breaking** mini-ndn: re-design

refs: #5062

Everything is now done through examples like Mininet.
bin/minindn no longer provided as a binary installed in the system
bin/minindnedit GUI: will no longer be maintained
Remove cluster edition, will be re-introduced later

Change-Id: Id4ef137cb2a04d1b0dd24d01941757363bbf7d26
diff --git a/.gitignore b/.gitignore
index 1ea55fa..e56d449 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,24 +1,19 @@
+# Python
 build
-crypto
-ChronoSync
 dist
-doc/html
-doc/latex
-.DS_Store
-examples
-infoedit
-jndn
 Mini_NDN.egg-info
-mininet
-NFD
-NLSR
-ndn-cpp
-ndn-cxx
-ndn-js
-ndn-tools
-openflow
-PyNDN2
-PSync
-util
-.vagrant
 *.pyc
+
+# Docs
+docs/html
+docs/latex
+docs/_build
+
+# Misc
+.DS_Store
+mininet
+ndn-src
+openflow
+
+# Vagrant
+.vagrant
diff --git a/INSTALL.md b/INSTALL.md
deleted file mode 100644
index f263972..0000000
--- a/INSTALL.md
+++ /dev/null
@@ -1,92 +0,0 @@
-Mini-NDN Installation Instructions
-================================
-
-### What equipment will I need?
-
-For this guide, you will need a laptop/desktop with a recent version
-of a Linux distro such as Ubuntu or any of its variants. Fedora is not
-officially supported but has also been reported to work for some
-users. For this guide, the _Ubuntu 18.04 LTS_ release was used.
-Also, note that you'll need administrative privileges in order to
-download and install extra packages and also to execute **Mini-NDN**.
-
-### Installing **Mini-NDN**
-
-NOTE: Mini-NDN, while providing a high level of emulation of hosts,
-requires programs to be installed onto your computer. It will not work
-if they are not installed. If you do not want NDN software installed
-onto your computer, you can use a virtual machine, which can be quite
-simply set up with the provided vagrantfile.
-
-If you have all the dependencies (see sections below) installed simply clone this repository and run:
-
-    ./install.sh -i
-
-else if you don't have the dependencies, the following command will install them from source along with Mini-NDN:
-
-    ./install.sh -a
-
-If you want to install the dependencies manually or from the Named Data PPA, follow the instructions below:
-
-### Installing NDN
-
-Each node in **Mini-NDN** will run the official implementation of NDN installed on your system. The following dependencies are needed:
-
-Mini-NDN uses NFD, NLSR, and ndn-tools.
-
-To install NFD:
-https://named-data.net/doc/NFD/current/INSTALL.html
-
-To install NLSR:
-https://named-data.net/doc/NLSR/current/INSTALL.html
-
-To install ndn-tools:
-https://github.com/named-data/ndn-tools
-
-Note that all three of these can be installed from the Named Data PPA. Instructions for setting it up can
-be found in the NFD insallation instructions. Note that PPA and installs from source **cannot** be mixed.
-
-###Special Instructions for PPA Installs
-
-If you are using a custom nfd.conf file in an experiment, you should place it in /usr/local/etc/ndn/
-rather than /etc/ndn/. This is to avoid a bug from the default configuration file for the PPA, which
-is incompatiable with Mini-NDN.
-
-### Installing Mininet
-
-**Mini-NDN** is based on Mininet. To install Mininet:
-
-First, clone Mininet from github:
-
-    git clone --depth 1 https://github.com/mininet/mininet.git
-
-After Mininet source is on your system, run the following command to
-install Mininet core dependencies and Open vSwitch:
-
-    ./util/install.sh -nv
-
-To check if Mininet is working correctly, run this test:
-
-    sudo mn --test pingall
-
-This will print out a series of statements that show the test setup
-and the results of the test. Look for `Results:` two-thirds of the way
-down where it will indicate the percentage of dropped packets.
-Your results should show "0% dropped (2/2 received)".
-
-### Installing Infoedit
-
-Infoedit is used to edit configuration files such as NFD configuration
-file.
-
-To install infoedit:
-https://github.com/NDN-Routing/infoedit
-
-### Verification
-
-You can use these steps to verify your installation:
-
-1. Issue the command: `sudo minindn --experiment=pingall --nPings=50`
-2. When the `mini-ndn>` CLI prompt appears, the experiment has finished. On the Mini-NDN CLI, issue the command `exit` to exit the experiment.
-3. Issue the command: `grep -c content /tmp/minindn/*/ping-data/*.txt`. Each file should report a count of 50.
-4. Issue the command: `grep -c timeout /tmp/minindn/*/ping-data/*.txt`. Each file should report a count of 0.
\ No newline at end of file
diff --git a/README.md b/README.md
index 146d953..5a1d007 100644
--- a/README.md
+++ b/README.md
@@ -7,10 +7,9 @@
 ### What is Mini-NDN?
 
 Mini-NDN is a lightweight networking emulation tool that enables testing, experimentation, and
-research on the NDN platform. Based on [Mini-CCNx](https://github.com/chesteve/mn-ccnx) which
-is a fork of [Mininet](https://github.com/mininet/mininet), Mini-NDN uses the NDN libraries,
-NFD, NLSR, and tools released by the [NDN project](http://named-data.net/codebase/platform/)
-to emulate an NDN network on a single system.
+research on the NDN platform based on [Mininet](https://github.com/mininet/mininet).
+Mini-NDN uses the NDN libraries, NFD, NLSR, and tools released by the
+[NDN project](http://named-data.net/codebase/platform/) to emulate an NDN network on a single system.
 
 Mini-NDN is open and free software licensed under the GPL 3.0 license. Mini-NDN is free to all
 users and developers. For more information about licensing details and limitations,
@@ -25,9 +24,9 @@
 
 ### Documentation
 
-* [Installation](INSTALL.md)
-* [Getting Started](docs/GETTING-STARTED.md)
-* [Mini-NDN Edit](docs/GUI.md)
-* [Running Experiments](docs/EXPERIMENTS.md)
-* [Mini-NDN Redmine](http://redmine.named-data.net/projects/mini-ndn)
-* [Contributor's Guide](https://github.com/named-data/NFD/blob/master/CONTRIBUTING.md)
+Please refer to http://minindn.memphis.edu/ or [docs/index.rst](docs/index.rst) for installation, usage, and other documentation.
+The documentation can be built using:
+
+    ./install.sh -d
+
+and is available under `docs/_build/html`.
diff --git a/Vagrantfile b/Vagrantfile
index 6083675..f3bb364 100644
--- a/Vagrantfile
+++ b/Vagrantfile
@@ -2,18 +2,27 @@
 # vi: set ft=ruby :
 
 $script = <<SCRIPT
+ln -s /vagrant /home/vagrant/mini-ndn
 
-git clone --depth 1 https://github.com/named-data/mini-ndn.git
-cd mini-ndn
+# Check if install.sh is present or someone just copied the Vagrantfile directly
+if [[ -f /home/vagrant/mini-ndn/install.sh ]]; then
+  pushd /home/vagrant/mini-ndn
+else
+  # Remove the symlink
+  rm /home/vagrant/mini-ndn
+  git clone --depth 1 https://github.com/named-data/mini-ndn.git
+  pushd mini-ndn
+fi
 ./install.sh -a
+
 SCRIPT
 
 Vagrant.configure(2) do |config|
-  config.vm.box = "bento/ubuntu-16.04"
+  config.vm.box = "ubuntu/bionic64"
   config.vm.provision "shell", privileged: false, inline: $script
   config.vm.provider "virtualbox" do |vb|
     vb.name = "mini-ndn_box"
-    vb.memory = 2000
-    vb.cpus = 2
+    vb.memory = 4096
+    vb.cpus = 4
   end
 end
diff --git a/bin/minindn b/bin/minindn
deleted file mode 100755
index e0b4446..0000000
--- a/bin/minindn
+++ /dev/null
@@ -1,462 +0,0 @@
-# -*- Mode:python; c-file-style:"gnu"; indent-tabs-mode:nil -*- */
-#
-# Copyright (C) 2015-2019, The University of Memphis,
-#                          Arizona Board of Regents,
-#                          Regents of the University of California.
-#
-# This file is part of Mini-NDN.
-# See AUTHORS.md for a complete list of Mini-NDN authors and contributors.
-#
-# Mini-NDN is free software: you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# Mini-NDN is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with Mini-NDN, e.g., in COPYING.md file.
-# If not, see <http://www.gnu.org/licenses/>.
-#
-# This file incorporates work covered by the following copyright and
-# permission notice:
-#
-#   Mininet 2.3.0d1 License
-#
-#   Copyright (c) 2013-2016 Open Networking Laboratory
-#   Copyright (c) 2009-2012 Bob Lantz and The Board of Trustees of
-#   The Leland Stanford Junior University
-#
-#   Original authors: Bob Lantz and Brandon Heller
-#
-#   We are making Mininet available for public use and benefit with the
-#   expectation that others will use, modify and enhance the Software and
-#   contribute those enhancements back to the community. However, since we
-#   would like to make the Software available for broadest use, with as few
-#   restrictions as possible permission is hereby granted, free of charge, to
-#   any person obtaining a copy of this Software to deal in the Software
-#   under the copyrights without restriction, including without limitation
-#   the rights to use, copy, modify, merge, publish, distribute, sublicense,
-#   and/or sell copies of the Software, and to permit persons to whom the
-#   Software is furnished to do so, subject to the following conditions:
-#
-#   The above copyright notice and this permission notice shall be included
-#   in all copies or substantial portions of the Software.
-#
-#   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
-#   OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
-#   MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
-#   IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
-#   CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
-#   TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
-#   SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-#
-#   The name and trademarks of copyright holder(s) may NOT be used in
-#   advertising or publicity pertaining to the Software or any derivatives
-#   without specific, written prior permission.
-
-from mininet.topo import Topo
-from mininet.net import Mininet
-from mininet.log import setLogLevel, output, info, error, warn
-from mininet.link import TCLink
-from mininet.util import ipStr, ipParse
-
-from mininet.examples.cluster import MininetCluster, RoundRobinPlacer, ClusterCleanup
-from mininet.examples.clustercli import ClusterCLI
-
-from ndn import ExperimentManager
-from ndn.experiments.experiment import Experiment
-from ndn.ndn_host import NdnHost, CpuLimitedNdnHost, RemoteNdnHost
-from ndn.conf_parser import parse_hosts, parse_switches, parse_links
-from ndn.remote_ndn_link import RemoteNdnLink, RemoteGRENdnLink
-from ndn.placer import GuidedPlacer, PopulatePlacement
-from ndn.nfd import Nfd
-from ndn.util import ssh, scp, MiniNDNCLI, ProgramOptions
-
-import os.path, time
-import shutil
-import argparse
-import datetime
-from os.path import expanduser
-import sys
-import signal
-from subprocess import call
-import glob
-from functools import partial
-import re
-
-try:
-    import argcomplete
-except ImportError:
-    pass
-
-VERSION_NUMBER = "0.4.0"
-INSTALL_DIR='/usr/local/etc/mini-ndn/'
-
-class PrintExperimentNames(argparse.Action):
-    def __init__(self, option_strings, dest, nargs=0, help=None):
-        super(PrintExperimentNames, self).__init__(option_strings=option_strings, dest=dest, nargs=nargs, help=help)
-
-    def __call__(self, parser, namespace, values, option_string=None):
-        experimentNames = ExperimentManager.getExperimentNames()
-
-        print("Mini-NDN experiments:")
-        for experiment in experimentNames:
-            print("  {}".format(experiment))
-
-        sys.exit(0)
-
-def createResultsDir(resultDir, faces, rType):
-    if faces == 0:
-        faces = "all"
-
-    routingChoice = "/{}/".format(rType)
-
-    resultDir = "{}/{}/faces-{}".format(resultDir, routingChoice, faces)
-    resultDir = os.path.abspath(resultDir)
-
-    if not os.path.isdir(resultDir):
-        os.makedirs(resultDir)
-    else:
-        warn("Results directory ({}) already exists!".format(resultDir))
-        sys.exit(1)
-
-    info("Results will be stored at: {}".format(resultDir))
-    return resultDir
-
-def parse_args():
-    parser = argparse.ArgumentParser(prog='minindn')
-
-    # nargs='?' required here since optional argument
-    parser.add_argument('tempfile', nargs='?', default=INSTALL_DIR + 'default-topology.conf',
-                        help="If no template_file is given, topologies/default-topology.conf (given sample file) will be used.")
-
-    parser.add_argument("--ctime", type=int, default=60,
-                        help="Specify convergence time for the topology (Default: 60 seconds)")
-
-    parser.add_argument("--experiment", choices=[experiment for experiment in ExperimentManager.getExperimentNames()],
-                        help="Runs the specified experiment")
-
-    parser.add_argument("--faces", type=int, default=3,
-                        help="Specify number of max faces per prefix for NLSR 0-60")
-
-    parser.add_argument("--routing", dest="routingType", default='link-state', choices=['link-state', 'hr', 'dry'],
-                        help="""choices for routing are 'link-state' for link state, 'hr' for hyperbolic, and 'dry'
-                        to test hyperbolic routing and compare with link state. Default is link-state.""")
-
-    parser.add_argument("--no-nlsr", action="store_false", dest="isNlsrEnabled",
-                        help="Run mini-ndn without NLSR routing")
-
-    parser.add_argument("--list-experiments", action=PrintExperimentNames,
-                        help="Lists the names of all available experiments")
-
-    parser.add_argument("--no-cli", action="store_false", dest="isCliEnabled",
-                        help="Run experiments and exit without showing the command line interface")
-
-    parser.add_argument("--nPings", type=int, default=300,
-                        help="Number of pings to perform between each node in the experiment")
-
-    # store_true stores default value of False
-    parser.add_argument("--nlsr-security", action="store_true", dest="nlsrSecurity",
-                        help="Enables NLSR security")
-
-    parser.add_argument("-t", "--testbed", action="store_true", dest="testbed",
-                         help="Instantiates a snapshot of the NDN Testbed irrespective of the tempfile provided")
-
-    parser.add_argument("--work-dir", action="store", dest="workDir", default="/tmp/minindn",
-                        help="Specify the working directory; default is /tmp/minindn")
-
-    parser.add_argument("--result-dir", action="store", dest="resultDir", default=None,
-                        help="Specify the full path destination folder where experiment results will be moved")
-
-    parser.add_argument("--pct-traffic", dest="pctTraffic", type=float, default=1.0,
-                        help="Specify the percentage of nodes each node should ping")
-
-    parser.add_argument('--version', '-V', action='version', version='%(prog)s ' + VERSION_NUMBER,
-                        help='Displays version information')
-
-    parser.add_argument("--cluster", metavar='localhost,server2,...',
-                        help="Run cluster edition")
-
-    parser.add_argument("--placement", default='guided',
-                        choices=['roundRobin', 'guided'])
-
-    parser.add_argument("--place-list", dest="placeList",
-                        help="""Provide corresponding number of nodes (comma separated) to put on
-                        each node respectively of --cluster when guided placement is used""")
-
-    parser.add_argument("--tunnel-type", dest="tunnelType", default='ssh',
-                        choices=['ssh', 'gre'])
-
-    parser.add_argument("--face-type", dest='faceType', default='udp', choices=['udp', 'tcp'])
-
-    parser.add_argument("--cs-size", dest='csSize', type=int, default=65536,
-                        help="Set CS size in NFD's conf file")
-
-    ExperimentManager.addExperimentArgs(parser)
-
-    if "argcomplete" in sys.modules:
-        argcomplete.autocomplete(parser)
-
-    args = parser.parse_args()
-
-    options = ProgramOptions()
-    options.templateFile = args.tempfile
-    options.ctime = args.ctime
-    options.experimentName = args.experiment
-    options.nFaces = args.faces
-    options.routingType = args.routingType
-    options.isNlsrEnabled = args.isNlsrEnabled
-    options.isCliEnabled = args.isCliEnabled
-    options.nlsrSecurity = args.nlsrSecurity
-    options.nPings = args.nPings
-
-    options.testbed = args.testbed
-    options.workDir = args.workDir
-    options.resultDir = args.resultDir
-    options.pctTraffic = args.pctTraffic
-    options.cluster = args.cluster
-    options.placement = args.placement
-    options.tunnelType = args.tunnelType
-    options.placeList = args.placeList
-    options.faceType = args.faceType
-    options.csSize = args.csSize
-    options.arguments = args
-
-    if options.experimentName is not None and options.experimentName not in ExperimentManager.getExperimentNames():
-        error("No experiment named {}\n".format(options.experimentName))
-        sys.exit(1)
-
-    if options.experimentName is not None and options.resultDir is None:
-        warn("No results folder specified; experiment results will remain in the working directory\n")
-
-    if options.cluster is not None:
-        servers = options.cluster.split(',')
-        for server in servers:
-            ClusterCleanup.add(server)
-        options.servers = servers
-
-        if options.placement == "roundRobin":
-            options.placement = RoundRobinPlacer
-        elif options.placement == "guided":
-            if options.placeList is None or not re.match("^[0-9,]+$", options.placeList):
-                error("Please specify correctly how many nodes you want to place on each node!")
-                sys.exit(1)
-            else:
-                try:
-                    options.placeList = map(int, options.placeList.split(","))
-                except ValueError:
-                    error("Please specify the nodes correctly, no comma at the beginning/end!")
-                    sys.exit(1)
-
-                PopulatePlacement(options.placeList)
-                options.placement = GuidedPlacer
-
-        if options.tunnelType == "ssh":
-            options.tunnelType = RemoteNdnLink
-        else:
-            options.tunnelType = RemoteGRENdnLink
-
-    return options
-
-class NdnTopo(Topo):
-    def __init__(self, conf_arq, workDir, **opts):
-        Topo.__init__(self, **opts)
-
-        self.hosts_conf = parse_hosts(conf_arq)
-        self.switches_conf = parse_switches(conf_arq)
-        self.links_conf = parse_links(conf_arq)
-
-        self.isTCLink = False
-        self.isLimited = False
-
-        for host in self.hosts_conf:
-            if host.cpu != None and self.isLimited != True:
-                self.isLimited = True
-            self.addHost(host.name, app=host.app, params=host.params, cpu=host.cpu,
-                         cores=host.cores,cache=host.cache, workdir=workDir)
-
-        for switch in self.switches_conf:
-            self.addSwitch(switch.name)
-
-        for link in self.links_conf:
-            if len(link.linkDict) == 0:
-                self.addLink(link.h1, link.h2)
-            else:
-                self.addLink(link.h1, link.h2, **link.linkDict)
-                self.isTCLink = True
-
-        info('Parse of ' + conf_arq + ' done.\n')
-
-def execute(options):
-    "Create a network based on template_file"
-
-    if options.testbed:
-        options.templateFile = INSTALL_DIR + 'minindn.testbed.conf'
-
-    if os.path.exists(options.templateFile) == False:
-        error('Template file cannot be found. Exiting...\n')
-        sys.exit(1)
-
-    if options.cluster is not None and options.placement == GuidedPlacer:
-        num_nodes = 0
-        with open(options.templateFile, 'r') as topo:
-            for line in topo:
-                if ': _' in line:
-                    num_nodes += 1
-
-        if sum(options.placeList) != num_nodes:
-            error("Placement list sum is not equal to number of nodes!")
-            sys.exit(1)
-
-    # Copy nfd.conf to remote hosts - this assumes that NDN versions across
-    # the cluster are at least compatible if not the same
-    if options.cluster is not None:
-        for server in options.servers:
-            if server != "localhost":
-                login = "mininet@{}".format(server)
-                src = nfdConfFile
-                dst = "{}:/tmp/nfd.conf".format(login)
-                scp(src, dst)
-                ssh(login, "sudo cp /tmp/nfd.conf {}".format(src))
-
-    if options.resultDir is not None:
-        options.resultDir = createResultsDir(options.resultDir, options.nFaces, options.routingType)
-
-    topo = NdnTopo(options.templateFile, options.workDir)
-
-    if topo.isTCLink == True and topo.isLimited == True:
-        net = Mininet(topo,host=CpuLimitedNdnHost,link=TCLink)
-    elif topo.isTCLink == True and topo.isLimited == False:
-        if options.cluster is not None:
-            mn = partial(MininetCluster, servers=options.servers, placement=options.placement)
-            net = mn(topo=topo, host=RemoteNdnHost, link=options.tunnelType)
-        else:
-            net = Mininet(topo, host=NdnHost, link=TCLink)
-    elif topo.isTCLink == False and topo.isLimited == True:
-        net = Mininet(topo, host=CpuLimitedNdnHost)
-    else:
-        net = Mininet(topo, host=NdnHost)
-
-    net.start()
-
-    # Giving proper IPs to intf so neighbor nodes can communicate
-    # This is one way of giving connectivity, another way could be
-    # to insert a switch between each pair of neighbors
-    ndnNetBase = "1.0.0.0"
-    interfaces = []
-    for host in net.hosts:
-        for intf in host.intfList():
-            link = intf.link
-            node1, node2 = link.intf1.node, link.intf2.node
-
-            if node1 in net.switches or node2 in net.switches:
-                continue
-
-            if link.intf1 not in interfaces and link.intf2 not in interfaces:
-                interfaces.append(link.intf1)
-                interfaces.append(link.intf2)
-                node1.setIP(ipStr(ipParse(ndnNetBase) + 1) + '/30', intf=link.intf1)
-                node2.setIP(ipStr(ipParse(ndnNetBase) + 2) + '/30', intf=link.intf2)
-                ndnNetBase = ipStr(ipParse(ndnNetBase) + 4)
-
-    time.sleep(2)
-
-    info('Starting NFD on nodes\n')
-    for host in net.hosts:
-        host.nfd = Nfd(host, options.csSize)
-        host.nfd.start()
-
-    for host in net.hosts:
-        if 'app' in host.params:
-            if host.params['app'] != '':
-                app = host.params['app']
-                info("Starting {} on node {}".format(app, host.name))
-                info(host.cmd(app))
-
-    # Determine if each host is running NFD
-    for host in net.hosts:
-        nfdStatus = host.cmd("ps -g -U root | grep 'nfd --config {}/[n]fd.conf'".format(host.homeFolder))
-        if not host.nfd.isRunning or not nfdStatus:
-            error("NFD on host {} is not running. Printing log file and exiting...".format(host.name))
-            info(host.cmd("tail {}/nfd.log".format(host.homeFolder)))
-            net.stop()
-            sys.exit(1)
-
-    # Load experiment
-    experimentName = options.experimentName
-
-    experimentArgs = {
-        "net": net,
-        "options": options
-    }
-
-    if experimentName is not None:
-        info("Loading experiment: {}\n".format(experimentName))
-
-        experiment = ExperimentManager.create(experimentName, experimentArgs)
-
-        if experiment is not None:
-            experiment.start()
-        else:
-            error("Experiment '{}' does not exist\n".format(experimentName))
-            return
-    else:
-        experiment = Experiment(experimentArgs)
-        if options.isNlsrEnabled:
-            experiment.startNlsr(checkConvergence = False)
-
-    if options.isCliEnabled is True:
-        MiniNDNCLI(net)
-
-    net.stop()
-
-    if options.resultDir is not None:
-        info("Moving results to {}".format(options.resultDir))
-        for file in glob.glob('{}/*'.format(options.workDir)):
-            shutil.move(file, options.resultDir)
-        if options.cluster is not None:
-            for server in options.servers:
-                if server != "localhost":
-                    login = "mininet@{}".format(server)
-                    src = "{}:{}/*".format(login, options.workDir)
-                    dst = options.resultDir
-                    scp(src, dst)
-            info("Please clean work directories of other machines before running the cluster again")
-
-def signal_handler(signal, frame):
-    info('Cleaning up...')
-    call(["nfd-stop"])
-    call(["sudo", "mn", "--clean"])
-    sys.exit(1)
-
-def verify_dependencies():
-    "Prevent MiniNDN from running without necessary dependencies"
-    dependencies = ["nfd", "nlsr", "infoedit", "ndnping", "ndnpingserver"]
-    devnull = open("/dev/null", "w")
-    # Checks that each program is in the system path
-    for program in dependencies:
-        if call(["which", program], stdout=devnull):
-            error("{} is missing from the system path! Exiting...".format(program))
-            sys.exit(1)
-    devnull.close()
-
-if __name__ == '__main__':
-
-    signal.signal(signal.SIGQUIT, signal_handler)
-
-    options = parse_args()
-
-    setLogLevel('info')
-    verify_dependencies()
-
-    try:
-        execute(options)
-    except Exception as e:
-        error("{}".format(e))
-        call(["nfd-stop"])
-        call(["sudo", "mn", "--clean"])
-        sys.exit(1)
diff --git a/bin/minindnedit b/bin/minindnedit
deleted file mode 100755
index 0bdcb5d..0000000
--- a/bin/minindnedit
+++ /dev/null
@@ -1,1901 +0,0 @@
-# -*- Mode:python; c-file-style:"gnu"; indent-tabs-mode:nil -*- */
-#
-# Copyright (C) 2015-2018, The University of Memphis,
-#                          Arizona Board of Regents,
-#                          Regents of the University of California.
-#
-# This file is part of Mini-NDN.
-# See AUTHORS.md for a complete list of Mini-NDN authors and contributors.
-#
-# Mini-NDN is free software: you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# Mini-NDN is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with Mini-NDN, e.g., in COPYING.md file.
-# If not, see <http://www.gnu.org/licenses/>.
-#
-# This file incorporates work covered by the following copyright and
-# permission notice:
-#
-#   Mininet 2.2.1 License
-#
-#   Copyright (c) 2013-2015 Open Networking Laboratory
-#   Copyright (c) 2009-2012 Bob Lantz and The Board of Trustees of
-#   The Leland Stanford Junior University
-#
-#   Original authors: Bob Lantz and Brandon Heller
-#
-#   We are making Mininet available for public use and benefit with the
-#   expectation that others will use, modify and enhance the Software and
-#   contribute those enhancements back to the community. However, since we
-#   would like to make the Software available for broadest use, with as few
-#   restrictions as possible permission is hereby granted, free of charge, to
-#   any person obtaining a copy of this Software to deal in the Software
-#   under the copyrights without restriction, including without limitation
-#   the rights to use, copy, modify, merge, publish, distribute, sublicense,
-#   and/or sell copies of the Software, and to permit persons to whom the
-#   Software is furnished to do so, subject to the following conditions:
-#
-#   The above copyright notice and this permission notice shall be included
-#   in all copies or substantial portions of the Software.
-#
-#   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
-#   OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
-#   MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
-#   IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
-#   CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
-#   TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
-#   SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-#
-#   The name and trademarks of copyright holder(s) may NOT be used in
-#   advertising or publicity pertaining to the Software or any derivatives
-#   without specific, written prior permission.
-
-"""
-MiniNDNEdit: a simple network editor for MiniNDN
-
-Based on miniccnxedit by:
-Carlos Cabral, Jan 2013
-Caio Elias, Nov 2014
-
-Based on miniedit by:
-Bob Lantz, April 2010
-Gregory Gee, July 2013
-
-"""
-
-MINIEDIT_VERSION = '2.2.0.1'
-
-from optparse import OptionParser
-from Tkinter import *
-from ttk import Notebook
-from tkMessageBox import showinfo, showerror, showwarning
-from subprocess import call, Popen
-import tkFont
-import csv
-import tkFileDialog
-import tkSimpleDialog
-import re
-import json
-from distutils.version import StrictVersion
-import os
-import sys
-import threading
-from functools import partial
-
-import pdb
-
-if 'PYTHONPATH' in os.environ:
-    sys.path = os.environ[ 'PYTHONPATH' ].split( ':' ) + sys.path
-
-# someday: from ttk import *
-
-from mininet.log import info, error, debug, output, setLogLevel
-from mininet.net import Mininet, VERSION
-from mininet.util import ipStr, netParse, ipAdd, quietRun
-from mininet.util import buildTopo
-from mininet.util import custom
-from mininet.term import makeTerm, cleanUpScreens
-from mininet.node import Controller, RemoteController, NOX, OVSController
-from mininet.node import CPULimitedHost, Host, Node
-from mininet.node import OVSKernelSwitch, OVSSwitch, UserSwitch
-from mininet.link import TCLink, Intf, Link
-from mininet.cli import CLI
-from mininet.moduledeps import moduleDeps, pathCheck
-from mininet.topo import SingleSwitchTopo, LinearTopo, SingleSwitchReversedTopo
-from mininet.topolib import TreeTopo
-
-print 'MiniNDNEdit running...' #+VERSION
-MININET_VERSION = re.sub(r'[^\d\.]', '', VERSION)
-if StrictVersion(MININET_VERSION) > StrictVersion('2.0'):
-    from mininet.node import IVSSwitch
-
-
-from ndn.gui import NfdFrame, NlsrFrame
-
-TOPODEF = 'none'
-TOPOS = { 'minimal': lambda: SingleSwitchTopo( k=2 ),
-          'linear': LinearTopo,
-          'reversed': SingleSwitchReversedTopo,
-          'single': SingleSwitchTopo,
-          'none': None,
-          'tree': TreeTopo }
-LINKDEF = 'default'
-LINKS = { 'default': Link,
-          'tc': TCLink }
-HOSTDEF = 'proc'
-HOSTS = { 'proc': Host,
-          'rt': custom( CPULimitedHost, sched='rt' ),
-          'cfs': custom( CPULimitedHost, sched='cfs' ) }
-
-def runMiniNdn(window, template):
-    # Hide window
-    window.withdraw()
-
-    proc = Popen("sudo minindn %s" % template, shell=True)
-    proc.wait()
-
-    # Restore window
-    window.deiconify()
-
-class LegacyRouter( Node ):
-
-    def __init__( self, name, inNamespace=True, **params ):
-        Node.__init__( self, name, inNamespace, **params )
-
-    def config( self, **_params ):
-        if self.intfs:
-            self.setParam( _params, 'setIP', ip='0.0.0.0' )
-        r = Node.config( self, **_params )
-        self.cmd('sysctl -w net.ipv4.ip_forward=1')
-        return r
-
-class CustomDialog(object):
-
-        # TODO: Fix button placement and Title and window focus lock
-        def __init__(self, master, title):
-            self.top=Toplevel(master)
-
-            self.bodyFrame = Frame(self.top)
-            self.bodyFrame.grid(row=0, column=0, sticky='nswe')
-            self.body(self.bodyFrame)
-
-            #return self.b # initial focus
-            buttonFrame = Frame(self.top, relief='ridge', bd=3, bg='lightgrey')
-            buttonFrame.grid(row=1 , column=0, sticky='nswe')
-
-            okButton = Button(buttonFrame, width=8, text='OK', relief='groove',
-                       bd=4, command=self.okAction)
-            okButton.grid(row=1, column=0, sticky=E)
-
-            canlceButton = Button(buttonFrame, width=8, text='Cancel', relief='groove',
-                        bd=4, command=self.cancelAction)
-            canlceButton.grid(row=1, column=1, sticky=W)
-
-        def body(self, master):
-            self.rootFrame = master
-
-        def apply(self):
-            self.top.destroy()
-
-        def cancelAction(self):
-            self.top.destroy()
-
-        def okAction(self):
-            self.apply()
-            self.top.destroy()
-
-class HostDialog(CustomDialog):
-
-        def __init__(self, master, title, prefDefaults, isRouter):
-
-            self.prefValues = prefDefaults
-            self.result = None
-            self.isRouter = isRouter
-            self.title = title
-
-            CustomDialog.__init__(self, master, title)
-
-        def body(self, master):
-            self.rootFrame = master
-            n = Notebook(self.rootFrame)
-            self.propFrame = Frame(n)
-
-            # NDN
-            self.nfdFrame = NfdFrame(n, self.prefValues)
-            self.nlsrFrame = NlsrFrame(n,self.prefValues)
-
-            n.add(self.propFrame, text='Properties')
-
-            n.add(self.nfdFrame, text=self.nfdFrame.frameLabel)
-            n.add(self.nlsrFrame, text=self.nlsrFrame.frameLabel)
-
-            n.pack()
-
-            ### TAB 1
-            # Field for Hostname
-            Label(self.propFrame, text="Hostname:").grid(row=0, sticky=E)
-            self.hostnameEntry = Entry(self.propFrame)
-            self.hostnameEntry.grid(row=0, column=1)
-            if 'hostname' in self.prefValues:
-                self.hostnameEntry.insert(0, self.prefValues['hostname'])
-
-            # Field for CPU
-            Label(self.propFrame, text="Amount CPU:").grid(row=2, sticky=E)
-            self.cpuEntry = Entry(self.propFrame)
-            self.cpuEntry.grid(row=2, column=1)
-            Label(self.propFrame, text="%").grid(row=2, column=2, sticky=W)
-            if 'cpu' in self.prefValues:
-                self.cpuEntry.insert(0, str(self.prefValues['cpu']))
-
-            # Field for Memory
-            Label(self.propFrame, text="Amount MEM:").grid(row=3, sticky=E)
-            self.memEntry = Entry(self.propFrame)
-            self.memEntry.grid(row=3, column=1)
-            Label(self.propFrame, text="%").grid(row=3, column=2, sticky=W)
-            if 'mem' in self.prefValues:
-                self.memEntry.insert(0, str(self.prefValues['mem']))
-
-            # Field for Cache
-            Label(self.propFrame, text="Amount CACHE:").grid(row=4, sticky=E)
-            self.cacheEntry = Entry(self.propFrame)
-            self.cacheEntry.grid(row=4, column=1)
-            Label(self.propFrame, text="KBytes").grid(row=4, column=2, sticky=W)
-            if 'cache' in self.prefValues:
-                self.cacheEntry.insert(0, str(self.prefValues['cache']))
-
-            # Start command
-            #print self.isRouter
-            if self.isRouter == 'False':
-                    Label(self.propFrame, text="Start Command(s):").grid(row=5, sticky=E)
-                    self.scrollbar = Scrollbar(self.propFrame, orient="horizontal")
-                    self.startEntry = Entry(self.propFrame, xscrollcommand=self.scrollbar.set,)
-                    self.startEntry.grid(row=5, column=1)
-                    self.scrollbar.grid(row=6, column=1, sticky=N+S+E+W)
-                    self.scrollbar.config(command=self.startEntry.xview)
-                    Label(self.propFrame, text="[Use bash syntax]").grid(row=5, column=2, sticky=W)
-                    if 'startCommand' in self.prefValues:
-                        self.startEntry.insert(0, str(self.prefValues['startCommand']))
-            else:
-                self.startEntry= Entry(self.propFrame)
-
-        def apply(self):
-            results = {'cpu': self.cpuEntry.get(),
-                       'cache': self.cacheEntry.get(),
-                       'mem': self.memEntry.get(),
-                       'hostname':self.hostnameEntry.get(),
-                       'startCommand':self.startEntry.get(),
-                       'nfd': self.nfdFrame.getValues(),
-                       'nlsr': self.nlsrFrame.getValues()
-            }
-
-            self.result = results
-
-class VerticalScrolledTable(LabelFrame):
-    """A pure Tkinter scrollable frame that actually works!
-
-    * Use the 'interior' attribute to place widgets inside the scrollable frame
-    * Construct and pack/place/grid normally
-    * This frame only allows vertical scrolling
-
-    """
-    def __init__(self, parent, rows=2, columns=2, title=None, *args, **kw):
-        LabelFrame.__init__(self, parent, text=title, padx=5, pady=5, *args, **kw)
-
-        # create a canvas object and a vertical scrollbar for scrolling it
-        vscrollbar = Scrollbar(self, orient=VERTICAL)
-        vscrollbar.pack(fill=Y, side=RIGHT, expand=FALSE)
-        canvas = Canvas(self, bd=0, highlightthickness=0,
-                        yscrollcommand=vscrollbar.set)
-        canvas.pack(side=LEFT, fill=BOTH, expand=TRUE)
-        vscrollbar.config(command=canvas.yview)
-
-        # reset the view
-        canvas.xview_moveto(0)
-        canvas.yview_moveto(0)
-
-        # create a frame inside the canvas which will be scrolled with it
-        self.interior = interior = TableFrame(canvas, rows=rows, columns=columns)
-        interior_id = canvas.create_window(0, 0, window=interior,
-                                           anchor=NW)
-
-        # track changes to the canvas and frame width and sync them,
-        # also updating the scrollbar
-        def _configure_interior(event):
-            # update the scrollbars to match the size of the inner frame
-            size = (interior.winfo_reqwidth(), interior.winfo_reqheight())
-            canvas.config(scrollregion="0 0 %s %s" % size)
-            if interior.winfo_reqwidth() != canvas.winfo_width():
-                # update the canvas's width to fit the inner frame
-                canvas.config(width=interior.winfo_reqwidth())
-        interior.bind('<Configure>', _configure_interior)
-
-        def _configure_canvas(event):
-            if interior.winfo_reqwidth() != canvas.winfo_width():
-                # update the inner frame's width to fill the canvas
-                canvas.itemconfigure(interior_id, width=canvas.winfo_width())
-        canvas.bind('<Configure>', _configure_canvas)
-
-        return
-
-class TableFrame(Frame):
-    def __init__(self, parent, rows=2, columns=2):
-
-        Frame.__init__(self, parent, background="black")
-        self._widgets = []
-        self.rows = rows
-        self.columns = columns
-        for row in range(rows):
-            current_row = []
-            for column in range(columns):
-                label = Entry(self, borderwidth=0)
-                label.grid(row=row, column=column, sticky="wens", padx=1, pady=1)
-                current_row.append(label)
-            self._widgets.append(current_row)
-
-    def set(self, row, column, value):
-        widget = self._widgets[row][column]
-        widget.insert(0, value)
-
-    def get(self, row, column):
-        widget = self._widgets[row][column]
-        return widget.get()
-
-    def addRow( self, value=None, readonly=False ):
-        #print "Adding row " + str(self.rows +1)
-        current_row = []
-        for column in range(self.columns):
-            label = Entry(self, borderwidth=0)
-            label.grid(row=self.rows, column=column, sticky="wens", padx=1, pady=1)
-            if value is not None:
-                label.insert(0, value[column])
-            if (readonly == True):
-                label.configure(state='readonly')
-            current_row.append(label)
-        self._widgets.append(current_row)
-        self.update_idletasks()
-        self.rows += 1
-
-class LinkDialog(tkSimpleDialog.Dialog):
-
-        def __init__(self, parent, title, linkDefaults):
-
-            self.linkValues = linkDefaults
-
-            tkSimpleDialog.Dialog.__init__(self, parent, title)
-
-        def body(self, master):
-
-            self.var = StringVar(master)
-            Label(master, text="Bandwidth:").grid(row=0, sticky=E)
-            self.e1 = Entry(master)
-            self.e1.grid(row=0, column=1)
-            Label(master, text="[1-1000] Mbps").grid(row=0, column=2, sticky=W)
-            if 'bw' in self.linkValues:
-                self.e1.insert(0,str(self.linkValues['bw']))
-
-            Label(master, text="Delay:").grid(row=1, sticky=E)
-            self.e2 = Entry(master)
-            self.e2.grid(row=1, column=1)
-            Label(master, text="[0-1000] ms").grid(row=1, column=2, sticky=W)
-            if 'delay' in self.linkValues:
-                self.e2.insert(0, self.linkValues['delay'])
-
-            Label(master, text="Loss:").grid(row=2, sticky=E)
-            self.e3 = Entry(master)
-            self.e3.grid(row=2, column=1)
-            Label(master, text="%").grid(row=2, column=2, sticky=W)
-            if 'loss' in self.linkValues:
-                self.e3.insert(0, str(self.linkValues['loss']))
-
-            return self.e1 # initial focus
-
-        def apply(self):
-            self.result = {}
-            if (len(self.e1.get()) > 0):
-                self.result['bw'] = int(self.e1.get())
-            if (len(self.e2.get()) > 0):
-                self.result['delay'] = self.e2.get()
-            if (len(self.e3.get()) > 0):
-                self.result['loss'] = int(self.e3.get())
-
-class ToolTip(object):
-
-    def __init__(self, widget):
-        self.widget = widget
-        self.tipwindow = None
-        self.id = None
-        self.x = self.y = 0
-
-    def showtip(self, text):
-        "Display text in tooltip window"
-        self.text = text
-        if self.tipwindow or not self.text:
-            return
-        x, y, cx, cy = self.widget.bbox("insert")
-        x = x + self.widget.winfo_rootx() + 27
-        y = y + cy + self.widget.winfo_rooty() +27
-        self.tipwindow = tw = Toplevel(self.widget)
-        tw.wm_overrideredirect(1)
-        tw.wm_geometry("+%d+%d" % (x, y))
-        try:
-            # For Mac OS
-            tw.tk.call("::tk::unsupported::MacWindowStyle",
-                       "style", tw._w,
-                       "help", "noActivates")
-        except TclError:
-            pass
-        label = Label(tw, text=self.text, justify=LEFT,
-                      background="#ffffe0", relief=SOLID, borderwidth=1,
-                      font=("tahoma", "8", "normal"))
-        label.pack(ipadx=1)
-
-    def hidetip(self):
-        tw = self.tipwindow
-        self.tipwindow = None
-        if tw:
-            tw.destroy()
-
-class MiniEdit( Frame ):
-
-    "A simple network editor for MiniNDN."
-
-    def __init__( self, parent=None, cheight=600, cwidth=1000, template_file='minindn.conf' ):
-
-        self.template_file = template_file
-
-        Frame.__init__( self, parent )
-        self.action = None
-        self.appName = 'MiniNDNEdit'
-        self.fixedFont = tkFont.Font ( family="DejaVu Sans Mono", size="14" )
-
-        # Style
-        self.font = ( 'Geneva', 9 )
-        self.smallFont = ( 'Geneva', 7 )
-        self.bg = 'white'
-
-        # Title
-        self.top = self.winfo_toplevel()
-        self.top.title( self.appName )
-
-        # Menu bar
-        self.createMenubar()
-
-        # Editing canvas
-        self.cheight, self.cwidth = cheight, cwidth
-        self.cframe, self.canvas = self.createCanvas()
-
-        # Toolbar
-        self.controllers = {}
-
-        # Toolbar
-        self.images = miniEditImages()
-        self.buttons = {}
-        self.active = None
-        self.tools = ( 'Select', 'Host', 'Switch', 'NetLink' )
-        #self.customColors = { 'LegacyRouter': 'darkGreen', 'Host': 'blue' }
-        self.toolbar = self.createToolbar()
-
-        # Layout
-        self.toolbar.grid( column=0, row=0, sticky='nsew')
-        self.cframe.grid( column=1, row=0 )
-        self.columnconfigure( 1, weight=1 )
-        self.rowconfigure( 0, weight=1 )
-        self.pack( expand=True, fill='both' )
-
-        # About box
-        self.aboutBox = None
-
-        # Initialize node data
-        self.nodeBindings = self.createNodeBindings()
-        self.nodePrefixes = { 'LegacyRouter': 'r', 'Host': 'h', 'Switch': 's'}
-        self.widgetToItem = {}
-        self.itemToWidget = {}
-
-        # Initialize link tool
-        self.link = self.linkWidget = None
-
-        # Selection support
-        self.selection = None
-
-        # Keyboard bindings
-        self.bind( '<Control-q>', lambda event: self.quit() )
-        self.bind( '<KeyPress-Delete>', self.deleteSelection )
-        self.bind( '<KeyPress-BackSpace>', self.deleteSelection )
-        self.focus()
-
-        #Mouse bindings
-        self.bind( '<Button-1>', lambda event: self.clearPopups )
-
-        self.hostPopup = Menu(self.top, tearoff=0)
-        self.hostPopup.add_command(label='Host Options', font=self.font, command=self.hostDetails)
-        #self.hostPopup.add_separator()
-        #self.hostPopup.add_command(label='Properties', font=self.font, command=self.hostDetails )
-
-        self.legacyRouterPopup = Menu(self.top, tearoff=0)
-        self.legacyRouterPopup.add_command(label='Router Options', font=self.font, command=self.hostDetails)
-
-        self.linkPopup = Menu(self.top, tearoff=0)
-        self.linkPopup.add_command(label='Link Options', font=self.font, command=self.linkDetails)
-        #self.linkPopup.add_separator()
-        #self.linkPopup.add_command(label='Properties', font=self.font, command=self.linkDetails )
-
-        # Event handling initalization
-        self.linkx = self.linky = self.linkItem = None
-        self.lastSelection = None
-
-        # Model initialization
-        self.links = {}
-        self.hostOpts = {}
-        self.switchOpts = {}
-        self.routerOpts = {}
-        self.hostCount = 0
-        self.switchCount = 0
-        self.routerCount = 0
-        self.net = None
-
-        # Close window gracefully
-        Wm.wm_protocol( self.top, name='WM_DELETE_WINDOW', func=self.quit )
-
-    def quit( self ):
-        "Stop our network, if any, then quit."
-        #sself.stop()
-        Frame.quit( self )
-
-    def createMenubar( self ): # MODIFICADO - OK
-        "Create our menu bar."
-
-        font = self.font
-
-        mbar = Menu( self.top, font=font )
-        self.top.configure( menu=mbar )
-
-        fileMenu = Menu( mbar, tearoff=False )
-        mbar.add_cascade( label="File", font=font, menu=fileMenu )
-        fileMenu.add_command( label="New", font=font, command=self.newTopology )
-        fileMenu.add_command( label="Open", font=font, command=self.loadTopology )
-        fileMenu.add_command( label="Save", font=font, command=self.saveTopology )
-        fileMenu.add_command( label="Generate", font=font, command=self.doGenerate )
-        fileMenu.add_command( label="Run", font=font, command=self.doRun )
-        fileMenu.add_separator()
-        fileMenu.add_command( label='Quit', command=self.quit, font=font )
-
-        editMenu = Menu( mbar, tearoff=False )
-        mbar.add_cascade( label="Edit", font=font, menu=editMenu )
-        editMenu.add_command( label="Cut", font=font,
-                              command=lambda: self.deleteSelection( None ) )
-
-        # Application menu
-        appMenu = Menu( mbar, tearoff=False )
-        mbar.add_cascade( label=self.appName, font=font, menu=appMenu )
-        appMenu.add_command( label='About Mini-NDN', command=self.about,
-                             font=font)
-        #appMenu.add_separator()
-        #appMenu.add_command( label='Quit', command=self.quit, font=font )
-
-    # Canvas - TUDO IGUAL - OK
-
-    def createCanvas( self ):
-        "Create and return our scrolling canvas frame."
-        f = Frame( self )
-
-        canvas = Canvas( f, width=self.cwidth, height=self.cheight,
-                         bg=self.bg )
-
-        # Scroll bars
-        xbar = Scrollbar( f, orient='horizontal', command=canvas.xview )
-        ybar = Scrollbar( f, orient='vertical', command=canvas.yview )
-        canvas.configure( xscrollcommand=xbar.set, yscrollcommand=ybar.set )
-
-        # Resize box
-        resize = Label( f, bg='white' )
-
-        # Layout
-        canvas.grid( row=0, column=1, sticky='nsew')
-        ybar.grid( row=0, column=2, sticky='ns')
-        xbar.grid( row=1, column=1, sticky='ew' )
-        resize.grid( row=1, column=2, sticky='nsew' )
-
-        # Resize behavior
-        f.rowconfigure( 0, weight=1 )
-        f.columnconfigure( 1, weight=1 )
-        f.grid( row=0, column=0, sticky='nsew' )
-        f.bind( '<Configure>', lambda event: self.updateScrollRegion() )
-
-        # Mouse bindings
-        canvas.bind( '<ButtonPress-1>', self.clickCanvas )
-        canvas.bind( '<B1-Motion>', self.dragCanvas )
-        canvas.bind( '<ButtonRelease-1>', self.releaseCanvas )
-
-        return f, canvas
-
-    def updateScrollRegion( self ):
-        "Update canvas scroll region to hold everything."
-        bbox = self.canvas.bbox( 'all' )
-        if bbox is not None:
-            self.canvas.configure( scrollregion=( 0, 0, bbox[ 2 ],
-                                   bbox[ 3 ] ) )
-
-    def canvasx( self, x_root ):
-        "Convert root x coordinate to canvas coordinate."
-        c = self.canvas
-        return c.canvasx( x_root ) - c.winfo_rootx()
-
-    def canvasy( self, y_root ):
-        "Convert root y coordinate to canvas coordinate."
-        c = self.canvas
-        return c.canvasy( y_root ) - c.winfo_rooty()
-
-    # Toolbar
-
-    def activate( self, toolName ): #IGUAL - OK
-        "Activate a tool and press its button."
-        # Adjust button appearance
-        if self.active:
-            self.buttons[ self.active ].configure( relief='raised' )
-        self.buttons[ toolName ].configure( relief='sunken' )
-        # Activate dynamic bindings
-        self.active = toolName
-
-
-    def createToolTip(self, widget, text): #NOVA - CRIA HINTS E TIPS
-        toolTip = ToolTip(widget)
-        def enter(event):
-            toolTip.showtip(text)
-        def leave(event):
-            toolTip.hidetip()
-        widget.bind('<Enter>', enter)
-        widget.bind('<Leave>', leave)
-
-    def createToolbar( self ): #MODIFICADO - OK
-        "Create and return our toolbar frame."
-
-        toolbar = Frame( self )
-
-        # Tools
-        for tool in self.tools:
-            cmd = ( lambda t=tool: self.activate( t ) )
-            b = Button( toolbar, text=tool, font=self.smallFont, command=cmd)
-            if tool in self.images:
-                b.config( height=35, image=self.images[ tool ] )
-                self.createToolTip(b, str(tool))
-                # b.config( compound='top' )
-            b.pack( fill='x' )
-            self.buttons[ tool ] = b
-        self.activate( self.tools[ 0 ] )
-
-        # Spacer
-        Label( toolbar, text='' ).pack()
-
-        # abaixo copiado Mini-NDN para criar botao Generate
-
-        for cmd, color in [ ( 'Generate', 'darkGreen' ) ]:
-            doCmd = getattr( self, 'do' + cmd )
-            b = Button( toolbar, text=cmd, font=self.smallFont,
-                        fg=color, command=doCmd )
-            b.pack( fill='x', side='bottom' )
-
-        return toolbar
-
-    def doGenerate( self ): #COPIA Mini-NDN - GERA TEMPLATE
-        "Generate template."
-        self.activate( 'Select' )
-        for tool in self.tools:
-            self.buttons[ tool ].config( state='disabled' )
-
-        self.buildTemplate()
-
-        for tool in self.tools:
-            self.buttons[ tool ].config( state='normal' )
-
-        toplevel = Toplevel()
-        label1 = Label(toplevel, text="Template file generated successfully", height=0, width=30)
-        label1.pack()
-        b=Button(toplevel, text="Ok", width=5, command=toplevel.destroy)
-        b.pack(side='bottom', padx=0,pady=0)
-
-    def doRun( self ):
-        "Use current configuration to generate a template and run the topology"
-
-        # Generate temporary template file
-        old_template_file = self.template_file
-        self.template_file = "/tmp/tmp.conf"
-        self.doGenerate()
-
-        thread = threading.Thread(target=runMiniNdn, args=(self.master, self.template_file))
-        thread.start()
-
-        self.template_file = old_template_file
-
-    def buildTemplate( self ): #COPIA Mini-NDN para criar Template
-        "Generate template"
-
-        template = open(self.template_file, 'w')
-
-        # hosts
-        template.write('[nodes]\n')
-        for widget in self.widgetToItem:
-            name = widget[ 'text' ]
-            tags = self.canvas.gettags( self.widgetToItem[ widget ] )
-
-            if 'Host' in tags:
-                hOpts=self.hostOpts[name]
-                print hOpts
-
-                template.write(name + ': ')
-                if 'startCommand' in hOpts:
-                        cmds = hOpts['startCommand'].replace("\"", "\\\"")
-                        template.write('apps="%s" ' % cmds)
-                else:
-                        template.write('_ ')
-                if 'cache' in hOpts:
-                        template.write('cache=' + hOpts['cache'] + ' ')
-                if 'cpu' in hOpts:
-                        cpu=float(hOpts['cpu'])/100
-                        template.write('cpu=' + repr(cpu) + ' ')
-                if 'mem' in hOpts:
-                        mem=float(hOpts['mem'])/100
-                        template.write('mem=' + repr(mem) + ' ')
-                if 'nlsr' in hOpts:
-                        values = hOpts['nlsr']
-
-                        template.write('hyperbolic-state=' + values['hyperbolic-state'] + ' ')
-                        template.write('radius=' + values['radius'] + ' ')
-                        template.write('angle=' + values['angle'] + ' ')
-                        template.write('network=' + values['network'] + ' ')
-                        template.write('router=' + values['router'] + ' ')
-                        template.write('site=' + values['site'] + ' ')
-                        template.write('nlsr-log-level=' + values['log-level'] + ' ')
-                        template.write('max-faces-per-prefix=' + values['max-faces-per-prefix'] + ' ')
-                if 'nfd' in hOpts:
-                        values = hOpts['nfd']
-
-                        template.write('nfd-log-level=' + values['log-level'] + ' ')
-
-                template.write('\n')
-
-        # switches/routers
-        #template.write('[routers]\n')
-
-        for router in self.routerOpts.values():
-
-            hasOpt='False'
-            routerName=router['hostname']
-            #nodetype=router['nodetype']
-            #nodenum=router['nodenum']
-
-            rOpts=self.routerOpts[routerName]
-
-            template.write(routerName + ': ')
-
-            if 'cpu' in rOpts:
-                cpu=float(rOpts['cpu'])/100
-                template.write('cpu=' + repr(cpu) + ' ')
-                hasOpt='True'
-            if 'mem' in rOpts:
-                mem=float(rOpts['mem'])/100
-                template.write('mem=' + repr(mem) + ' ')
-                hasOpt='True'
-            if 'cache' in rOpts:
-                template.write('cache=' + rOpts['cache'] + ' ')
-                hasOpt='True'
-            if hasOpt == 'False':
-                template.write('_')
-
-            template.write('\n')
-
-        # Write switches
-        template.write('[switches]\n')
-
-        for switch in self.switchOpts.values():
-            print "Switch%s" % switch
-
-            name = switch['hostname']
-            template.write(name + ': _\n')
-
-        # Make links
-        template.write('[links]\n')
-        for link in self.links.values():
-             dst=link['dest']
-             src=link['src']
-             linkopts=link['linkOpts']
-             linktype=link['type']
-
-             srcName, dstName = src[ 'text' ], dst[ 'text' ]
-             template.write(srcName + ':' + dstName + ' ')
-             if 'bw' in linkopts:
-                     template.write('bw=' + str(linkopts['bw']) + ' ' )
-             if 'loss' in linkopts:
-                     template.write('loss=' + repr(linkopts['loss']) + ' ' )
-             if 'delay' in linkopts:
-                     template.write('delay=' + str(linkopts['delay'])+ 'ms' )
-
-             template.write('\n')
-
-        template.close()
-
-    def addNode( self, node, nodeNum, x, y, name=None):
-        "Add a new node to our canvas."
-
-        if 'LegacyRouter' == node:
-            self.routerCount += 1
-        if 'Host' == node:
-            self.hostCount += 1
-        if 'Switch' == node:
-            self.switchCount += 1
-        if name is None:
-            name = self.nodePrefixes[ node ] + nodeNum
-        self.addNamedNode(node, name, x, y)
-
-    def addNamedNode( self, node, name, x, y):
-        "Add a new node to our canvas."
-        c = self.canvas
-        icon = self.nodeIcon( node, name )
-        item = self.canvas.create_window( x, y, anchor='c', window=icon,
-                                          tags=node )
-        self.widgetToItem[ icon ] = item
-        self.itemToWidget[ item ] = icon
-        icon.links = {}
-
-    def convertJsonUnicode(self, input):
-        "Some part of Mininet don't like Unicode"
-        if isinstance(input, dict):
-            return {self.convertJsonUnicode(key): self.convertJsonUnicode(value) for key, value in input.iteritems()}
-        elif isinstance(input, list):
-            return [self.convertJsonUnicode(element) for element in input]
-        elif isinstance(input, unicode):
-            return input.encode('utf-8')
-        else:
-            return input
-
-    def loadTopology( self ):
-        "Load command."
-        c = self.canvas
-
-        myFormats = [
-            ('MiniNDN Topology','*.mnndn'),
-            ('All Files','*'),
-        ]
-        f = tkFileDialog.askopenfile(filetypes=myFormats, mode='rb')
-        if f == None:
-            return
-        self.newTopology()
-        loadedTopology = self.convertJsonUnicode(json.load(f))
-
-        # Load hosts
-        hosts = loadedTopology['hosts']
-        for host in hosts:
-            nodeNum = host['number']
-            hostname = 'h'+nodeNum
-            if 'hostname' in host['opts']:
-                hostname = host['opts']['hostname']
-            else:
-                host['opts']['hostname'] = hostname
-            if 'nodeNum' not in host['opts']:
-                host['opts']['nodeNum'] = int(nodeNum)
-
-            x = host['x']
-            y = host['y']
-
-            self.addNode('Host', nodeNum, float(x), float(y), name=hostname)
-
-            # Fix JSON converting tuple to list when saving
-            if 'privateDirectory' in host['opts']:
-                newDirList = []
-                for privateDir in host['opts']['privateDirectory']:
-                    if isinstance( privateDir, list ):
-                        newDirList.append((privateDir[0],privateDir[1]))
-                    else:
-                        newDirList.append(privateDir)
-                host['opts']['privateDirectory'] = newDirList
-            self.hostOpts[hostname] = host['opts']
-            icon = self.findWidgetByName(hostname)
-            icon.bind('<Button-3>', self.do_hostPopup )
-
-        # Load routers
-        routers = loadedTopology['routers']
-        for router in routers:
-            nodeNum = router['number']
-            hostname = 'r'+nodeNum
-            #print router
-            if 'nodeType' not in router['opts']:
-                router['opts']['nodeType'] = 'legacyRouter'
-            if 'hostname' in router['opts']:
-                hostname = router['opts']['hostname']
-            else:
-                router['opts']['hostname'] = hostname
-            if 'nodeNum' not in router['opts']:
-                router['opts']['nodeNum'] = int(nodeNum)
-            x = router['x']
-            y = router['y']
-            if router['opts']['nodeType'] == "legacyRouter":
-                self.addNode('LegacyRouter', nodeNum, float(x), float(y), name=hostname)
-                icon = self.findWidgetByName(hostname)
-                icon.bind('<Button-3>', self.do_legacyRouterPopup )
-            self.routerOpts[hostname] = router['opts']
-
-        # Load switches
-        switches = loadedTopology['switches']
-
-        for switch in switches:
-            nodeNum = switch['number']
-            hostname = 's' + nodeNum
-
-            if 'hostname' in switch['opts']:
-                hostname = switch['opts']['hostname']
-            else:
-                switch['opts']['hostname'] = hostname
-
-            if 'nodeNum' not in switch['opts']:
-                switch['opts']['nodeNum'] = int(nodeNum)
-
-            x = switch['x']
-            y = switch['y']
-
-            self.addNode('Switch', nodeNum, float(x), float(y), name=hostname)
-            icon = self.findWidgetByName(hostname)
-
-            self.switchOpts[hostname] = switch['opts']
-
-        # Load links
-        links = loadedTopology['links']
-        for link in links:
-            srcNode = link['src']
-            src = self.findWidgetByName(srcNode)
-            sx, sy = self.canvas.coords( self.widgetToItem[ src ] )
-
-            destNode = link['dest']
-            dest = self.findWidgetByName(destNode)
-            dx, dy = self.canvas.coords( self.widgetToItem[ dest ]  )
-
-            self.link = self.canvas.create_line( sx, sy, dx, dy, width=4,
-                                             fill='blue', tag='link' )
-            c.itemconfig(self.link, tags=c.gettags(self.link)+('data',))
-            self.addLink( src, dest, linkopts=link['opts'] )
-            self.createDataLinkBindings()
-            self.link = self.linkWidget = None
-
-        f.close
-
-    def findWidgetByName( self, name ):
-        for widget in self.widgetToItem:
-            if name ==  widget[ 'text' ]:
-                return widget
-
-    def newTopology( self ):
-        "New command."
-        for widget in self.widgetToItem.keys():
-            self.deleteItem( self.widgetToItem[ widget ] )
-        self.hostCount = 0
-        self.routerCount = 0
-        self.switchCount = 0
-        self.links = {}
-        self.hostOpts = {}
-        self.routerOpts = {}
-
-    def saveTopology( self ):
-        "Save command."
-        myFormats = [
-            ('MiniNDN Topology','*.mnndn'),
-            ('All Files','*'),
-        ]
-
-        savingDictionary = {}
-        fileName = tkFileDialog.asksaveasfilename(filetypes=myFormats ,title="Save the topology as...")
-        if len(fileName ) > 0:
-            # Save Application preferences
-            savingDictionary['version'] = '2'
-
-            # Save routers and Hosts
-            hostsToSave = []
-            routersToSave = []
-            switchesToSave = []
-
-            for widget in self.widgetToItem:
-                name = widget[ 'text' ]
-                tags = self.canvas.gettags( self.widgetToItem[ widget ] )
-                x1, y1 = self.canvas.coords( self.widgetToItem[ widget ] )
-                if 'LegacyRouter' in tags:
-                    nodeNum = self.routerOpts[name]['nodeNum']
-                    nodeToSave = {'number':str(nodeNum),
-                                  'x':str(x1),
-                                  'y':str(y1),
-                                  'opts':self.routerOpts[name] }
-                    routersToSave.append(nodeToSave)
-                elif 'Host' in tags:
-                    nodeNum = self.hostOpts[name]['nodeNum']
-                    nodeToSave = {'number':str(nodeNum),
-                                  'x':str(x1),
-                                  'y':str(y1),
-                                  'opts':self.hostOpts[name] }
-                    hostsToSave.append(nodeToSave)
-                elif 'Switch' in tags:
-                    nodeNum = self.switchOpts[name]['nodeNum']
-                    nodeToSave = {'number':str(nodeNum),
-                                  'x':str(x1),
-                                  'y':str(y1),
-                                  'opts':self.switchOpts[name] }
-                    switchesToSave.append(nodeToSave)
-                else:
-                    raise Exception( "Cannot create mystery node: " + name )
-            savingDictionary['hosts'] = hostsToSave
-            savingDictionary['routers'] = routersToSave
-            savingDictionary['switches'] = switchesToSave
-
-            # Save Links
-            linksToSave = []
-            for link in self.links.values():
-                src = link['src']
-                dst = link['dest']
-                linkopts = link['linkOpts']
-
-                srcName, dstName = src[ 'text' ], dst[ 'text' ]
-                linkToSave = {'src':srcName,
-                              'dest':dstName,
-                              'opts':linkopts}
-                if link['type'] == 'data':
-                    linksToSave.append(linkToSave)
-            savingDictionary['links'] = linksToSave
-
-            # Save Application preferences
-            #savingDictionary['application'] = self.appPrefs
-
-            try:
-                f = open(fileName, 'wb')
-                f.write(json.dumps(savingDictionary, sort_keys=True, indent=4, separators=(',', ': ')))
-            except Exception as er:
-                print er
-            finally:
-                f.close()
-
-    # Generic canvas handler
-    #
-    # We could have used bindtags, as in nodeIcon, but
-    # the dynamic approach used here
-    # may actually require less code. In any case, it's an
-    # interesting introspection-based alternative to bindtags.
-
-    def canvasHandle( self, eventName, event ):
-        "Generic canvas event handler"
-        if self.active is None:
-            return
-        toolName = self.active
-        handler = getattr( self, eventName + toolName, None )
-        if handler is not None:
-            handler( event )
-
-    def clickCanvas( self, event ):
-        "Canvas click handler."
-        self.canvasHandle( 'click', event )
-
-    def dragCanvas( self, event ):
-        "Canvas drag handler."
-        self.canvasHandle( 'drag', event )
-
-    def releaseCanvas( self, event ):
-        "Canvas mouse up handler."
-        self.canvasHandle( 'release', event )
-
-    # Currently the only items we can select directly are
-    # links. Nodes are handled by bindings in the node icon.
-
-    def findItem( self, x, y ):
-        "Find items at a location in our canvas."
-        items = self.canvas.find_overlapping( x, y, x, y )
-        if len( items ) == 0:
-            return None
-        else:
-            return items[ 0 ]
-
-    # Canvas bindings for Select, Host, Router and Link tools
-
-    def clickSelect( self, event ):
-        "Select an item."
-        self.selectItem( self.findItem( event.x, event.y ) )
-
-    def deleteItem( self, item ):
-        "Delete an item."
-        # Don't delete while network is running
-        if self.buttons[ 'Select' ][ 'state' ] == 'disabled':
-            return
-        # Delete from model
-        if item in self.links:
-            self.deleteLink( item )
-        if item in self.itemToWidget:
-            self.deleteNode( item )
-        # Delete from view
-        self.canvas.delete( item )
-
-    def deleteSelection( self, _event ):
-        "Delete the selected item."
-        if self.selection is not None:
-            self.deleteItem( self.selection )
-        self.selectItem( None )
-
-    def clearPopups(self):
-        print 'Entrou funcao clear_popups'
-        if isHostPopup == True:
-            print 'Hostpopup = true'
-            self.hostPopup.unpost
-            isHostPopup = False
-        #if isRouterPopup == True
-        #if isLinkPopup == True
-
-    def nodeIcon( self, node, name ):
-        "Create a new node icon."
-        icon = Button( self.canvas, image=self.images[ node ],
-                       text=name, compound='top' )
-        # Unfortunately bindtags wants a tuple
-        bindtags = [ str( self.nodeBindings ) ]
-        bindtags += list( icon.bindtags() )
-        icon.bindtags( tuple( bindtags ) )
-        return icon
-
-    def newNode( self, node, event ):
-        "Add a new node to our canvas."
-        c = self.canvas
-        x, y = c.canvasx( event.x ), c.canvasy( event.y )
-        name = self.nodePrefixes[ node ]
-
-        if 'LegacyRouter' == node:
-            self.routerCount += 1
-            name = self.nodePrefixes[ node ] + str( self.routerCount )
-            self.routerOpts[name] = {}
-            self.routerOpts[name]['nodeNum']=self.routerCount
-            self.routerOpts[name]['hostname']=name
-            self.routerOpts[name]['nodeType']='legacyRouter'
-
-        if 'Host' == node:
-            self.hostCount += 1
-            name = self.nodePrefixes[ node ] + str( self.hostCount )
-            self.hostOpts[name] = {'sched':'host'}
-            self.hostOpts[name]['nodeNum']=self.hostCount
-            self.hostOpts[name]['hostname']=name
-
-        if 'Switch' == node:
-            self.switchCount += 1
-            name = self.nodePrefixes[ node ] + str( self.switchCount )
-            self.switchOpts[name] = {}
-            self.switchOpts[name]['nodeNum']=self.switchCount
-            self.switchOpts[name]['hostname']=name
-
-        icon = self.nodeIcon( node, name )
-        item = self.canvas.create_window( x, y, anchor='c', window=icon,
-                                          tags=node )
-        self.widgetToItem[ icon ] = item
-        self.itemToWidget[ item ] = icon
-        self.selectItem( item )
-        icon.links = {}
-        if 'LegacyRouter' == node:
-            icon.bind('<Button-3>', self.do_legacyRouterPopup )
-        if 'Host' == node:
-            icon.bind('<Button-3>', self.do_hostPopup )
-
-    def clickHost( self, event ):
-        "Add a new host to our canvas."
-        self.newNode( 'Host', event )
-
-    def clickSwitch( self, event ):
-        "Add a new switch to the canvas."
-        self.newNode( 'Switch', event )
-
-    def clickLegacyRouter( self, event ):
-        "Add a new router to our canvas."
-        self.newNode( 'LegacyRouter', event )
-
-    def dragNetLink( self, event ):
-        "Drag a link's endpoint to another node."
-        if self.link is None:
-            return
-        # Since drag starts in widget, we use root coords
-        x = self.canvasx( event.x_root )
-        y = self.canvasy( event.y_root )
-        c = self.canvas
-        c.coords( self.link, self.linkx, self.linky, x, y )
-
-    def releaseNetLink( self, _event ):
-        "Give up on the current link."
-        if self.link is not None:
-            self.canvas.delete( self.link )
-        self.linkWidget = self.linkItem = self.link = None
-
-    # Generic node handlers
-
-    def createNodeBindings( self ):
-        "Create a set of bindings for nodes."
-        bindings = {
-            '<ButtonPress-1>': self.clickNode,
-            '<B1-Motion>': self.dragNode,
-            '<ButtonRelease-1>': self.releaseNode,
-            '<Enter>': self.enterNode,
-            '<Leave>': self.leaveNode
-        }
-        l = Label()  # lightweight-ish owner for bindings
-        for event, binding in bindings.items():
-            l.bind( event, binding )
-        return l
-
-    def selectItem( self, item ):
-        "Select an item and remember old selection."
-        self.lastSelection = self.selection
-        self.selection = item
-
-    def enterNode( self, event ):
-        "Select node on entry."
-        self.selectNode( event )
-
-    def leaveNode( self, _event ):
-        "Restore old selection on exit."
-        self.selectItem( self.lastSelection )
-
-    def clickNode( self, event ):
-        "Node click handler."
-        if self.active is 'NetLink':
-            self.startLink( event )
-        else:
-            self.selectNode( event )
-        return 'break'
-
-    def dragNode( self, event ):
-        "Node drag handler."
-        if self.active is 'NetLink':
-            self.dragNetLink( event )
-        else:
-            self.dragNodeAround( event )
-
-    def releaseNode( self, event ):
-        "Node release handler."
-        if self.active is 'NetLink':
-            self.finishLink( event )
-
-    # Specific node handlers
-
-    def selectNode( self, event ):
-        "Select the node that was clicked on."
-        item = self.widgetToItem.get( event.widget, None )
-        self.selectItem( item )
-
-    def dragNodeAround( self, event ):
-        "Drag a node around on the canvas."
-        c = self.canvas
-        # Convert global to local coordinates;
-        # Necessary since x, y are widget-relative
-        x = self.canvasx( event.x_root )
-        y = self.canvasy( event.y_root )
-        w = event.widget
-        # Adjust node position
-        item = self.widgetToItem[ w ]
-        c.coords( item, x, y )
-        # Adjust link positions
-        for dest in w.links:
-            link = w.links[ dest ]
-            item = self.widgetToItem[ dest ]
-            x1, y1 = c.coords( item )
-            c.coords( link, x, y, x1, y1 )
-        self.updateScrollRegion()
-
-    def createDataLinkBindings( self ):
-        "Create a set of bindings for nodes."
-        # Link bindings
-        # Selection still needs a bit of work overall
-        # Callbacks ignore event
-
-        def select( _event, link=self.link ):
-            "Select item on mouse entry."
-            self.selectItem( link )
-
-        def highlight( _event, link=self.link ):
-            "Highlight item on mouse entry."
-            self.selectItem( link )
-            self.canvas.itemconfig( link, fill='green' )
-
-        def unhighlight( _event, link=self.link ):
-            "Unhighlight item on mouse exit."
-            self.canvas.itemconfig( link, fill='blue' )
-            #self.selectItem( None )
-
-        self.canvas.tag_bind( self.link, '<Enter>', highlight )
-        self.canvas.tag_bind( self.link, '<Leave>', unhighlight )
-        self.canvas.tag_bind( self.link, '<ButtonPress-1>', select )
-        self.canvas.tag_bind( self.link, '<Button-3>', self.do_linkPopup )
-
-    def startLink( self, event ):
-        "Start a new link."
-        if event.widget not in self.widgetToItem:
-            # Didn't click on a node
-            return
-
-        w = event.widget
-        item = self.widgetToItem[ w ]
-        x, y = self.canvas.coords( item )
-        self.link = self.canvas.create_line( x, y, x, y, width=4,
-                                             fill='blue', tag='link' )
-        self.linkx, self.linky = x, y
-        self.linkWidget = w
-        self.linkItem = item
-
-    def finishLink( self, event ):
-        "Finish creating a link"
-        if self.link is None:
-            return
-        source = self.linkWidget
-        c = self.canvas
-        # Since we dragged from the widget, use root coords
-        x, y = self.canvasx( event.x_root ), self.canvasy( event.y_root )
-        target = self.findItem( x, y )
-        dest = self.itemToWidget.get( target, None )
-        if ( source is None or dest is None or source == dest
-                or dest in source.links or source in dest.links ):
-            self.releaseNetLink( event )
-            return
-        # For now, don't allow hosts to be directly linked
-        stags = self.canvas.gettags( self.widgetToItem[ source ] )
-        dtags = self.canvas.gettags( target )
-        #if (('Host' in stags and 'Host' in dtags)):
-            #self.releaseNetLink( event )
-            #return
-
-        # Set link type
-        linkType='data'
-
-        self.createDataLinkBindings()
-        c.itemconfig(self.link, tags=c.gettags(self.link)+(linkType,))
-
-        x, y = c.coords( target )
-        c.coords( self.link, self.linkx, self.linky, x, y )
-        self.addLink( source, dest, linktype=linkType )
-
-        # We're done
-        self.link = self.linkWidget = None
-
-    # Menu handlers
-
-    def about( self ):
-        "Display about box."
-        about = self.aboutBox
-        if about is None:
-            bg = 'white'
-            about = Toplevel( bg='white' )
-            about.title( 'About' )
-            info = self.appName + ': a simple network editor for MiniNDN - based on Miniedit'
-            warning = 'Development version - not entirely functional!'
-            #version = 'MiniEdit '+MINIEDIT_VERSION
-            author = 'Vince Lehman, Jan 2015'
-            author2 = 'Ashlesh Gawande, Jan 2015'
-            author3 = 'Carlos Cabral, Jan 2013'
-            author4 = 'Caio Elias, Nov 2014'
-            author5 = 'Originally by: Bob Lantz <rlantz@cs>, April 2010'
-            enhancements = 'Enhancements by: Gregory Gee, Since July 2013'
-            www = 'http://gregorygee.wordpress.com/category/miniedit/'
-            line1 = Label( about, text=info, font='Helvetica 10 bold', bg=bg )
-            line2 = Label( about, text=warning, font='Helvetica 9', bg=bg )
-            line3 = Label( about, text=author, font='Helvetica 9', bg=bg )
-            line4 = Label( about, text=author2, font='Helvetica 9', bg=bg )
-            line5 = Label( about, text=author3, font='Helvetica 9', bg=bg )
-            line6 = Label( about, text=author4, font='Helvetica 9', bg=bg )
-            line7 = Label( about, text=author5, font='Helvetica 9', bg=bg )
-            line8 = Label( about, text=enhancements, font='Helvetica 9', bg=bg )
-            line9 = Entry( about, font='Helvetica 9', bg=bg, width=len(www), justify=CENTER )
-
-
-            line9.insert(0, www)
-            line9.configure(state='readonly')
-            line1.pack( padx=20, pady=10 )
-            line2.pack(pady=10 )
-            line3.pack(pady=10 )
-            line4.pack(pady=10 )
-            line5.pack(pady=10 )
-            line6.pack(pady=10 )
-            line7.pack(pady=10 )
-            line8.pack(pady=10 )
-            line9.pack(pady=10 )
-            hide = ( lambda about=about: about.withdraw() )
-            self.aboutBox = about
-            # Hide on close rather than destroying window
-            Wm.wm_protocol( about, name='WM_DELETE_WINDOW', func=hide )
-        # Show (existing) window
-        about.deiconify()
-
-    def createToolImages( self ):
-        "Create toolbar (and icon) images."
-
-    def hostDetails( self, _ignore=None ):
-        if ( self.selection is None or
-             self.net is not None or
-             self.selection not in self.itemToWidget ):
-            return
-        widget = self.itemToWidget[ self.selection ]
-        name = widget[ 'text' ]
-        tags = self.canvas.gettags( self.selection )
-
-        #print tags
-        if 'Host' in tags:
-
-                prefDefaults = self.hostOpts[name]
-                hostBox = HostDialog(self, title='Host Details', prefDefaults=prefDefaults, isRouter='False')
-                self.master.wait_window(hostBox.top)
-                if hostBox.result:
-                    newHostOpts = {'nodeNum':self.hostOpts[name]['nodeNum']}
-
-                    if len(hostBox.result['startCommand']) > 0:
-                        newHostOpts['startCommand'] = hostBox.result['startCommand']
-                    if hostBox.result['cpu']:
-                        newHostOpts['cpu'] = hostBox.result['cpu']
-                    if hostBox.result['mem']:
-                        newHostOpts['mem'] = hostBox.result['mem']
-                    if len(hostBox.result['hostname']) > 0:
-                        newHostOpts['hostname'] = hostBox.result['hostname']
-                        name = hostBox.result['hostname']
-                        widget[ 'text' ] = name
-                    if len(hostBox.result['cache']) > 0:
-                        newHostOpts['cache'] = hostBox.result['cache']
-
-                    newHostOpts['nlsr'] = hostBox.nlsrFrame.getValues()
-                    newHostOpts['nfd'] = hostBox.nfdFrame.getValues()
-
-                    self.hostOpts[name] = newHostOpts
-
-                    print 'New host details for ' + name + ' = ' + str(newHostOpts)
-
-        elif 'LegacyRouter' in tags:
-
-                prefDefaults = self.routerOpts[name]
-                hostBox = HostDialog(self, title='Router Details', prefDefaults=prefDefaults, isRouter='True')
-                self.master.wait_window(hostBox.top)
-                if hostBox.result:
-                    newRouterOpts = {'nodeNum':self.routerOpts[name]['nodeNum']}
-
-                    if hostBox.result['cpu']:
-                        newRouterOpts['cpu'] = hostBox.result['cpu']
-                    if hostBox.result['mem']:
-                        newRouterOpts['mem'] = hostBox.result['mem']
-                    if len(hostBox.result['hostname']) > 0:
-                        newRouterOpts['hostname'] = hostBox.result['hostname']
-                        name = hostBox.result['hostname']
-                        widget[ 'text' ] = name
-                    if len(hostBox.result['cache']) > 0:
-                        newRouterOpts['cache'] = hostBox.result['cache']
-                    self.routerOpts[name] = newRouterOpts
-
-                    print 'New host details for ' + name + ' = ' + str(newRouterOpts)
-
-    def linkDetails( self, _ignore=None ):
-        if ( self.selection is None or
-             self.net is not None):
-            return
-        link = self.selection
-
-        linkDetail =  self.links[link]
-        src = linkDetail['src']
-        dest = linkDetail['dest']
-        linkopts = linkDetail['linkOpts']
-        linkBox = LinkDialog(self, title='Link Details', linkDefaults=linkopts)
-        if linkBox.result is not None:
-            linkDetail['linkOpts'] = linkBox.result
-            print 'New link details = ' + str(linkBox.result)
-
-    # Model interface
-    #
-    # Ultimately we will either want to use a topo or
-    # mininet object here, probably.
-
-    def addLink( self, source, dest, linktype='data', linkopts={} ):
-        "Add link to model."
-        source.links[ dest ] = self.link
-        dest.links[ source ] = self.link
-        self.links[ self.link ] = {'type' :linktype,
-                                   'src':source,
-                                   'dest':dest,
-                                   'linkOpts':linkopts}
-
-    def deleteLink( self, link ):
-        "Delete link from model."
-        pair = self.links.get( link, None )
-        if pair is not None:
-            source=pair['src']
-            dest=pair['dest']
-            del source.links[ dest ]
-            del dest.links[ source ]
-            stags = self.canvas.gettags( self.widgetToItem[ source ] )
-            dtags = self.canvas.gettags( self.widgetToItem[ dest ] )
-            ltags = self.canvas.gettags( link )
-
-        if link is not None:
-            del self.links[ link ]
-
-    def deleteNode( self, item ):
-        "Delete node (and its links) from model."
-
-        widget = self.itemToWidget[ item ]
-        tags = self.canvas.gettags(item)
-
-        for link in widget.links.values():
-            # Delete from view and model
-            self.deleteItem( link )
-        del self.itemToWidget[ item ]
-        del self.widgetToItem[ widget ]
-
-    def do_linkPopup(self, event):
-        # display the popup menu
-        if ( self.net is None ):
-            try:
-                self.linkPopup.tk_popup(event.x_root, event.y_root)
-            finally:
-                # make sure to release the grab (Tk 8.0a1 only)
-                self.linkPopup.grab_release()
-
-    def do_legacyRouterPopup(self, event):
-        # display the popup menu
-        if ( self.net is None ):
-            try:
-                self.legacyRouterPopup.tk_popup(event.x_root, event.y_root)
-            finally:
-                # make sure to release the grab (Tk 8.0a1 only)
-                self.legacyRouterPopup.grab_release()
-
-    def do_hostPopup(self, event):
-        # display the popup menu
-        if ( self.net is None ):
-                try:
-                    self.hostPopup.tk_popup(event.x_root, event.y_root)
-                    isHostPopup = True
-                finally:
-                    # make sure to release the grab (Tk 8.0a1 only)
-                    self.hostPopup.grab_release()
-
-    def xterm( self, _ignore=None ):
-        "Make an xterm when a button is pressed."
-        if ( self.selection is None or
-             self.net is None or
-             self.selection not in self.itemToWidget ):
-            return
-        name = self.itemToWidget[ self.selection ][ 'text' ]
-        if name not in self.net.nameToNode:
-            return
-        term = makeTerm( self.net.nameToNode[ name ], 'Host', term=self.appPrefs['terminalType'] )
-        if StrictVersion(MININET_VERSION) > StrictVersion('2.0'):
-            self.net.terms += term
-        else:
-            self.net.terms.append(term)
-
-    def iperf( self, _ignore=None ):
-        "Make an xterm when a button is pressed."
-        if ( self.selection is None or
-             self.net is None or
-             self.selection not in self.itemToWidget ):
-            return
-        name = self.itemToWidget[ self.selection ][ 'text' ]
-        if name not in self.net.nameToNode:
-            return
-        self.net.nameToNode[ name ].cmd( 'iperf -s -p 5001 &' )
-
-    """ BELOW HERE IS THE TOPOLOGY IMPORT CODE """
-
-    def parseArgs( self ):
-        """Parse command-line args and return options object.
-           returns: opts parse options dict"""
-
-        if '--custom' in sys.argv:
-            index = sys.argv.index( '--custom' )
-            if len( sys.argv ) > index + 1:
-                filename = sys.argv[ index + 1 ]
-                self.parseCustomFile( filename )
-            else:
-                raise Exception( 'Custom file name not found' )
-
-        desc = ( "The %prog utility creates Minindn network from the\n"
-                 "command line. It can create parametrized topologies,\n"
-                 "invoke the Minindn CLI, and run tests." )
-
-        usage = ( '%prog [options] [template_file]\n'
-                  '\nIf no template_file is given, generated template will be written to the file minindn.conf in the current directory.\n'
-                  'Type %prog -h for details)' )
-
-        opts = OptionParser( description=desc, usage=usage )
-
-        addDictOption( opts, TOPOS, TOPODEF, 'topo' )
-        addDictOption( opts, LINKS, LINKDEF, 'link' )
-
-        opts.add_option( '--custom', type='string', default=None,
-                         help='read custom topo and node params from .py' +
-                         'file' )
-
-        self.options, self.args = opts.parse_args()
-        # We don't accept extra arguments after the options
-        if self.args:
-             if len(self.args) > 1:
-                     opts.print_help()
-                     exit()
-             else:
-                     self.template_file=self.args[0]
-
-    def setCustom( self, name, value ):
-        "Set custom parameters for MininetRunner."
-        if name in ( 'topos', 'switches', 'hosts', 'controllers' ):
-            # Update dictionaries
-            param = name.upper()
-            globals()[ param ].update( value )
-        elif name == 'validate':
-            # Add custom validate function
-            self.validate = value
-        else:
-            # Add or modify global variable or class
-            globals()[ name ] = value
-
-    def parseCustomFile( self, fileName ):
-        "Parse custom file and add params before parsing cmd-line options."
-        customs = {}
-        if os.path.isfile( fileName ):
-            execfile( fileName, customs, customs )
-            for name, val in customs.iteritems():
-                self.setCustom( name, val )
-        else:
-            raise Exception( 'could not find custom file: %s' % fileName )
-
-    def importTopo( self ):
-        print 'topo='+self.options.topo
-        if self.options.topo == 'none':
-            return
-        self.newTopology()
-        topo = buildTopo( TOPOS, self.options.topo )
-        link = customConstructor( LINKS, self.options.link )
-        importNet = Mininet(topo=topo, build=False, link=link)
-        importNet.build()
-
-        c = self.canvas
-        rowIncrement = 100
-        currentY = 100
-
-        # Add switches
-        print 'switches:'+str(len(importNet.switches))
-        columnCount = 0
-        for switch in importNet.switches:
-            name = switch.name
-            self.switchOpts[name] = {}
-            self.switchOpts[name]['nodeNum']=self.switchCount
-            self.switchOpts[name]['hostname']=name
-            self.switchOpts[name]['switchType']='default'
-            self.switchOpts[name]['controllers']=[]
-
-            x = columnCount*100+100
-            self.addNode('Switch', self.switchCount,
-                 float(x), float(currentY), name=name)
-            icon = self.findWidgetByName(name)
-            icon.bind('<Button-3>', self.do_switchPopup )
-
-            if columnCount == 9:
-                columnCount = 0
-                currentY = currentY + rowIncrement
-            else:
-                columnCount =columnCount+1
-
-        currentY = currentY + rowIncrement
-        # Add hosts
-        print 'hosts:'+str(len(importNet.hosts))
-        columnCount = 0
-        for host in importNet.hosts:
-            name = host.name
-            self.hostOpts[name] = {'sched':'host'}
-            self.hostOpts[name]['nodeNum']=self.hostCount
-            self.hostOpts[name]['hostname']=name
-            #self.hostOpts[name]['ip']=host.IP()
-
-            x = columnCount*100+100
-            self.addNode('Host', self.hostCount,
-                 float(x), float(currentY), name=name)
-            icon = self.findWidgetByName(name)
-            icon.bind('<Button-3>', self.do_hostPopup )
-            if columnCount == 9:
-                columnCount = 0
-                currentY = currentY + rowIncrement
-            else:
-                columnCount =columnCount+1
-
-        print 'links:'+str(len(topo.links()))
-        #[('h1', 's3'), ('h2', 's4'), ('s3', 's4')]
-        for link in topo.links():
-            print str(link)
-            srcNode = link[0]
-            src = self.findWidgetByName(srcNode)
-            sx, sy = self.canvas.coords( self.widgetToItem[ src ] )
-
-            destNode = link[1]
-            dest = self.findWidgetByName(destNode)
-            dx, dy = self.canvas.coords( self.widgetToItem[ dest]  )
-
-            params = topo.linkInfo( srcNode, destNode )
-            print 'Link Parameters='+str(params)
-
-            self.link = self.canvas.create_line( sx, sy, dx, dy, width=4,
-                                             fill='blue', tag='link' )
-            c.itemconfig(self.link, tags=c.gettags(self.link)+('data',))
-            self.addLink( src, dest, linkopts=params )
-            self.createDataLinkBindings()
-            self.link = self.linkWidget = None
-
-        importNet.stop()
-
-def miniEditImages():
-    "Create and return images for MiniEdit."
-
-    # Image data. Git will be unhappy. However, the alternative
-    # is to keep track of separate binary files, which is also
-    # unappealing.
-
-    return {
-        'Select': BitmapImage(
-            file='/usr/include/X11/bitmaps/left_ptr' ),
-
-        'LegacyRouter': PhotoImage( data=r"""
-            R0lGODlhMgAYAPcAAAEBAXZ8gQNAgL29vQNctjl/xVSa4j1dfCF+
-            3QFq1DmL3wJMmAMzZZW11dnZ2SFrtyNdmTSO6gIZMUKa8gJVqEOH
-            zR9Pf5W74wFjxgFx4jltn+np6Eyi+DuT6qKiohdtwwUPGWiq6ymF
-            4LHH3Rh11CV81kKT5AMoUA9dq1ap/mV0gxdXlytRdR1ptRNPjTt9
-            vwNgvwJZsX+69gsXJQFHjTtjizF0tvHx8VOm9z2V736Dhz2N3QM2
-            acPZ70qe8gFo0HS19wVRnTiR6hMpP0eP1i6J5iNlqAtgtktjfQFu
-            3TNxryx4xAMTIzOE1XqAh1uf5SWC4AcfNy1XgQJny93n8a2trRh3
-            12Gt+VGm/AQIDTmByAF37QJasydzvxM/ayF3zhdLf8zLywFdu4i5
-            6gFlyi2J4yV/1w8wUo2/8j+X8D2Q5Eee9jeR7Uia7DpeggFt2QNP
-            m97e3jRong9bpziH2DuT7aipqQoVICmG45vI9R5720eT4Q1hs1er
-            /yVVhwJJktPh70tfdbHP7Xev5xs5V7W1sz9jhz11rUVZcQ9WoCVV
-            hQk7cRdtwWuw9QYOFyFHbSBnr0dznxtWkS18zKfP9wwcLAMHCwFF
-            iS5UeqGtuRNNiwMfPS1hlQMtWRE5XzGM5yhxusLCwCljnwMdOFWh
-            7cve8pG/7Tlxp+Tr8g9bpXF3f0lheStrrYu13QEXLS1ppTV3uUuR
-            1RMjNTF3vU2X4TZupwRSolNne4nB+T+L2YGz4zJ/zYe99YGHjRdD
-            cT95sx09XQldsgMLEwMrVc/X3yN3yQ1JhTRbggsdMQNfu9HPz6Wl
-            pW2t7RctQ0GFyeHh4dvl8SBZklCb5kOO2kWR3Vmt/zdjkQIQHi90
-            uvPz8wIVKBp42SV5zbfT7wtXpStVfwFWrBVvyTt3swFz5kGBv2+1
-            /QlbrVFjdQM7d1+j54i67UmX51qn9i1vsy+D2TuR5zddhQsjOR1t
-            u0GV6ghbsDVZf4+76RRisent8Xd9hQFBgwFNmwJLlcPDwwFr1z2T
-            5yH5BAEAAAAALAAAAAAyABgABwj/AAEIHEiQYJY7Qwg9UsTplRIb
-            ENuxEiXJgpcz8e5YKsixY8Essh7JcbbOBwcOa1JOmJAmTY4cHeoI
-            abJrCShI0XyB8YRso0eOjoAdWpciBZajJ1GuWcnSZY46Ed5N8hPA
-            TqEBoRB9gVJsxRlhPwHI0kDkVywcRpGe9LF0adOnMpt8CxDnxg1o
-            9lphKoEACoIvmlxxvHOKVg0n/Tzku2WoVoU2J1P6WNkSrtwADuxC
-            G/MOjwgRUEIjGG3FhaOBzaThiDSCil27G8Isc3LLjZwXsA6YYJmD
-            jhTMmseoKQIFDx7RoxHo2abnwygAlUj1mV6tWjlelEpRwfd6gzI7
-            VeJQ/2vZoVaDUqigqftXpH0R46H9Kl++zUo4JnKq9dGvv09RHFhc
-            IUMe0NiFDyql0OJUHWywMc87TXRhhCRGiHAccvNZUR8JxpDTH38p
-            9HEUFhxgMSAvjbBjQge8PSXEC6uo0IsHA6gAAShmgCbffNtsQwIJ
-            ifhRHX/TpUUiSijlUk8AqgQixSwdNBjCa7CFoVggmEgCyRf01WcF
-            CYvYUgB104k4YlK5HONEXXfpokYdMrXRAzMhmNINNNzB9p0T57Ag
-            yZckpKKPGFNgw06ZWKR10jTw6MAmFWj4AJcQQkQQwSefvFeGCemM
-            IQggeaJywSQ/wgHOAmJskQEfWqBlFBEH1P/QaGY3QOpDZXA2+A6m
-            7hl3IRQKGDCIAj6iwE8yGKC6xbJv8IHNHgACQQybN2QiTi5NwdlB
-            pZdiisd7vyanByOJ7CMGGRhgwE+qyy47DhnBPLDLEzLIAEQjBtCh
-            RmVPNWgpr+Be+Nc9icARww9TkIEuDAsQ0O7DzGIQzD2QdDEJHTsI
-            AROc3F7qWQncyHPPHN5QQAAG/vjzw8oKp8sPPxDH3O44/kwBQzLB
-            xBCMOTzzHEMMBMBARgJvZJBBEm/4k0ACKydMBgwYoKNNEjJXbTXE
-            42Q9jtFIp8z0Dy1jQMA1AGziz9VoW7310V0znYDTGMQgwUDXLDBO
-            2nhvoTXbbyRk/XXL+pxWkAT8UJ331WsbnbTSK8MggDZhCTOMLQkc
-            jvXeSPedAAw0nABWWARZIgEDfyTzxt15Z53BG1PEcEknrvgEelhZ
-            MDHKCTwI8EcQFHBBAAFcgGPLHwLwcMIo12Qxu0ABAQA7
-            """),
-
-        'Host': PhotoImage( data=r"""
-            R0lGODlhIAAYAPcAMf//////zP//mf//Zv//M///AP/M///MzP/M
-            mf/MZv/MM//MAP+Z//+ZzP+Zmf+ZZv+ZM/+ZAP9m//9mzP9mmf9m
-            Zv9mM/9mAP8z//8zzP8zmf8zZv8zM/8zAP8A//8AzP8Amf8AZv8A
-            M/8AAMz//8z/zMz/mcz/Zsz/M8z/AMzM/8zMzMzMmczMZszMM8zM
-            AMyZ/8yZzMyZmcyZZsyZM8yZAMxm/8xmzMxmmcxmZsxmM8xmAMwz
-            /8wzzMwzmcwzZswzM8wzAMwA/8wAzMwAmcwAZswAM8wAAJn//5n/
-            zJn/mZn/Zpn/M5n/AJnM/5nMzJnMmZnMZpnMM5nMAJmZ/5mZzJmZ
-            mZmZZpmZM5mZAJlm/5lmzJlmmZlmZplmM5lmAJkz/5kzzJkzmZkz
-            ZpkzM5kzAJkA/5kAzJkAmZkAZpkAM5kAAGb//2b/zGb/mWb/Zmb/
-            M2b/AGbM/2bMzGbMmWbMZmbMM2bMAGaZ/2aZzGaZmWaZZmaZM2aZ
-            AGZm/2ZmzGZmmWZmZmZmM2ZmAGYz/2YzzGYzmWYzZmYzM2YzAGYA
-            /2YAzGYAmWYAZmYAM2YAADP//zP/zDP/mTP/ZjP/MzP/ADPM/zPM
-            zDPMmTPMZjPMMzPMADOZ/zOZzDOZmTOZZjOZMzOZADNm/zNmzDNm
-            mTNmZjNmMzNmADMz/zMzzDMzmTMzZjMzMzMzADMA/zMAzDMAmTMA
-            ZjMAMzMAAAD//wD/zAD/mQD/ZgD/MwD/AADM/wDMzADMmQDMZgDM
-            MwDMAACZ/wCZzACZmQCZZgCZMwCZAABm/wBmzABmmQBmZgBmMwBm
-            AAAz/wAzzAAzmQAzZgAzMwAzAAAA/wAAzAAAmQAAZgAAM+4AAN0A
-            ALsAAKoAAIgAAHcAAFUAAEQAACIAABEAAADuAADdAAC7AACqAACI
-            AAB3AABVAABEAAAiAAARAAAA7gAA3QAAuwAAqgAAiAAAdwAAVQAA
-            RAAAIgAAEe7u7t3d3bu7u6qqqoiIiHd3d1VVVURERCIiIhEREQAA
-            ACH5BAEAAAAALAAAAAAgABgAAAiNAAH8G0iwoMGDCAcKTMiw4UBw
-            BPXVm0ixosWLFvVBHFjPoUeC9Tb+6/jRY0iQ/8iVbHiS40CVKxG2
-            HEkQZsyCM0mmvGkw50uePUV2tEnOZkyfQA8iTYpTKNOgKJ+C3AhO
-            p9SWVaVOfWj1KdauTL9q5UgVbFKsEjGqXVtP40NwcBnCjXtw7tx/
-            C8cSBBAQADs=
-        """ ),
-
-        'Switch': PhotoImage( data=r"""
-            R0lGODlhMgAYAPcAAAEBAXmDjbe4uAE5cjF7xwFWq2Sa0S9biSlrrdTW1k2Ly02a5xUvSQFHjmep
-            6bfI2Q5SlQIYLwFfvj6M3Jaan8fHyDuFzwFp0Vah60uU3AEiRhFgrgFRogFr10N9uTFrpytHYQFM
-            mGWt9wIwX+bm5kaT4gtFgR1cnJPF9yt80CF0yAIMGHmp2c/P0AEoUb/P4Fei7qK4zgpLjgFkyQlf
-            t1mf5jKD1WWJrQ86ZwFAgBhYmVOa4MPV52uv8y+A0iR3ywFbtUyX5ECI0Q1UmwIcOUGQ3RBXoQI0
-            aRJbpr3BxVeJvQUJDafH5wIlS2aq7xBmv52lr7fH12el5Wml3097ph1ru7vM3HCz91Ke6lid40KQ
-            4GSQvgQGClFnfwVJjszMzVCX3hljrdPT1AFLlBRnutPf6yd5zjeI2QE9eRBdrBNVl+3v70mV4ydf
-            lwMVKwErVlul8AFChTGB1QE3bsTFxQImTVmAp0FjiUSM1k+b6QQvWQ1SlxMgLgFixEqU3xJhsgFT
-            pn2Xs5OluZ+1yz1Xb6HN+Td9wy1zuYClykV5r0x2oeDh4qmvt8LDwxhuxRlLfyRioo2124mft9bi
-            71mDr7fT79nl8Z2hpQs9b7vN4QMQIOPj5XOPrU2Jx32z6xtvwzeBywFFikFnjwcPFa29yxJjuFmP
-            xQFv3qGxwRc/Z8vb6wsRGBNqwqmpqTdvqQIbNQFPngMzZAEfP0mQ13mHlQFYsAFnznOXu2mPtQxj
-            vQ1Vn4Ot1+/x8my0/CJgnxNNh8DT5CdJaWyx+AELFWmt8QxPkxBZpwMFB015pgFduGCNuyx7zdnZ
-            2WKm6h1xyOPp8aW70QtPkUmM0LrCyr/FyztljwFPm0OJzwFny7/L1xFjswE/e12i50iR2VR8o2Gf
-            3xszS2eTvz2BxSlloQdJiwMHDzF3u7bJ3T2I1WCp8+Xt80FokQFJklef6mORw2ap7SJ1y77Q47nN
-            3wFfu1Kb5cXJyxdhrdDR0wlNkTSF11Oa4yp4yQEuW0WQ3QIDBQI7dSH5BAEAAAAALAAAAAAyABgA
-            Bwj/AAEIHDjKF6SDvhImPMHwhA6HOiLqUENRDYSLEIplxBcNHz4Z5GTI8BLKS5OBA1Ply2fDhxwf
-            PlLITGFmmRkzP+DlVKHCmU9nnz45csSqKKsn9gileZKrVC4aRFACOGZu5UobNuRohRkzhc2b+36o
-            qCaqrFmzZEV1ERBg3BOmMl5JZTBhwhm7ZyycYZnvJdeuNl21qkCHTiPDhxspTtKoQgUKCJ6wehMV
-            5QctWupeo6TkjOd8e1lmdQkTGbTTMaDFiDGINeskX6YhEicUiQa5A/kUKaFFwQ0oXzjZ8Tbcm3Hj
-            irwpMtTSgg9QMJf5WEZ9375AiED19ImpSQSUB4Kw/8HFSMyiRWJaqG/xhf2X91+oCbmq1e/MFD/2
-            EcApVkWVJhp8J9AqsywQxDfAbLJJPAy+kMkL8shjxTkUnhOJZ5+JVp8cKfhwxwdf4fQLgG4MFAwW
-            KOZRAxM81EAPPQvoE0QQfrDhx4399OMBMjz2yCMVivCoCAWXKLKMTPvoUYcsKwi0RCcwYCAlFjU0
-            A6OBM4pXAhsl8FYELYWFWZhiZCbRQgIC2AGTLy408coxAoEDx5wwtGPALTVg0E4NKC7gp4FsBKoA
-            Ki8U+oIVmVih6DnZPMBMAlGwIARWOLiggSYC+ZNIOulwY4AkSZCyxaikbqHMqaeaIp4+rAaxQxBg
-            2P+IozuRzvLZIS4syYVAfMAhwhSC1EPCGoskIIYY9yS7Hny75OFnEIAGyiVvWkjjRxF11fXIG3WU
-            KNA6wghDTCW88PKMJZOkm24Z7LarSjPtoIjFn1lKyyVmmBVhwRtvaDDMgFL0Eu4VhaiDwhXCXNFD
-            D8QQw7ATEDsBw8RSxotFHs7CKJ60XWrRBj91EOGPQCA48c7J7zTjSTPctOzynjVkkYU+O9S8Axg4
-            Z6BzBt30003Ps+AhNB5C4PCGC5gKJMMTZJBRytOl/CH1HxvQkMbVVxujtdZGGKGL17rsEfYQe+xR
-            zNnFcGQCv7LsKlAtp8R9Sgd0032BLXjPoPcMffTd3YcEgAMOxOBA1GJ4AYgXAMjiHDTgggveCgRI
-            3RfcnffefgcOeDKEG3444osDwgEspMNiTQhx5FoOShxcrrfff0uQjOycD+554qFzMHrpp4cwBju/
-            5+CmVNbArnntndeCO+O689777+w0IH0o1P/TRJMohRA4EJwn47nyiocOSOmkn/57COxE3wD11Mfh
-            fg45zCGyVF4Ufvvyze8ewv5jQK9++6FwXxzglwM0GPAfR8AeSo4gwAHCbxsQNCAa/kHBAVhwAHPI
-            4BE2eIRYeHAEIBwBP0Y4Qn41YWRSCQgAOw==
-        """ ),
-
-        'NetLink': PhotoImage( data=r"""
-            R0lGODlhFgAWAPcAMf//////zP//mf//Zv//M///AP/M///MzP/M
-            mf/MZv/MM//MAP+Z//+ZzP+Zmf+ZZv+ZM/+ZAP9m//9mzP9mmf9m
-            Zv9mM/9mAP8z//8zzP8zmf8zZv8zM/8zAP8A//8AzP8Amf8AZv8A
-            M/8AAMz//8z/zMz/mcz/Zsz/M8z/AMzM/8zMzMzMmczMZszMM8zM
-            AMyZ/8yZzMyZmcyZZsyZM8yZAMxm/8xmzMxmmcxmZsxmM8xmAMwz
-            /8wzzMwzmcwzZswzM8wzAMwA/8wAzMwAmcwAZswAM8wAAJn//5n/
-            zJn/mZn/Zpn/M5n/AJnM/5nMzJnMmZnMZpnMM5nMAJmZ/5mZzJmZ
-            mZmZZpmZM5mZAJlm/5lmzJlmmZlmZplmM5lmAJkz/5kzzJkzmZkz
-            ZpkzM5kzAJkA/5kAzJkAmZkAZpkAM5kAAGb//2b/zGb/mWb/Zmb/
-            M2b/AGbM/2bMzGbMmWbMZmbMM2bMAGaZ/2aZzGaZmWaZZmaZM2aZ
-            AGZm/2ZmzGZmmWZmZmZmM2ZmAGYz/2YzzGYzmWYzZmYzM2YzAGYA
-            /2YAzGYAmWYAZmYAM2YAADP//zP/zDP/mTP/ZjP/MzP/ADPM/zPM
-            zDPMmTPMZjPMMzPMADOZ/zOZzDOZmTOZZjOZMzOZADNm/zNmzDNm
-            mTNmZjNmMzNmADMz/zMzzDMzmTMzZjMzMzMzADMA/zMAzDMAmTMA
-            ZjMAMzMAAAD//wD/zAD/mQD/ZgD/MwD/AADM/wDMzADMmQDMZgDM
-            MwDMAACZ/wCZzACZmQCZZgCZMwCZAABm/wBmzABmmQBmZgBmMwBm
-            AAAz/wAzzAAzmQAzZgAzMwAzAAAA/wAAzAAAmQAAZgAAM+4AAN0A
-            ALsAAKoAAIgAAHcAAFUAAEQAACIAABEAAADuAADdAAC7AACqAACI
-            AAB3AABVAABEAAAiAAARAAAA7gAA3QAAuwAAqgAAiAAAdwAAVQAA
-            RAAAIgAAEe7u7t3d3bu7u6qqqoiIiHd3d1VVVURERCIiIhEREQAA
-            ACH5BAEAAAAALAAAAAAWABYAAAhIAAEIHEiwoEGBrhIeXEgwoUKG
-            Cx0+hGhQoiuKBy1irChxY0GNHgeCDAlgZEiTHlFuVImRJUWXEGEy
-            lBmxI8mSNknm1Dnx5sCAADs=
-        """ )
-    }
-
-def addDictOption( opts, choicesDict, default, name, helpStr=None ):
-    """Convenience function to add choices dicts to OptionParser.
-       opts: OptionParser instance
-       choicesDict: dictionary of valid choices, must include default
-       default: default choice key
-       name: long option name
-       help: string"""
-    if default not in choicesDict:
-        raise Exception( 'Invalid  default %s for choices dict: %s' %
-                         ( default, name ) )
-    if not helpStr:
-        helpStr = ( '|'.join( sorted( choicesDict.keys() ) ) +
-                    '[,param=value...]' )
-    opts.add_option( '--' + name,
-                     type='string',
-                     default = default,
-                     help = helpStr )
-
-if __name__ == '__main__':
-    setLogLevel( 'info' )
-    app = MiniEdit()
-    """ import topology if specified """
-    app.parseArgs()
-    app.importTopo()
-
-    global isHostPopup
-    global isRouterPopup
-    global isLinkPopup
-
-    app.mainloop()
diff --git a/docs/CLUSTER.md b/docs/CLUSTER.md
deleted file mode 100644
index e1e52ad..0000000
--- a/docs/CLUSTER.md
+++ /dev/null
@@ -1,25 +0,0 @@
-Mini-NDN cluster edition
-========================
-
-Mini-NDN cluster edition uses the experimental Mininet cluster edition.
-**Make sure that you can run the Mininet cluster edition by following
-[these instructions](https://github.com/mininet/mininet/wiki/Cluster-Edition-Prototype)**.
-Mini-NDN will use the "mininet" username created in Mininet cluster edition setup.
-
-## Mini-NDN cluster options
-
-To run Mini-NDN cluster on `localhost` and another server `server1` with
-the guided node placement strategy (default), the following command can be used:
-
-    sudo minindn --cluster=localhost,server1 --place-list=1,3
-
-Note that `place-list` specifies the number of nodes to be placed on the corresponding servers
-of the cluster.
-In the example, one node will be placed on `localhost` and three nodes on `server1`.
-Unless specified, the default 4 node topology is used.
-Another placement can be `roundRobin` placement algorithm from Mininet.
-This does not require a place-list.
-
-    sudo minindn --cluster=localhost,server1 --placement roundRobin
-
-By default the tunnel type used is SSH, but GRE tunnel can be specified by `--tunnel-type=gre`
diff --git a/docs/CONFIG.md b/docs/CONFIG.md
deleted file mode 100644
index d2a5bf1..0000000
--- a/docs/CONFIG.md
+++ /dev/null
@@ -1,68 +0,0 @@
-Configuration
-=============
-
-Mini-NDN uses a configuration file describing the topology and its parameters to setup a network.
-It can be generated by the GUI or written by hand or scripts.
-
-## Configuration file format:
-
-### The [nodes] section:
-
-At the bare minimum, the node section describes the nodes present in the topology.
-
-    [nodes]
-    a: _
-    b: _
-
-Additionally each node can take the following parameters:
-
-* app   : Default application(s) to be started on a node (specify '_' if no app needs to be started - required).
-The app is the only parameter which needs double quotes (see example below).
-
-* cpu   : Amount of cpu available to a node (0.00 - 1.00), optional
-
-* mem   : Amount of memory available to a node in KB
-
-* cache : Amount of cache memory available to a node in KB
-
-* nfd-log-level: Set the log level of the NFD running on the node (ex: DEBUG). For finer control,
-nfd.conf or nfd.conf.sample needs to be modified in /usr/local/etc/ndn/
-
-* nlsr-log-level: Set the log level of the NLSR running on the node (ex: DEBUG).
-
-    e.g.)
-
-    [nodes]
-    a: _ cpu=0.3 nfd-log-level=TRACE nlsr-log-level=NONE
-    b: app="sample app 1; sampleapp2.sh" cpu=0.3
-
-### The [links] section:
-
-The links section describes the links in the topology.
-
-    e.g.)
-
-    [links]
-    a:b delay=10ms
-
-This would create a link between a and b. 'b:a' would also result in the same.
-The following parameters can be configured for a node:
-
-* delay : Delay parameter is a required parameter which defines the delay of the link (1-1000ms)
-
-* bw    : Bandwidth of a link (<1-1000> Mbps)
-
-* loss  : Percentage of packet loss (<1-100>)
-
-### Example configuration file
-
-    [nodes]
-    a: _ cpu=0.3
-    b: app="sampleApp1; ./sampleApp2.sh" cpu=0.3
-    [links]
-    a:b delay=10ms bw=100
-
-Note that `sampleApp1` and `sampleApp2` must be either installed in the system (ex: /usr/bin)
-or an absolute path needs to be given.
-
-See `topologies` for more sample files
diff --git a/docs/CONNECT-TO-OUTSIDE.md b/docs/CONNECT-TO-OUTSIDE.md
deleted file mode 100644
index eb3f5b6..0000000
--- a/docs/CONNECT-TO-OUTSIDE.md
+++ /dev/null
@@ -1,91 +0,0 @@
-Connect Mini-NDN nodes to an outside network
-============================================
-
-Mini-NDN nodes can be connected to an outside network indirectly by running NFD on the local machine:
-
-    (Mini-NDN node) ------ (NFD running on the host machine where Mini-NDN is running) ------- (External Network)
-
-## Add a node in root namespace
-
-For this simple example, we use the default topology:
-
-    c----a----b----d
-
-If we want node "a" to connect to the host machine, we need to add a "root" node which has a link with node "a."
-
-To do so, we need to add the following import statement at the top of `bin/minindn`:
-
-```python
-from mininet.node import Node
-```
-
-Then the following lines can be added to `bin/minindn` before net.start():
-
-```python
-root = Node( 'root', inNamespace=False )
-net.addLink(root, 'a')
-```
-
-Adding these before net.start() is important as node "root" would then be assigned an IP automatically.
-Re-install Mini-NDN by issuing the following command in the mini-ndn folder:
-
-    sudo ./install.sh -i
-
-## Configuration
-
-Run Mini-NDN with the simple topology and issue ifconfig on the local machine to confirm the addition of
-the interface. You should be able to locate "root-eth0":
-
-    root-eth0 Link encap:Ethernet  HWaddr 3e:eb:77:d2:6f:1f
-              inet addr:1.0.0.9  Bcast:1.0.0.11  Mask:255.255.255.252
-              inet6 addr: fe80::3ceb:77ff:fed2:6f1f/64 Scope:Link
-              UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
-              RX packets:34 errors:0 dropped:0 overruns:0 frame:0
-              TX packets:33 errors:0 dropped:0 overruns:0 carrier:0
-              collisions:0 txqueuelen:1000
-              RX bytes:2667 (2.6 KB)  TX bytes:2797 (2.7 KB)
-
-To make the IP address associated with this interface persistent, add the following line to
-/etc/network/interfaces and reboot your machine:
-
-    iface root-eth0 inet manual
-
-## Check connection
-
-After rebooting, run Mini-NDN and issue the following command:
-
-    mini-ndn>net
-    a a-eth0:b-eth0 a-eth1:c-eth0 a-eth2:root-eth0
-
-Node "a" is connected to "root-eth0". Now issue "ifconfig a-eth2" on node "a":
-
-    mini-ndn>a ifconfig a-eth2
-    a-eth2    Link encap:Ethernet  HWaddr fa:76:d4:86:d3:ba
-              inet addr:1.0.0.10  Bcast:1.0.0.11  Mask:255.255.255.252
-
-As learned from the previous step, the IP address of root-eth0 is 1.0.0.9.
-
-    mini-ndn>a ping 1.0.0.9
-    PING 1.0.0.9 (1.0.0.9) 56(84) bytes of data.
-    64 bytes from 1.0.0.9: icmp_seq=1 ttl=64 time=0.137 ms
-    64 bytes from 1.0.0.9: icmp_seq=2 ttl=64 time=0.123 ms
-
-The host machine will also be able to ping node "a":
-
-    VirtualBox:~$ ping 1.0.0.10
-    PING 1.0.0.10 (1.0.0.10) 56(84) bytes of data.
-    64 bytes from 1.0.0.10: icmp_seq=1 ttl=64 time=0.086 ms
-
-## Run NFD on local machine and register route
-
-Start NFD on the local machine by using:
-
-    sudo nfd
-
-The "nfd-start" script cannot be used, since the script allows only one instance of NFD at a time.
-The NFD processes running on the Mini-NDN nodes will prevent the "nfd-start" script from working.
-
-Now, using "nfdc register", we can register a route from node "a" in Mini-NDN to the NFD process on the
-host machine and from the host machine to an external machine.
-
-Also, if the local machine has a public IP, Mini-NDN nodes can be reached via external machines.
diff --git a/docs/EXPERIMENTS.md b/docs/EXPERIMENTS.md
deleted file mode 100644
index a2191ac..0000000
--- a/docs/EXPERIMENTS.md
+++ /dev/null
@@ -1,207 +0,0 @@
-Experiments
-===========
-
-Mini-NDN includes an experimentation framework which allows a user to create and automate
-networking experiments. Users can run existing experiments, included with Mini-NDN, or define
-their own custom experiment.
-
-## Running existing experiments
-
-Mini-NDN includes three example experiments that can be used to test the network or as reference
-for custom experiment implementations. `ndn-tools` must be installed to run the example
-experiments as each experiment uses both `ndnpingserver` and `ndnping`. Please see
-[INSTALL.md](../INSTALL.md) for instructions on installing `ndn-tools`.
-
-To see a list of the available experiments, run Mini-NDN using the `--list-experiments` parameter:
-
-    minindn --list-experiments
-
-To run an experiment, provide the experiment name as an argument to the `--experiment` parameter:
-
-    sudo minindn --experiment=pingall
-
-Each experiment will run until completion or exit if there is an error setting up the
-test environment.
-
-The three included experiments are set up using the same starting
-configuration. Each node runs NFD, NLSR, and an ndnpingserver which advertises the node's
-site name. After a waiting period to allow the network to converge (default is 60 seconds),
-the convergence status of the network is checked. If each node's FIB does not have an entry
-for every other node's router name and advertised prefix, the experiment is aborted and an error
-is reported.
-
-#### Common experiment parameters
-
-The time allowed for convergence (in seconds) can be configured using the `--ctime` parameter:
-
-    sudo minindn --ctime=30 ...
-
-After the experiment has finished running, the command-line interface (CLI) will be launched and the
-user can then interact with the test environment. To disable the CLI and instead exit Mini-NDN
-as soon as the experiment has finished, use the `--no-cli` parameter:
-
-    sudo minindn --no-cli ...
-
-To ping only a percentage of nodes `--pct-traffic` can be set.
-
-    sudo minindn --pct-traffic=0.5 ...
-
-The above command will ping only 50% of other nodes from each node.
-The default value is 1 i.e. ping every other node.
-
-To move the experiment results to a results directory from the working directory
-after the experiment is complete (either --no-cli or quit) the following option
-can be used:
-
-    sudo minindn --result-dir /home/mydir/result-dir ...
-
-The included experiments are described in detail below along with additional
-parameters that can be provided to modify the execution of the experiments.
-
-### Pingall experiment
-
-**Scenario**: Each node in the network simultaneously pings every other node in the network at a
-one second interval.
-
-**Experiment ID**: `--pingall`
-
-The number of pings sent in the experiment can be configured using the `--nPings` parameter:
-
-    sudo minindn --experiment=pingall --nPings=120
-
-By default, `--nPings` is 300 for the Pingall experiment.
-
-### Failure experiment
-
-**Scenario**: Each node in the network simultaneously pings every other node in the network at a
-one second interval. After 60 seconds, the node with the name "csu" is brought down. The node is
-left in a failed state for 120 seconds while the other nodes continue pinging. After this period,
-the failed node is recovered and pings are collected for an additional 90 seconds.
-
-**Experiment ID**: `--failure`
-
-`--nPings` is 300 for the Failure experiment and cannot be modified.
-
-### Multiple failure experiment
-
-**Scenario**: Each node in the network simultaneously pings every other node in the network at a
-one second interval. After 60 seconds, the first node in the network will be brought down and remain
-in failed state for 60 seconds. After the failure period, the node is recovered and the network
-is allowed to recover for 60 seconds. After the recovery period, the next node will go through this
-failure and recovery process. Once every node in the network has gone through the failure and
-recovery process, the experiment will end.
-
-**Experiment ID**: `--multiple-failure`
-
-`--nPings` is dependent on the size of the topology being tested. 120 pings are scheduled for
-each node's failure/recovery period as well as an additional 60 pings for the initial collection
-period.
-
-### MCN failure experiment
-
-**Scenario**: This is exactly like the failure experiment but instead of failing the node named "csu" it fails the most connected node (MCN) i.e the node with the most links.
-
-Experiment ID: `--mcn-failure`
-
-### Experiment data
-
-The ping data is stored at `/tmp/minindn/node-name/ping-data`.
-
-The ping server log is stored at `/tmp/minindn/node-name/ping-server`
-
-## Creating custom experiments
-
-Mini-NDN provides a simple Python based framework which allows a user to define their own experiment
-and run it from the command line.
-
-To create an experiment, follow these steps:
-
-1. Create a Python source file for the experiment in the `ndn/experiments` directory.
-
-   e.g.) `ndn/experiments/example.py`
-
-2. Derive the experiment from the `Experiment` base class defined in
-   `ndn/experiments/experiment.py`.
-
-        #!/usr/bin/python
-
-        from ndn.experiments.experiment import Experiment
-
-        class ExampleExperiment(Experiment):
-            def __init__(self, args):
-                Experiment.__init__(self, args)
-
-3. Override `start()` if the experiments want to override NLSR setup and skip `setup()` and `run()`
-as described below. `start()` is the entry point for an experiment.
-
-4. Override the `setup()` method to define how the experiment should be initialized
-
-   e.g.) Run an ndnping server in the background on each node
-
-        def setup(self):
-           for host in self.net.hosts:
-               host.cmd("ndnpingserver host.name &")
-
-
-5. Override the `run()` method to define how the experiment should behave
-
-    e.g.) Obtain the NFD status of each node and save it to file
-
-        def run(self):
-            for host in self.net.hosts:
-                host.cmd("nfdc status report > status.txt")
-
-6. Register the experiment with the `ExperimentManager` to make the experiment runnable from the
-command line.
-
-        Experiment.register("example-name", ExampleExperiment)
-
-The experiment can then be run from the command-line using the name registered.
-"example-name" in the above example:
-
-        sudo minindn --experiment=example-name
-
-### Full example experiment code
-
-    #!/usr/bin/python
-
-    from ndn.experiments.experiment import Experiment
-
-    class ExampleExperiment(Experiment):
-        def __init__(self, args):
-            Experiment.__init__(self, args)
-
-        def setup(self):
-            for host in self.net.hosts:
-                host.cmd("ndnpingserver host.name &")
-
-        def run(self):
-            for host in self.net.hosts:
-                # By default status.txt would be stored
-                # at /tmp/minindn/host/status.txt
-                host.cmd("nfdc status report > status.txt")
-
-    Experiment.register("example-name", ExampleExperiment)
-
-## Passing arguments to experiments
-
-Mini-NDN has the capacity to pass arguments to experiments, insofar
-as they do not clash with those of Mini-NDN. This feature
-allows users to pass arguments to Mini-NDN and process them in an
-experiment without having to rewrite Mini-NDN's core.
-
-An example of an experiment implementing this functionality is given in
-`ndn/experiments/arguments_experiment.py`, which demonstrates how to
-write code which handles arguments. Documentation for these arguments
-appears when called with `sudo minindn -h`, as does documentation for
-fixed arguments defined in core code, so it is strongly recommended to
-differentiate those you write yourself. Note that Bash and Zsh users
-can make use of autocomplete functionality when calling these arguments.
-
-To run the experiment:
-
-    sudo minindn --experiment arg-exp --ds 226 --logging
-
-The experiment will print out the supplied value for --ds and a boolean
-value for the presence of --logging. --experiment is a fixed argument of
-Mini-NDN.
\ No newline at end of file
diff --git a/docs/GETTING-STARTED.md b/docs/GETTING-STARTED.md
deleted file mode 100644
index d15e95f..0000000
--- a/docs/GETTING-STARTED.md
+++ /dev/null
@@ -1,115 +0,0 @@
-Getting Started
-===============
-
-## Installation
-Please see [INSTALL.md](../INSTALL.md) for instructions on installing Mini-NDN and its dependencies.
-
-## Running Mini-NDN
-
-To run Mini-NDN with the default topology, `ndn_utils/topologies/default-topology.conf`, type:
-
-    sudo minindn
-
-A full list of options can be printed by using:
-
-    sudo minindn --help
-
-To run Mini-NDN with a topology file, provide the filename as the first argument:
-
-    sudo minindn my-topology.conf
-
-During set up, the list of nodes in the network will be listed as they are initialized:
-
-    *** Adding hosts:
-    a b c d
-
-After set up, the command-line interface (CLI) will display a prompt.
-
-    mini-ndn>
-
-To interact with a node, first type the node's name and then the command to be executed:
-
-    mini-ndn> a echo "Hello, world!"
-    Hello, world!
-
-To see the status of the forwarder on the node:
-
-    mini-ndn> a nfdc status report
-
-To see the status of routing on the node:
-
-    mini-ndn> a nlsrc status
-
-To exit Mini-NDN, type `quit` in the CLI:
-
-    mini-ndn> quit
-
-Another option to quit Mini-NDN is sending a SIGQUIT (ctrl+\). SIGINT (ctrl+c)
-is reserved for the purpose of stopping applications initiated on the minindn command
-line.
-
-For a more in depth explanation of the CLI, please see the
-[Mininet Walkthrough](http://mininet.org/walkthrough/).
-
-## Command-line options
-
-To run Mini-NDN with a replica of the NDN testbed, use the `--testbed` parameter:
-
-    sudo minindn --testbed
-
-To change the working directory from default `/tmp/minindn` following option can be used:
-
-    sudo minindn --work-dir /home/mydir/test
-
-Autocomplete of command-line options is available for users of Bash and Zsh.
-
-#### Routing options
-
-To run minindn without NLSR, use the `--no-nlsr` parameter:
-
-    sudo minindn --no-nlsr
-
-To run NLSR with hyperbolic routing enabled, use the `--routing` parameter:
-
-    sudo minindn --routing hr
-
-Topology files given under ndn_utils/topologies/minindn* have hyperbolic coordinates configured
-and can be used with this option.
-
-To run NLSR in dry-run mode, use the `--routing` parameter:
-
-    sudo minindn --routing dry
-
-To configure the max number of faces added by NLSR to reach each name prefix, use the `--faces`
-parameter:
-
-    sudo minindn --faces 3
-
-`--faces` can be an integer from 0 to 60; 0 indicates NLSR can add all available faces.
-
-To run Mini-NDN with NLSR security configured
-
-    sudo minindn --nlsr-security
-
-## Working Directory Structure
-
-Currently Mini-NDN uses /tmp/minindn as the working directory if not specified otherwise by using
-the option --work-dir.
-
-Each node is given a HOME directory under /tmp/minindn/node-name
-where node-name is the name of the node specified in the [nodes] section of the conf file.
-
-### NFD
-NFD conf file is stored at `/tmp/minindn/node-name/node-name.conf`
-
-NFD log file is stored at `/tmp/minindn/node-name/node-name.log`
-
-`.ndn` folder is stored at `/tmp/minindn/node-name/.ndn`
-
-### NLSR
-NLSR conf file is stored at `/tmp/minindn/node-name/nlsr.conf`
-
-NLSR log file is stored at `/tmp/minindn/node-name/log/nlsr.log`
-
-When security is enabled, NLSR security certificates are stored in: `/tmp/minindn/node-name/security`
-Note that no NLSR publishes the root certificate, Mini-NDN installs root.cert in security folder for each NLSR.
diff --git a/docs/GUI.md b/docs/GUI.md
deleted file mode 100644
index b190eeb..0000000
--- a/docs/GUI.md
+++ /dev/null
@@ -1,81 +0,0 @@
-Mini-NDN Edit
-=============
-
-Mini-NDN Edit is a GUI program designed to simplify the creation of Mini-NDN topology
-configuration files. Mini-NDN Edit allows the user to build a topology, configure the hosts and
-links, and generate a topology file that can be used by Mini-NDN.
-
-![minindnedit](img/gui/minindnedit.png)
-
-## Working with topology files
-
-#### File formats
-
-There are two file formats used by Mini-NDN edit.
-
-* `.mnndn` files store topology information used by the GUI. Mini-NDN Edit can open and
-save `.mnndn` files.
-
-* `.conf` files are used by Mini-NDN to create and run topologies. Mini-NDN Edit can generate a
-`.conf` file from a `.mnndn` file. Mini-NDN edit cannot open `.conf` files. Please see
-[CONFIG-FILE.md](CONFIG.md) for more information on the content and format of `.conf` files.
-
-#### File menu
-
-![file-dropdown](img/gui/file-dropdown.png)
-
-* **New** - Erase the current canvas and provide a new empty topology
-* **Open** - Open a `.mnndn` topology file
-* **Save** - Save the current topology as a `.mnndn` topology file
-* **Generate** - Convert the current topology to a `.conf` file runnable by Mini-NDN
-* **Run** - Run the current topology in Mini-NDN. The GUI will disappear and Mini-NDN will run on
-            the command-line.
-* **Quit** - Exit Mini-NDN edit
-
-## Tools
-
-### Cursor tool
-![cursor-tool](img/gui/cursor-tool.png)
-
-The *cursor tool* is used to select and move nodes in the topology. A selected node or link can be
-removed from the topology using the `backspace` key or by selecting `edit/cut`.
-
-### Host tool
-![host-tool](img/gui/host-tool.png)
-
-The *host tool* is used to add a host node to the topology. Click on the canvas to create a new host
-node at the mouse cursor's position.
-
-### Switch tool
-![switch-tool](img/gui/switch-tool.png)
-
-The *switch tool* is used to add a switch to the topology. Click on the canvas to create a new
-switch at the mouse cursor's position.
-
-### Link tool
-![link-tool](img/gui/link-tool.png)
-
-The *link tool* is used to connect two nodes with a network link. Click on a node and drag the mouse
-cursor to another node to create a link between the two nodes.
-
-## Configuration
-
-#### Editing host information
-
-To edit a host's information and configuration, right-click on the desired node and select
-`Host Options`.
-
-The *Host Options* window will include tabs to modify the node's properties as well as the
-configuration files for NFD and NLSR on that node.
-
-*Note: Network, Site, and Router in the NLSR tab are unused in v0.1.0*
-
-![host-config](img/gui/host-config.png)
-
-#### Editing link configuration
-
-To edit a link's configuration, right-click on the desired link and select `Link Options`.
-
-The *Link Options* window will allow configuration of the link's bandwidth, delay, and loss rate.
-
-![link-details](img/gui/link-details.png)
diff --git a/docs/Makefile b/docs/Makefile
new file mode 100644
index 0000000..d4bb2cb
--- /dev/null
+++ b/docs/Makefile
@@ -0,0 +1,20 @@
+# Minimal makefile for Sphinx documentation
+#
+
+# You can set these variables from the command line, and also
+# from the environment for the first two.
+SPHINXOPTS    ?=
+SPHINXBUILD   ?= sphinx-build
+SOURCEDIR     = .
+BUILDDIR      = _build
+
+# Put it first so that "make" without argument is like "make help".
+help:
+	@$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
+
+.PHONY: help Makefile
+
+# Catch-all target: route all unknown targets to Sphinx using the new
+# "make mode" option.  $(O) is meant as a shortcut for $(SPHINXOPTS).
+%: Makefile
+	@$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
diff --git a/docs/RELEASE-NOTES.md b/docs/RELEASE-NOTES.md
deleted file mode 100644
index 9a825d7..0000000
--- a/docs/RELEASE-NOTES.md
+++ /dev/null
@@ -1,176 +0,0 @@
-Release Notes
-=============
-
-Mini-NDN version 0.4.0 (changes since version 0.3.0)
-----------------------------------------------------
-
-Release date: January 10, 2018
-
-**New features**:
-
-- Use SIGQUIT to quit Mini-NDN, SIGINT to kill programs
-
-- Use Infoedit to edit NFD and NLSR configuration files
-
-- Use nlsr.conf installed in the system
-
-- Provide a Vagrantfile to setup Mini-NDN and NDN
-
-- Provide option to disable NLSR
-
-- Provide an option to run NLSR in dry-run mode
-
-- Add option to specify whether to use TCP or UDP face in nlsr.conf
-
-- Add option to specify arbitrary arguments to use in experiments
-
-- Include a single option to install Mini-NDN and all the dependencies
-
-**Bug fixes**:
-
-- Fix "key does not exist error" after NLSR starts
-
-- Update install.sh to call ldconfig after installing ChronoSync
-
-- Add hyperbolic coordinates to default topology
-
-**Misc changes**:
-
-- Add an experiment to test nlsrc
-
-- Create faces in NFD for each neighbor in NLSR
-
-- Update to latest ndn-cxx
-
-- Use /tmp/minindn folder as default work dir instead of /tmp
-
-Mini-NDN version 0.3.0 (changes since version 0.2.0)
-----------------------------------------------------
-
-Release date: March 3, 2017
-
-**New features**:
-
-- Mini-NDN cluster edition
-
-- New experiments for making NLSR testing easier
-
-**Bug fixes**:
-
-- Set site name correctly
-
-- Install missing certificates in NLSR security config
-
-- Fix quitting of NLSR due to key not found error
-
-**Misc changes**:
-
-- Removed nlsr.conf file, generate it within the code
-
-- Use argparse instead of deprecated optparse
-
-- Update security config section for NLSR
-
-- Change mininet prompt to mini-ndn
-
-- Set network name at one place
-
-- Update install.sh script to install openssl
-
-- Update install.sh script to install cryptopp from package instead of compiling from source
-
-- Update install.sh to clean build folder every time to get rid of removed files such as old experiments
-
-- Fix old code - use net.hosts instead of storing hosts in a variable
-
-- Use nfdc instead of deprecated nfd-status
-
-Mini-NDN version 0.2.0 (changes since version 0.1.1)
-----------------------------------------
-
-Release date: August 18, 2016
-
-**New features**:
-
-- Automatic security configuration for NLSR
-
-- Use /usr/local/etc/ndn/nfd.conf as default config file for NFD
-
-- Class to monitor /proc/$PID/stat file for PID
-
-- Mini-NDN exits gracefully on SIGINT and non-convergence
-
-- Faster Mini-NDN install script - does not do apt-get update everytime
-
-- NLSR is launched with explicit config file for easier process identification
-
-- Add and update more documentation
-
-**Bug fixes**:
-
-- NFD is killed correctly on exit
-
-- Best route strategy is set correctly
-
-Mini-NDN version 0.1.1 (changes since version 0.1.0)
-----------------------------------------
-
-Release date: November 4, 2015
-
-**New features**:
-
-- Use nfd.conf.sample from currently installed NFD
-
-- Add working directory option to allow execution environment outside of /tmp
-
-- Add results directory option to store experiment results after completion
-
-- Add support for switches in GUI and configuration file
-
-- Add failNode and recoverNode methods to Experiment class
-
-- Add most connected node (MCN) failure experiment
-
-- Add option to specify percentage of nodes pinged
-
-**Code changes**:
-
-- Refactor program options into container class
-
-- Remove unused "FIB Entries" option from NDN host options
-
-**Bug fixes**:
-
-- Abort start up if experiment name is invalid
-
-- Restart pings after recovery in failure experiment
-
-Mini-NDN version 0.1.0 (initial release)
-----------------------------------------
-
-Release date: July 15, 2015
-
-Mini-NDN is a lightweight networking emulation tool that enables testing, experimentation, and
-research on the NDN platform. Based on Mininet, Mini-NDN uses the NDN libraries, NFD, NLSR, and
-tools released by the [NDN project](http://named-data.net/codebase/platform/) to emulate
-an NDN network on a single system.
-
-**Included features**:
-
-- Run a complete NDN network on a single system
-
-- Automatic configuration of NLSR to provide a routable NDN network
-
-- Supports user created NDN applications
-
-- Create a topology using the included Mini-NDN Edit GUI application
-
-- Allows individual configuration of NFD and NLSR parameters for each node
-
-- Provides an experiment management framework for easy creation of custom networking experiments
-
-- Uses a simple topology file format to define hosts, links, and configuration values
-
-- Configure network link parameters including bandwidth, delay, and loss rate
-
-- Includes a pre-configured topology file to replicate the NDN testbed
diff --git a/docs/conf.py b/docs/conf.py
new file mode 100644
index 0000000..54591c1
--- /dev/null
+++ b/docs/conf.py
@@ -0,0 +1,56 @@
+# Configuration file for the Sphinx documentation builder.
+#
+# This file only contains a selection of the most common options. For a full
+# list see the documentation:
+# https://www.sphinx-doc.org/en/master/usage/configuration.html
+
+# -- Path setup --------------------------------------------------------------
+
+# If extensions (or modules to document with autodoc) are in another directory,
+# add these directories to sys.path here. If the directory is relative to the
+# documentation root, use os.path.abspath to make it absolute, like shown here.
+#
+# import os
+# import sys
+# sys.path.insert(0, os.path.abspath('.'))
+from datetime import datetime
+from minindn import __version__
+
+# -- Project information -----------------------------------------------------
+
+project = 'Mini-NDN'
+copyright = '2015-{}, Mini-NDN. This research is partially supported by NSF.'.format(datetime.now().year)
+author = 'Mini-NDN'
+
+# The full version, including alpha/beta/rc tags
+release = __version__
+version = __version__
+
+# -- General configuration ---------------------------------------------------
+
+# Add any Sphinx extension module names here, as strings. They can be
+# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
+# ones.
+extensions = [
+]
+
+# Add any paths that contain templates here, relative to this directory.
+templates_path = ['_templates']
+
+# List of patterns, relative to source directory, that match files and
+# directories to ignore when looking for source files.
+# This pattern also affects html_static_path and html_extra_path.
+exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store']
+
+
+# -- Options for HTML output -------------------------------------------------
+
+# The theme to use for HTML and HTML Help pages.  See the documentation for
+# a list of builtin themes.
+#
+html_theme = 'sphinx_rtd_theme'
+
+# Add any paths that contain custom static files (such as style sheets) here,
+# relative to this directory. They are copied after the builtin static files,
+# so a file named "default.css" will overwrite the builtin "default.css".
+html_static_path = ['_static']
\ No newline at end of file
diff --git a/docs/experiment.rst b/docs/experiment.rst
new file mode 100644
index 0000000..58cc303
--- /dev/null
+++ b/docs/experiment.rst
@@ -0,0 +1,235 @@
+Experiment
+==========
+
+Configuration
+-------------
+
+Mini-NDN uses a configuration file describing the topology and its parameters to setup a network.
+
+The [nodes] section:
+
+At the bare minimum, the node section describes the nodes present in the
+topology.
+
+::
+
+    [nodes]
+    a: key1=value1 key2=value2
+    b: key1=value1
+
+Any key and value passed here is accessible in Mini-NDN as:
+
+::
+
+    ndn = Minindn(...)
+    value = ndn.net.hosts[0].params['params'].get('key1', "defaultValue")
+
+One can specify log levels for each node's NFD and NLSR using this key-value system:
+
+::
+
+    [nodes]
+    a: nfd-log-level=DEBUG nlsr-log-level=DEBUG
+    b: nfd-log-level=INFO
+
+To specify a log level for certain modules of NFD, the following line can be added to `nfd.py`:
+
+::
+
+   node.cmd('infoedit -f {} -s log.Forwarder -v {}'.format(self.confFile, 'INFO'))
+
+This will turn on FORWARDER logging to INFO for all nodes.
+
+.. Todo: Add switch section
+
+The [links] section:
+
+The links section describes the links in the topology.
+
+::
+
+    e.g.)
+
+    [links]
+    a:b delay=10ms
+
+This would create a link between a and b. 'b:a' would also result in the
+same. The following parameters can be configured for a node:
+
+-  delay : Delay parameter is a required parameter which defines the
+   delay of the link (1-1000ms)
+
+-  bw : Bandwidth of a link (<1-1000> Mbps)
+
+-  loss : Percentage of packet loss (<1-100>)
+
+Example configuration file
+
+::
+
+    [nodes]
+    a:
+    b:
+    [links]
+    a:b delay=10ms bw=100
+
+See ``ndn_utils/topologies`` for more sample files
+
+Sample
+------
+
+Sample experiment may written as follows:
+
+.. code:: python
+
+    from mininet.log import setLogLevel, info
+
+    from minindn.minindn import Minindn
+    from minindn.util import MiniNDNCLI
+    from minindn.apps.appmanager import AppManager
+    from minindn.apps.nfd import Nfd
+    from minindn.apps.nlsr import Nlsr
+    from minindn.helpers.routing_helper import IPRoutingHelper
+
+    if __name__ == '__main__':
+        setLogLevel('info')
+
+        Minindn.cleanUp()
+        Minindn.verifyDependencies()
+
+        # Can pass a custom parser, custom topology, or any Mininet params here
+        ndn = Minindn()
+
+        ndn.start()
+
+        # IP reachability if needed
+        # IPRoutingHelper.calcAllRoutes(ndn.net)
+        # info("IP routes configured, start ping\n")
+        # ndn.net.pingAll()
+
+        # Start apps with AppManager which registers a clean up function with ndn
+        info('Starting NFD on nodes\n')
+        nfds = AppManager(ndn, ndn.net.hosts, Nfd)
+        info('Starting NLSR on nodes\n')
+        nlsrs = AppManager(ndn, ndn.net.hosts, Nlsr)
+
+        # or can not start NLSRs with some delay in between:
+        # nlsrs = AppManager(ndn, ndn.net.hosts, Nlsr)
+        # for host in ndn.net.hosts:
+        #     nlsrs.startOnNode(host)
+        #     time.sleep(30)
+
+        MiniNDNCLI(ndn.net)
+
+        # Calls the clean up functions registered via AppManager
+        ndn.stop()
+
+Users may look at how the NFD and NLSR applications are written as a sub class of Application
+in the ``minindn/apps`` folder. Or users may choose to directly run their application on nodes
+such as ndnpingserver is run in ``minindn/helpers/experiment.py``.
+
+Execution
+---------
+
+To run Mini-NDN with the default topology,
+``ndn_utils/topologies/default-topology.conf``, type:
+
+::
+
+    sudo python examples/minindn.py
+
+To run Mini-NDN with a topology file, provide the filename as the first
+argument:
+
+::
+
+    sudo python examples/minindn.py my-topology.conf
+
+After Mini-NDN is installed, users can run examples from anywhere with python directly as follows:
+
+::
+
+    sudo python /path/to/myexample.py
+
+The user no longer needs to create an experiment in the old Mini-NDN way, then install it to the system before executing it via the minindn binary. The new examples can be separate from the Mini-NDN folder if the core is not being modified.
+
+CLI Interface
+_____________
+
+During set up, the list of nodes in the network will be listed as they
+are initialized:
+
+::
+
+    *** Adding hosts:
+    a b c d
+
+After set up, the command-line interface (CLI) will display a prompt.
+
+::
+
+    mini-ndn>
+
+To interact with a node, first type the node's name and then the command
+to be executed:
+
+::
+
+    mini-ndn> a echo "Hello, world!"
+    Hello, world!
+
+To see the status of the forwarder on the node:
+
+::
+
+    mini-ndn> a nfdc status report
+
+To see the status of routing on the node:
+
+::
+
+    mini-ndn> a nlsrc status
+
+To exit Mini-NDN, type ``quit`` in the CLI or use ``ctrl + D``:
+
+::
+
+    mini-ndn> quit
+
+``Ctrl + C`` is used to quit an application run in the foreground of the command line.
+
+For a more in depth explanation of the CLI, please see the `Mininet
+Walkthrough <http://mininet.org/walkthrough/>`__.
+
+To run NDN commands from the outside the command line user can also open a new terminal
+and export the HOME folder of a node ``export HOME=/tmp/minindn/a && cd ~``
+
+Working Directory Structure
+---------------------------
+
+Currently Mini-NDN uses /tmp/minindn as the working directory if not
+specified otherwise by using the option --work-dir.
+
+Each node is given a HOME directory under /tmp/minindn/<node-name> where
+<node-name> is the name of the node specified in the [nodes] section of
+the conf file.
+
+NFD
+___
+
+- NFD conf file is stored at ``/tmp/minindn/<node-name>/nfd.conf``
+
+- NFD log file is stored at ``/tmp/minindn/<node-name>/log/nfd.log``
+
+- ``.ndn`` folder is stored at ``/tmp/minindn/<node-name>/.ndn``
+
+NLSR
+____
+
+- NLSR conf file is stored at ``/tmp/minindn/<node-name>/nlsr.conf``
+- NLSR log file is stored at ``/tmp/minindn/<node-name>/log/nlsr.log``
+
+When security is enabled, NLSR security certificates are stored in:
+``/tmp/minindn/<node-name>/security`` Note that no NLSR publishes the root
+certificate, Mini-NDN installs root.cert in security folder for each
+NLSR.
diff --git a/docs/faq.rst b/docs/faq.rst
new file mode 100644
index 0000000..991dd06
--- /dev/null
+++ b/docs/faq.rst
@@ -0,0 +1,48 @@
+FAQ
+=========
+
+* ``How does Mini-NDN work?``
+
+Mini-NDN's principles of operation most heavily rely on the underlying Mininet code it relies on.
+Mininet uses a combination of limited containerization via network namespaces (which give processes
+isolated interfaces and routing tables) and emulated ethernet connections via veth connections.
+In practical terms, Mini-NDN ensures that processes running on distinct nodes will run seperately
+and without interfering with each other.
+
+* ``How does Mini-NDN apply link loss/delay/etc.?``
+
+Mini-NDN relies on Mininet's code, which in turn uses the Linux tc utility on a stations' virtualized
+interfaces to apply configurations known as qdiscs to these links. Note that these will only be applied
+on egress packets from a station where it's applied.
+For more information on qdiscs and tc, view the information `here <http://wiki.linuxwall.info/doku.php/en%3aressources%3adossiers%3anetworking%3atraffic_control>`_.
+
+* ``Why use Mini-NDN rather than a simulator such as ndnSIM?``
+
+Mini-NDN is easier and faster to use because, rather than serving as a mathematical model of a network,
+it is instead running real NDN code on a real Linux kernel. This also means it's quite useful for testing code changes, as it can more accurately test the interaction of software componenents.
+
++--------------------------------+-----------------------------------------------------------+-----------------------------------------+
+| Criteria                       | Mini-NDN                                                  | ndnSIM                                  |
++--------------------------------+-----------------------------------------------------------+-----------------------------------------+
+| Based On                       | Mininet                                                   | ns-3                                    |
++--------------------------------+-----------------------------------------------------------+-----------------------------------------+
+| Language                       | Python                                                    | C++                                     |
++--------------------------------+-----------------------------------------------------------+-----------------------------------------+
+| Library/Forwarder/Applications | Use system binaries (free to use any compatible versions) | Integrated (fixed release version)      |
++--------------------------------+-----------------------------------------------------------+-----------------------------------------+
+| Application language           | C++ (ndn-cxx), CCL (ndn-cpp, PyNDN, ndn-js, jNDN)         | C++ (ndn-cxx)                           |
++--------------------------------+-----------------------------------------------------------+-----------------------------------------+
+| Simulation size                | Medium - Large (cluster edition in development)           | Large (can be parallelized using MPI)   |
++--------------------------------+-----------------------------------------------------------+-----------------------------------------+
+| Simulation time                | Real time                                                 | Quick (depending on size/memory)        |
++--------------------------------+-----------------------------------------------------------+-----------------------------------------+
+| Porting real applications      | Drop in                                                   | Changes required                        |
++--------------------------------+-----------------------------------------------------------+-----------------------------------------+
+| Interactivity                  | Can interact directly with NFD, NLSR or Apps              | Can show stats while running            |
++--------------------------------+-----------------------------------------------------------+-----------------------------------------+
+| Logs                           | May need to manually setup to collect                     | Available with tracer                   |
++--------------------------------+-----------------------------------------------------------+-----------------------------------------+
+| Post processing scripts        | Not available, users need to write their own              | Available to use to process the logs    |
++--------------------------------+-----------------------------------------------------------+-----------------------------------------+
+| Other                          | Not yet supported (Wifi in development)                   | WiFi, LTE, etc available from ns-3      |
++--------------------------------+-----------------------------------------------------------+-----------------------------------------+
diff --git a/docs/hackathon.rst b/docs/hackathon.rst
new file mode 100644
index 0000000..e51ec74
--- /dev/null
+++ b/docs/hackathon.rst
@@ -0,0 +1,9 @@
+Past NDN Hackathon projects
+===========================
+
+- 1st NDN Hackathon: `NFD integration test <http://ndncomm.github.io/mini-ndn/>`_.
+- 2st NDN Hackathon: `Mini-NDN metrics <https://github.com/2nd-ndn-hackathon/mini-ndn-metrics>`_.
+- 3rd NDN Hackathon: `Mini-NDN cluster <https://github.com/3rd-ndn-hackathon/mini-NDN-cluster>`_.
+- 4th NDN Hackathon: `Mini-NDN wifi <https://github.com/4th-ndn-hackathon/Mini-NDN-Wi-Fi>`_.
+- 7th NDN Hackathon: `Mini-NDN documentation <https://github.com/7th-ndn-hackathon/mini-ndn-documentation>`_.
+
diff --git a/docs/howtos.rst b/docs/howtos.rst
new file mode 100644
index 0000000..b95bde3
--- /dev/null
+++ b/docs/howtos.rst
@@ -0,0 +1,115 @@
+Howtos
+======
+
+Connect Mini-NDN nodes to an outside network
+---------------------------------------------
+
+Mini-NDN nodes can be connected to an outside network indirectly by
+running NFD on the local machine:
+
+::
+
+    (Mini-NDN node) ------ (NFD running on the host machine where Mini-NDN is running) ------- (External Network)
+
+Add a node in root namespace
+____________________________
+
+For this simple example, we can use a single node topology with node 'a'
+
+If we want node 'a' to connect to the host machine, we need to add a
+"root" node which has a link with node "a."
+
+Then the following code can be used:
+
+.. code:: python
+
+    topo = Topo()
+    root = topo.addHost('root', inNamespace=False)
+    a = topo.addHost('a')
+    topo.addLink(root, a, delay='10ms')
+
+    ndn = Minindn(topo=topo)
+
+    ...
+
+Configuration
+_____________
+
+Run Mini-NDN with the above code and issue ifconfig on the local
+machine to confirm the addition of the interface. You should be able to
+locate "root-eth0":
+
+::
+
+    root-eth0 Link encap:Ethernet  HWaddr 3e:eb:77:d2:6f:1f
+              inet addr:1.0.0.9  Bcast:1.0.0.11  Mask:255.255.255.252
+              inet6 addr: fe80::3ceb:77ff:fed2:6f1f/64 Scope:Link
+              UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
+              RX packets:34 errors:0 dropped:0 overruns:0 frame:0
+              TX packets:33 errors:0 dropped:0 overruns:0 carrier:0
+              collisions:0 txqueuelen:1000
+              RX bytes:2667 (2.6 KB)  TX bytes:2797 (2.7 KB)
+
+To make the IP address associated with this interface persistent, add
+the following line to /etc/network/interfaces and reboot your machine:
+
+::
+
+    iface root-eth0 inet manual
+
+Check connection
+________________
+
+After rebooting, run Mini-NDN and issue the following command:
+
+::
+
+    mini-ndn>net
+    a a-eth0:b-eth0 a-eth1:c-eth0 a-eth2:root-eth0
+
+Node "a" is connected to "root-eth0". Now issue "ifconfig a-eth2" on
+node "a":
+
+::
+
+    mini-ndn>a ifconfig a-eth2
+    a-eth2    Link encap:Ethernet  HWaddr fa:76:d4:86:d3:ba
+              inet addr:1.0.0.10  Bcast:1.0.0.11  Mask:255.255.255.252
+
+As learned from the previous step, the IP address of root-eth0 is
+1.0.0.9.
+
+::
+
+    mini-ndn>a ping 1.0.0.9
+    PING 1.0.0.9 (1.0.0.9) 56(84) bytes of data.
+    64 bytes from 1.0.0.9: icmp_seq=1 ttl=64 time=0.137 ms
+    64 bytes from 1.0.0.9: icmp_seq=2 ttl=64 time=0.123 ms
+
+The host machine will also be able to ping node "a":
+
+::
+
+    VirtualBox:~$ ping 1.0.0.10
+    PING 1.0.0.10 (1.0.0.10) 56(84) bytes of data.
+    64 bytes from 1.0.0.10: icmp_seq=1 ttl=64 time=0.086 ms
+
+Run NFD on local machine and register route
+___________________________________________
+
+Start NFD on the local machine by using:
+
+::
+
+    sudo nfd
+
+The "nfd-start" script cannot be used, since the script allows only one
+instance of NFD at a time. The NFD processes running on the Mini-NDN
+nodes will prevent the "nfd-start" script from working.
+
+Now, using "nfdc register", we can register a route from node "a" in
+Mini-NDN to the NFD process on the host machine and from the host
+machine to an external machine.
+
+Also, if the local machine has a public IP, Mini-NDN nodes can be
+reached via external machines.
diff --git a/docs/img/gui/cursor-tool.png b/docs/img/gui/cursor-tool.png
deleted file mode 100644
index 6d53812..0000000
--- a/docs/img/gui/cursor-tool.png
+++ /dev/null
Binary files differ
diff --git a/docs/img/gui/file-dropdown.png b/docs/img/gui/file-dropdown.png
deleted file mode 100644
index 9e0354c..0000000
--- a/docs/img/gui/file-dropdown.png
+++ /dev/null
Binary files differ
diff --git a/docs/img/gui/host-config.png b/docs/img/gui/host-config.png
deleted file mode 100644
index 5cd79a8..0000000
--- a/docs/img/gui/host-config.png
+++ /dev/null
Binary files differ
diff --git a/docs/img/gui/host-tool.png b/docs/img/gui/host-tool.png
deleted file mode 100644
index 4f54d53..0000000
--- a/docs/img/gui/host-tool.png
+++ /dev/null
Binary files differ
diff --git a/docs/img/gui/link-details.png b/docs/img/gui/link-details.png
deleted file mode 100644
index 99e2449..0000000
--- a/docs/img/gui/link-details.png
+++ /dev/null
Binary files differ
diff --git a/docs/img/gui/link-tool.png b/docs/img/gui/link-tool.png
deleted file mode 100644
index 7772ecf..0000000
--- a/docs/img/gui/link-tool.png
+++ /dev/null
Binary files differ
diff --git a/docs/img/gui/minindnedit.png b/docs/img/gui/minindnedit.png
deleted file mode 100644
index 0fe6879..0000000
--- a/docs/img/gui/minindnedit.png
+++ /dev/null
Binary files differ
diff --git a/docs/img/gui/switch-tool.png b/docs/img/gui/switch-tool.png
deleted file mode 100644
index 919b11d..0000000
--- a/docs/img/gui/switch-tool.png
+++ /dev/null
Binary files differ
diff --git a/docs/index.rst b/docs/index.rst
new file mode 100644
index 0000000..d7d8f01
--- /dev/null
+++ b/docs/index.rst
@@ -0,0 +1,29 @@
+.. Mini-NDN documentation master file, created by
+   sphinx-quickstart on Mon Sep 23 11:15:54 2019.
+   You can adapt this file completely to your liking, but it should at least
+   contain the root `toctree` directive.
+
+Mini-NDN: A Mininet-based NDN emulator
+======================================
+
+.. toctree::
+   :maxdepth: 2
+   :caption: Contents
+
+   introduction
+   install
+   experiment
+   howtos
+   release-notes
+   faq
+   hackathon
+   videos
+
+Helpful Links
+-------------
+
+* `NDN Website <http://named-data.net/>`_
+* `NDN Contributor's Guide <https://github.com/named-data/NFD/blob/master/CONTRIBUTING.md>`_
+* `Mininet Documentation <http://mininet.org/>`_
+* `Mini-NDN redmine <https://redmine.named-data.net/projects/mini-ndn>`_
+* `Mailing list <http://www.lists.cs.ucla.edu/mailman/listinfo/mini-ndn>`_
diff --git a/docs/install.rst b/docs/install.rst
new file mode 100644
index 0000000..391fdfa
--- /dev/null
+++ b/docs/install.rst
@@ -0,0 +1,174 @@
+Install
+=======
+
+Prerequisites
+-------------
+
+For this guide, you will need a laptop/desktop with a recent version of
+a Linux distro that is supported by Mininet. For this guide, the *Ubuntu 18.04 LTS* release was used.
+Some tweaks maybe required to Mini-NDN's install.sh file for other distros.
+Note that you'll need administrative privileges in order to download and install
+extra packages and also to execute **Mini-NDN**.
+
+Using Vagrantfile
+-----------------
+
+With Vagrant installed, simply do ``vagrant up`` which will bring up an Ubuntu 18.04 virtual machine
+and install Mini-NDN and all its dependencies on it. Please make sure to tweak the CPU core count
+(default 4 cores) and RAM (default 4GB) according to your needs before doing vagrant up. Mini-NDN
+can be found in /home/vagrant/mini-ndn which is a symlink to /vagrant if Vagrantfile was used from within mini-ndn cloned on the host. Otherwise it is an actual clone of mini-ndn.
+
+Using install.sh
+----------------
+
+Mini-NDN depends on Mininet and NDN software to be installed in the system.
+If you have all the dependencies (see sections below) **installed in the system** simply
+clone this repository and run:
+
+::
+
+    ./install.sh -i
+
+The ``-i`` option uses ``setup.py develop`` to point the system install
+to the current directory. So any changes made to the cloned ``mini-ndn``
+folder will be used without having to install it again to the system.
+If you do not need to modify the core of Mini-NDN, then setup.py install (or pip install .)
+can be used directly. See :doc:`experiment <experiment>` for more information on running.
+
+If you don't have the dependencies, the following command will
+install them from source along with Mini-NDN. The dependencies include
+Mininet, NDN core (ndn-cxx, NFD, Chronosync, PSync, NLSR), Infoedit,
+and NDN Common Client Libraries (CCL). If you do not wish to install
+the master versions of the NDN core or want to switch to specific versions,
+you can edit the install.sh with release tags/specific versions.
+
+.. _scaling-note:
+.. important::
+    If you wish to scale Mini-NDN experiments and do not have use for security extensions
+    in your emulations, you should apply the ndn-cxx patch given in the ``patches`` folder
+    using ``./install.sh -p`` before running the following commands. The ndn-cxx patch is
+    taken from ndnSIM which provides an in-memory dummy KeyChain to reduce CPU computations.
+    After these patches are applied sleep time after NFD, nfdc, NLSR, etc. is not required
+    making the startup **MUCH** faster and scaling of Mini-NDN **MUCH** better.
+
+::
+
+    ./install.sh -a
+
+This pulls the NDN software from Github to ``ndn-src`` folder under the project.
+
+.. note::
+    If any changes are made to ``ndn-src`` folder, please don't forgot to re-install
+    the sources to the system.
+
+To install without CCL, use:
+
+::
+
+    ./install.sh -mni
+
+See ``install.sh -h`` for detailed options.
+
+Installing Dependencies
+-----------------------
+
+Mininet
+_______
+
+Mini-NDN is based on Mininet. To install Mininet:
+
+::
+
+    git clone --depth 1 https://github.com/mininet/mininet.git
+
+After Mininet source is on your system, run the following command to
+install Mininet core dependencies and Open vSwitch:
+
+::
+
+    ./util/install.sh -nv
+
+To check if Mininet is working correctly, run this test:
+
+::
+
+    sudo mn --test pingall
+
+This will print out a series of statements that show the test setup and
+the results of the test. Look for ``Results:`` two-thirds of the way
+down where it will indicate the percentage of dropped packets. Your
+results should show "0% dropped (2/2 received)".
+
+NOTE: Mini-NDN, while providing a high level of emulation of hosts,
+requires programs to be installed onto your computer. It will not work
+if they are not installed. If you do not want NDN software installed
+onto your computer, you can use a virtual machine, which can be quite
+simply set up with the provided Vagrantfile.
+
+NDN dependencies
+________________
+
+Each node in Mini-NDN will run the official implementation of NDN
+installed on your system. The following dependencies are needed:
+
+Mini-NDN uses NFD, NLSR, and ndn-tools.
+
+- To install NFD: https://named-data.net/doc/NFD/current/INSTALL.html
+- To install NLSR: https://named-data.net/doc/NLSR/current/INSTALL.html
+- To install ndn-tools: https://github.com/named-data/ndn-tools
+
+.. warning::
+    Please do not try to install NDN software from both the source (GitHub) and PPA (apt).
+    It will not work in most cases! If you used ./install.sh -a in the past but now want
+    to use apt, please run ``sudo ./waf uninstall`` in all the NDN projects before proceeding
+    with apt. Similarly, remove from apt if switching to source.
+
+Please see the :ref:`scaling-note <scaling-note>` to learn about disabling
+security for better scalability.
+
+Note that all three of these can be installed from the Named Data PPA.
+Instructions for setting it up can be found in the NFD installation
+instructions. Note that PPA and installs from source **cannot** be
+mixed. You must completely remove PPA installs from the system if switching
+to source and vice-versa.
+
+For PPA installs, if you are using a custom nfd.conf file in an experiment, you should
+place it in /usr/local/etc/ndn/ rather than /etc/ndn/. This is to avoid
+a bug from the default configuration file for the PPA, which is
+incompatible with Mini-NDN.
+
+Infoedit
+________
+
+Infoedit is used to edit configuration files for NFD and NLSR.
+To install infoedit:
+
+::
+
+    git clone --depth 1 https://github.com/NDN-Routing/infoedit
+    cd infoedit
+    make
+    sudo make install
+
+Verification
+------------
+
+You can execute the following example to bring up the Mini-NDN command line
+with NFD and NLSR running on each node:
+
+::
+
+    sudo python examples/mnndn.py
+
+You can use these steps to run the sample pingall experiment:
+
+1. Issue the command: ``sudo python examples/nlsr/pingall.py``
+2. When the ``mini-ndn>`` CLI prompt appears, the experiment has
+   finished. On the Mini-NDN CLI, issue the command ``exit`` to exit the
+   experiment.
+3. Issue the command:
+   ``grep -c content /tmp/minindn/*/ping-data/*.txt``. Each file should
+   report a count of 50.
+4. Issue the command:
+   ``grep -c timeout /tmp/minindn/*/ping-data/*.txt``. Each file should
+   report a count of 0.
diff --git a/docs/introduction.rst b/docs/introduction.rst
new file mode 100644
index 0000000..fe19ad8
--- /dev/null
+++ b/docs/introduction.rst
@@ -0,0 +1,40 @@
+Introduction
+=================
+
+If you are new to the NDN community of software generally, read the
+`Contributor's Guide <https://github.com/named-data/NFD/blob/master/CONTRIBUTING.md>`_.
+
+What is Mini-NDN?
+-----------------
+
+Mini-NDN is a lightweight networking emulation tool that enables testing, experimentation, and
+research on the NDN platform. It was initially based on `Mini-CCNx <https://github.com/chesteve/mn-ccnx>`_ which was a fork of `Mininet <https://github.com/mininet/mininet>`_. Mini-NDN uses the NDN libraries, NFD, NLSR, and tools released by the `NDN project <http://named-data.net/codebase/platform/>`_ to emulate an NDN network on a single system.
+
+The first release of Mini-NDN is developed by members of the NSF-sponsored NDN project team.
+Mini-NDN is open to contribution from the public.
+
+.. image:: minindnnet.svg
+
+License
+_______
+
+Mini-NDN is open and free software licensed under the GPL 3.0 license. Mini-NDN is free to all
+users and developers. For more information about licensing details and limitations,
+please refer to COPYING.md.
+
+Feedback/Mailing List
+_____________________
+
+Bug reports and feedback are highly appreciated and can be made through our
+`Redmine site <http://redmine.named-data.net/projects/mini-ndn>`_ and the
+`mini-ndn mailing list <http://www.lists.cs.ucla.edu/mailman/listinfo/mini-ndn>`_.
+
+Video
+_____
+
+.. raw:: html
+
+ 	<div id="video-container" class="col-md-6 ">
+        <p>Mini-NDN (content maybe outdated)</p>
+         <iframe width="600" height="345" src="https://www.youtube.com/embed/UxHPqaUwefg" frameborder="0" allowfullscreen=""></iframe>
+    </div>
\ No newline at end of file
diff --git a/docs/minindnnet.svg b/docs/minindnnet.svg
new file mode 100644
index 0000000..0bc985a
--- /dev/null
+++ b/docs/minindnnet.svg
@@ -0,0 +1,126 @@
+<svg width="692" height="380" xmlns="http://www.w3.org/2000/svg">
+
+ <metadata id="metadata7">image/svg+xml</metadata>
+ <g>
+  <title>background</title>
+  <rect fill="none" id="canvas_background" height="382" width="694" y="-1" x="-1"/>
+ </g>
+ <g>
+  <title>Layer 1</title>
+  <g stroke="null" id="g3790">
+   <rect stroke="#000000" fill="#ffffff" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="4" id="rect2985" width="680.84682" height="350.443392" x="4.962804" y="5.773119" rx="0.324652"/>
+   <path stroke="#000000" fill="none" stroke-miterlimit="4" d="m4,175.828501l681.727736,1.357954" id="path3770"/>
+  </g>
+  <rect stroke="#000000" fill="#e3dedb" stroke-width="0.99538" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="4" stroke-dashoffset="0" rx="2.988095" y="176.111777" x="5.140841" height="180.524655" width="680.312936" id="rect3813"/>
+  <rect stroke="#000000" fill="#ffffff" stroke-width="1.602133" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="4" stroke-dasharray="4.80639973, 4.80639973" stroke-dashoffset="0" ry="8.582684" rx="7.476771" y="44.78072" x="55.492192" height="195.219243" width="148.366914" id="rect3764"/>
+  <rect stroke="#000000" fill="#ffffff" stroke-width="1.638042" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="4" stroke-dasharray="4.9141253, 4.9141253" stroke-dashoffset="0" id="rect3772" width="155.115252" height="195.190135" x="270.220868" y="46.153231" rx="7.816845" ry="8.581405"/>
+  <rect stroke="#000000" fill="#ffffff" stroke-width="1.602133" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="4" stroke-dasharray="4.80639973, 4.80639973" stroke-dashoffset="0" ry="8.582684" rx="7.476771" y="45.459696" x="489.86568" height="195.219243" width="148.366914" id="rect3774"/>
+  <text transform="matrix(1.0192718802397343,0,0,0.9115536073383187,-2.8313337784806336,-12.183034089773187) " font-size="16.005802px" font-style="normal" font-weight="normal" fill="#000000" font-family="Sans" id="text3776" y="392.709737" x="551.171304" xml:space="preserve">
+   <tspan stroke="null" font-weight="normal" y="398.194877" x="552.152396" id="tspan3778">Kernel Space</tspan>
+  </text>
+  <text transform="matrix(1.0524848569125234,0,0,0.8827879284304422,-52.30106698198968,-77.5201889102731) " font-size="14.180006px" font-style="normal" font-weight="normal" fill="#000000" font-family="Sans" xml:space="preserve" x="601.466013" y="108.524983" id="text3782">
+   <tspan stroke="null" font-weight="normal" id="tspan3784" x="602.416145" y="114.188857">User Space</tspan>
+  </text>
+  <path stroke="#000000" fill="none" stroke-width="1px" id="path3796" d="m488.56554,176.773764c1.782236,0.480113 1.527628,0.360085 1.527628,0.360085"/>
+  <rect stroke="#000000" fill="#ffffff" stroke-width="0.649807" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="4" stroke-dashoffset="0" rx="0.838801" y="202.851471" x="84.255138" height="26.434283" width="94.537572" id="rect3815"/>
+  <rect stroke="#000000" fill="#ffffff" stroke-width="0.792138" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="4" stroke-dashoffset="0" id="rect3817" width="131.339764" height="28.275376" x="282.077514" y="201.912133" rx="1.165334"/>
+  <path stroke="#000000" fill="none" stroke-width="0.865499px" id="path3823" d="m347.74739,201.086282l0,28.351986"/>
+  <text transform="matrix(0.7776670647796514,0,0,1.19475416519067,-52.30106724263901,-77.52018902784596) " font-size="12.623897px" font-style="normal" font-weight="normal" fill="#000000" font-family="Sans" xml:space="preserve" x="434.634023" y="248.956571" id="text3835">
+   <tspan stroke="null" font-size="15.579311px" font-weight="bold" font-family="Monospace" x="435.91992" y="253.141532" id="tspan3837">n2-eth0</tspan>
+  </text>
+  <text transform="matrix(0.9031157255303414,0,0,1.0287949657006918,-2.8313337284492768,-13.069358400177762) " font-size="13.163442px" font-style="normal" font-weight="normal" fill="#000000" font-family="Sans" xml:space="preserve" x="110.835751" y="225.210496" id="text3863">
+   <tspan stroke="null" font-size="16.245171px" font-weight="bold" font-family="Monospace" x="111.943029" y="230.070551" id="tspan3865">n1-eth0</tspan>
+  </text>
+  <path stroke="#000000" fill="none" stroke-width="0.992521px" id="path3887" d="m131.263954,229.882774l0,67.221296l176.555867,-0.960307l1.337531,-65.300682"/>
+  <rect stroke="#000000" fill="#ffffff" stroke-width="0.676601" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="4" stroke-dashoffset="0" id="rect3891" width="95.679394" height="28.317195" x="515.346399" y="202.85306" rx="0.848932"/>
+  <path stroke="#000000" fill="none" stroke-width="0.992521px" d="m383.833251,230.842992l0,67.221296l176.555867,-0.960307l1.337531,-65.300682" id="path3897"/>
+  <text transform="matrix(0.9778213687833123,0,0,0.9501950205038839,-52.301066914088324,-76.5370021990862) " font-size="16.459864px" font-style="normal" font-weight="normal" fill="#000000" font-family="Sans" xml:space="preserve" x="164.136782" y="409.888383" id="text3907">
+   <tspan stroke="null" id="tspan3915" x="165.159464" y="415.15046">isolated point to point link</tspan>
+  </text>
+  <text transform="matrix(0.9778213687833123,0,0,0.9501950205038839,-52.301066914088324,-76.5370021990862) " font-size="16.459864px" font-style="normal" font-weight="normal" fill="#000000" font-family="Sans" id="text3919" y="409.573373" x="429.378262" xml:space="preserve">
+   <tspan stroke="null" y="414.83545" x="430.400944" id="tspan3921">isolated point to point link</tspan>
+  </text>
+  <text transform="matrix(0.9778213687833123,0,0,0.9501950205038839,-52.301066914088324,-76.5370021990862) " font-size="16.459864px" font-style="normal" font-weight="normal" fill="#000000" font-family="Sans" id="text3923" y="428.078233" x="194.615642" xml:space="preserve">
+   <tspan stroke="null" font-weight="bold" y="433.34031" x="195.638324" id="tspan3925">(eg: 1Mbps, 10ms)</tspan>
+  </text>
+  <text transform="matrix(0.9778213687833123,0,0,0.9501950205038839,-52.301066914088324,-76.5370021990862) " font-size="16.459864px" font-style="normal" font-weight="normal" fill="#000000" font-family="Sans" id="text3927" y="409.888383" x="164.136782" xml:space="preserve">
+   <tspan stroke="null" y="415.15046" x="165.159464" id="tspan3929">Isolated point to point link</tspan>
+  </text>
+  <text transform="matrix(0.9778213687833123,0,0,0.9501950205038839,-52.301066914088324,-76.5370021990862) " font-size="16.459864px" font-style="normal" font-weight="normal" fill="#000000" font-family="Sans" xml:space="preserve" x="429.378262" y="409.573373" id="text3931">
+   <tspan stroke="null" id="tspan3933" x="430.400944" y="414.83545">Isolated point to point link</tspan>
+  </text>
+  <text transform="matrix(0.877827228897152,0,0,1.0584326041598764,-53.30106731891522,-78.4233265445007) " font-size="13.706223px" font-style="normal" font-weight="normal" fill="#000000" font-family="Sans" id="text3935" y="282.545218" x="661.482605" xml:space="preserve">
+   <tspan stroke="null" font-size="16.915024px" font-weight="bold" font-family="Monospace" id="tspan3937" y="287.269185" x="662.621781">n3-eth0</tspan>
+  </text>
+  <text transform="matrix(0.7776670647796514,0,0,1.19475416519067,-52.30106724263901,-77.52018902784596) " font-size="12.623897px" font-style="normal" font-weight="normal" fill="#000000" font-family="Sans" id="text3939" y="248.297391" x="520.751053" xml:space="preserve">
+   <tspan stroke="null" font-size="15.579311px" font-weight="bold" font-family="Monospace" id="tspan3941" y="252.482352" x="522.03695">n2-eth1</tspan>
+  </text>
+  <text transform="matrix(0.8745385143393076,0,0,1.0624128521471832,-2.8313338199928846,-14.069358364146652) " font-size="16.205975px" font-style="normal" font-weight="normal" fill="#000000" font-family="Sans" id="text3943" y="80.648481" x="141.655977" xml:space="preserve">
+   <tspan stroke="null" font-size="20px" font-weight="bold" font-family="Monospace" id="tspan3945" y="85.354749" x="142.799437">n1</tspan>
+  </text>
+  <text transform="matrix(0.8745385143393076,0,0,1.0624128521471832,-2.8313338199928846,-14.069358364146652) " font-size="16.205975px" font-style="normal" font-weight="normal" fill="#000000" font-family="Sans" xml:space="preserve" x="385.430517" y="78.840861" id="text3955">
+   <tspan stroke="null" font-size="20px" font-weight="bold" font-family="Monospace" x="386.573977" y="83.547129" id="tspan3957">n2</tspan>
+  </text>
+  <text transform="matrix(0.8745385143393076,0,0,1.0624128521471832,-2.8313338199928846,-14.069358364146652) " font-size="16.205975px" font-style="normal" font-weight="normal" fill="#000000" font-family="Sans" id="text3959" y="79.181231" x="632.310457" xml:space="preserve">
+   <tspan stroke="null" font-size="20px" font-weight="bold" font-family="Monospace" id="tspan3961" y="83.887499" x="633.453917">n3</tspan>
+  </text>
+  <text transform="matrix(0.9526376792163275,0,0,0.9753141035001042,-53.30106707952308,-77.42332645354041) " font-size="14.959435px" font-style="normal" font-weight="normal" fill="#000000" font-family="Sans" xml:space="preserve" x="125.371536" y="171.060971" id="text3967">
+   <tspan stroke="null" font-weight="normal" id="tspan3969" x="126.421253" y="176.187525">(NDN Container)</tspan>
+  </text>
+  <text transform="matrix(0.9526376792163275,0,0,0.9753141035001042,-53.30106707952308,-77.42332645354041) " font-size="14.959435px" font-style="normal" font-weight="normal" fill="#000000" font-family="Sans" id="text3979" y="170.076461" x="352.011746" xml:space="preserve">
+   <tspan stroke="null" font-weight="normal" y="175.203015" x="353.061463" id="tspan3981">(NDN Container)</tspan>
+  </text>
+  <text transform="matrix(0.9526376792163275,0,0,0.9753141035001042,-53.30106707952308,-77.42332645354041) " font-size="14.959435px" font-style="normal" font-weight="normal" fill="#000000" font-family="Sans" xml:space="preserve" x="580.077396" y="169.091931" id="text3983">
+   <tspan stroke="null" font-weight="normal" id="tspan3985" x="581.127113" y="174.218485">(NDN Container)</tspan>
+  </text>
+  <text transform="matrix(0.9526376792163275,0,0,0.9753141035001042,-53.30106707952308,-77.42332645354041) " font-size="14.959435px" font-style="normal" font-weight="normal" fill="#000000" font-family="Sans" id="text3987" y="193.704961" x="123.946116" xml:space="preserve">
+   <tspan stroke="null" font-weight="bold" y="198.831515" x="124.995833" id="tspan3989">NFD</tspan>
+  </text>
+  <text transform="matrix(0.9526376792163275,0,0,0.9753141035001042,-53.30106707952308,-77.42332645354041) " font-size="14.959435px" font-style="normal" font-weight="normal" fill="#000000" font-family="Sans" xml:space="preserve" x="140.791686" y="208.664491" id="text3991">
+   <tspan stroke="null" font-weight="normal" id="tspan3993" x="141.841403" y="213.791045">n1.sock</tspan>
+  </text>
+  <text transform="matrix(0.9526376792163275,0,0,0.9753141035001042,-53.30106707952308,-77.42332645354041) " font-size="14.959435px" font-style="normal" font-weight="normal" fill="#000000" font-family="Sans" xml:space="preserve" x="123.946116" y="225.286191" id="text3995">
+   <tspan stroke="null" font-weight="bold" id="tspan3997" x="124.995833" y="230.412745">NLSR</tspan>
+  </text>
+  <text transform="matrix(0.9526376792163275,0,0,0.9753141035001042,-53.30106707952308,-77.42332645354041) " font-size="14.959435px" font-style="normal" font-weight="normal" fill="#000000" font-family="Sans" id="text3999" y="240.245731" x="138.385176" xml:space="preserve">
+   <tspan stroke="null" font-weight="normal" y="245.372285" x="139.434893" id="tspan4001">%C1.Router/n1</tspan>
+  </text>
+  <text transform="matrix(0.9526376792163275,0,0,0.9753141035001042,-53.30106707952308,-77.42332645354041) " font-size="14.959435px" font-style="normal" font-weight="normal" fill="#000000" font-family="Sans" xml:space="preserve" x="352.011746" y="191.735901" id="text4003">
+   <tspan stroke="null" font-weight="bold" id="tspan4005" x="353.061463" y="196.862455">NFD</tspan>
+  </text>
+  <text transform="matrix(0.9526376792163275,0,0,0.9753141035001042,-53.30106707952308,-77.42332645354041) " font-size="14.959435px" font-style="normal" font-weight="normal" fill="#000000" font-family="Sans" id="text4007" y="206.695431" x="366.450896" xml:space="preserve">
+   <tspan stroke="null" font-weight="normal" y="211.821985" x="367.500613" id="tspan4009">n2.sock</tspan>
+  </text>
+  <text transform="matrix(0.9526376792163275,0,0,0.9753141035001042,-53.30106707952308,-77.42332645354041) " font-size="14.959435px" font-style="normal" font-weight="normal" fill="#000000" font-family="Sans" id="text4011" y="223.317141" x="352.011746" xml:space="preserve">
+   <tspan stroke="null" font-weight="bold" y="228.443695" x="353.061463" id="tspan4013">NLSR</tspan>
+  </text>
+  <text transform="matrix(0.9526376792163275,0,0,0.9753141035001042,-53.30106707952308,-77.42332645354041) " font-size="14.959435px" font-style="normal" font-weight="normal" fill="#000000" font-family="Sans" xml:space="preserve" x="366.450896" y="238.276671" id="text4015">
+   <tspan stroke="null" font-weight="normal" id="tspan4017" x="367.500613" y="243.403225">%C1.Router/n2</tspan>
+  </text>
+  <text transform="matrix(0.9526376792163275,0,0,0.9753141035001042,-53.30106707952308,-77.42332645354041) " font-size="14.959435px" font-style="normal" font-weight="normal" fill="#000000" font-family="Sans" id="text4019" y="190.751371" x="582.928226" xml:space="preserve">
+   <tspan stroke="null" font-weight="bold" y="195.877925" x="583.977943" id="tspan4021">NFD</tspan>
+  </text>
+  <text transform="matrix(0.9526376792163275,0,0,0.9753141035001042,-53.30106707952308,-77.42332645354041) " font-size="14.959435px" font-style="normal" font-weight="normal" fill="#000000" font-family="Sans" xml:space="preserve" x="599.773686" y="205.710911" id="text4023">
+   <tspan stroke="null" font-weight="normal" id="tspan4025" x="600.823403" y="210.837465">n3.sock</tspan>
+  </text>
+  <text transform="matrix(0.9526376792163275,0,0,0.9753141035001042,-53.30106707952308,-77.42332645354041) " font-size="14.959435px" font-style="normal" font-weight="normal" fill="#000000" font-family="Sans" xml:space="preserve" x="582.928226" y="222.332611" id="text4027">
+   <tspan stroke="null" font-weight="bold" id="tspan4029" x="583.977943" y="227.459165">NLSR</tspan>
+  </text>
+  <text transform="matrix(0.9526376792163275,0,0,0.9753141035001042,-53.30106707952308,-77.42332645354041) " font-size="14.959435px" font-style="normal" font-weight="normal" fill="#000000" font-family="Sans" id="text4031" y="237.292141" x="597.367186" xml:space="preserve">
+   <tspan stroke="null" font-weight="normal" y="242.418695" x="598.416903" id="tspan4033">%C1.Router/n3</tspan>
+  </text>
+  <text transform="matrix(0.9778213687833123,0,0,0.9501950205038839,-52.301066914088324,-76.5370021990862) " font-size="16.459864px" font-style="normal" font-weight="normal" fill="#000000" font-family="Sans" id="text4035" y="102.475413" x="322.701992" xml:space="preserve">
+   <tspan stroke="null" y="107.73749" x="323.724674" id="tspan4037">Isolated NDN nodes</tspan>
+  </text>
+  <text transform="matrix(0.935021961689298,0,0,0.9936889310882339,-2.9798247609055646,-12.086171479984756) " font-size="11.279781px" font-style="normal" font-weight="normal" fill="#000000" font-family="Sans" xml:space="preserve" x="75.549398" y="206.131281" id="text4039">
+   <tspan stroke="null" id="tspan4041" x="76.618892" y="211.163037">Private network space</tspan>
+  </text>
+  <text transform="matrix(0.935021961689298,0,0,0.9936889310882339,-2.9798247609055646,-12.086171479984756) " font-size="11.279781px" font-style="normal" font-weight="normal" fill="#000000" font-family="Sans" id="text4043" y="206.946991" x="308.474828" xml:space="preserve">
+   <tspan stroke="null" y="211.978747" x="309.544322" id="tspan4045">Private network space</tspan>
+  </text>
+  <text transform="matrix(0.935021961689298,0,0,0.9936889310882339,-2.9798247609055646,-12.086171479984756) " font-size="11.279781px" font-style="normal" font-weight="normal" fill="#000000" font-family="Sans" xml:space="preserve" x="540.174348" y="206.131281" id="text4047">
+   <tspan stroke="null" id="tspan4049" x="541.243842" y="211.163037">Private network space</tspan>
+  </text>
+  <text xml:space="preserve" text-anchor="start" font-family="Helvetica,Arial,sans-serif" font-size="15px" id="svg_1" y="377" x="203.5" stroke-width="null" stroke="null" fill="#333">Figure: Relationship between Minindn and Mininet</text>
+ </g>
+</svg>   
\ No newline at end of file
diff --git a/docs/release-notes.rst b/docs/release-notes.rst
new file mode 100644
index 0000000..79cbb0f
--- /dev/null
+++ b/docs/release-notes.rst
@@ -0,0 +1,186 @@
+Release Notes
+=============
+
+Mini-NDN version 0.4.0 (changes since version 0.3.0)
+----------------------------------------------------
+
+Release date: January 10, 2018
+
+**New features**:
+
+-  Use SIGQUIT to quit Mini-NDN, SIGINT to kill programs
+
+-  Use Infoedit to edit NFD and NLSR configuration files
+
+-  Use nlsr.conf installed in the system
+
+-  Provide a Vagrantfile to setup Mini-NDN and NDN
+
+-  Provide option to disable NLSR
+
+-  Provide an option to run NLSR in dry-run mode
+
+-  Add option to specify whether to use TCP or UDP face in nlsr.conf
+
+-  Add option to specify arbitrary arguments to use in experiments
+
+-  Include a single option to install Mini-NDN and all the dependencies
+
+**Bug fixes**:
+
+-  Fix "key does not exist error" after NLSR starts
+
+-  Update install.sh to call ldconfig after installing ChronoSync
+
+-  Add hyperbolic coordinates to default topology
+
+**Misc changes**:
+
+-  Add an experiment to test nlsrc
+
+-  Create faces in NFD for each neighbor in NLSR
+
+-  Update to latest ndn-cxx
+
+-  Use /tmp/minindn folder as default work dir instead of /tmp
+
+Mini-NDN version 0.3.0 (changes since version 0.2.0)
+----------------------------------------------------
+
+Release date: March 3, 2017
+
+**New features**:
+
+-  Mini-NDN cluster edition
+
+-  New experiments for making NLSR testing easier
+
+**Bug fixes**:
+
+-  Set site name correctly
+
+-  Install missing certificates in NLSR security config
+
+-  Fix quitting of NLSR due to key not found error
+
+**Misc changes**:
+
+-  Removed nlsr.conf file, generate it within the code
+
+-  Use argparse instead of deprecated optparse
+
+-  Update security config section for NLSR
+
+-  Change mininet prompt to mini-ndn
+
+-  Set network name at one place
+
+-  Update install.sh script to install openssl
+
+-  Update install.sh script to install cryptopp from package instead of
+   compiling from source
+
+-  Update install.sh to clean build folder every time to get rid of
+   removed files such as old experiments
+
+-  Fix old code - use net.hosts instead of storing hosts in a variable
+
+-  Use nfdc instead of deprecated nfd-status
+
+Mini-NDN version 0.2.0 (changes since version 0.1.1)
+----------------------------------------------------
+
+Release date: August 18, 2016
+
+**New features**:
+
+-  Automatic security configuration for NLSR
+
+-  Use /usr/local/etc/ndn/nfd.conf as default config file for NFD
+
+-  Class to monitor /proc/$PID/stat file for PID
+
+-  Mini-NDN exits gracefully on SIGINT and non-convergence
+
+-  Faster Mini-NDN install script - does not do apt-get update everytime
+
+-  NLSR is launched with explicit config file for easier process
+   identification
+
+-  Add and update more documentation
+
+**Bug fixes**:
+
+-  NFD is killed correctly on exit
+
+-  Best route strategy is set correctly
+
+Mini-NDN version 0.1.1 (changes since version 0.1.0)
+----------------------------------------------------
+
+Release date: November 4, 2015
+
+**New features**:
+
+-  Use nfd.conf.sample from currently installed NFD
+
+-  Add working directory option to allow execution environment outside
+   of /tmp
+
+-  Add results directory option to store experiment results after
+   completion
+
+-  Add support for switches in GUI and configuration file
+
+-  Add failNode and recoverNode methods to Experiment class
+
+-  Add most connected node (MCN) failure experiment
+
+-  Add option to specify percentage of nodes pinged
+
+**Code changes**:
+
+-  Refactor program options into container class
+
+-  Remove unused "FIB Entries" option from NDN host options
+
+**Bug fixes**:
+
+-  Abort start up if experiment name is invalid
+
+-  Restart pings after recovery in failure experiment
+
+Mini-NDN version 0.1.0 (initial release)
+----------------------------------------
+
+Release date: July 15, 2015
+
+Mini-NDN is a lightweight networking emulation tool that enables
+testing, experimentation, and research on the NDN platform. Based on
+Mininet, Mini-NDN uses the NDN libraries, NFD, NLSR, and tools released
+by the `NDN project <http://named-data.net/codebase/platform/>`__ to
+emulate an NDN network on a single system.
+
+**Included features**:
+
+-  Run a complete NDN network on a single system
+
+-  Automatic configuration of NLSR to provide a routable NDN network
+
+-  Supports user created NDN applications
+
+-  Create a topology using the included Mini-NDN Edit GUI application
+
+-  Allows individual configuration of NFD and NLSR parameters for each
+   node
+
+-  Provides an experiment management framework for easy creation of
+   custom networking experiments
+
+-  Uses a simple topology file format to define hosts, links, and
+   configuration values
+
+-  Configure network link parameters including bandwidth, delay, and
+   loss rate
+
+-  Includes a pre-configured topology file to replicate the NDN testbed
diff --git a/docs/videos.rst b/docs/videos.rst
new file mode 100644
index 0000000..b6b3a51
--- /dev/null
+++ b/docs/videos.rst
@@ -0,0 +1,18 @@
+Video Tutorials
+===============
+
+Maybe outdated since version 0.5.0.
+
+.. raw:: html
+
+    <div id="video-container" class="col-md-6 ">
+        <p>Mini-NDN Demo at ACM, 2017</p>
+         <iframe width="400" height="230" src="https://www.youtube.com/embed/xYRPHZe18o0" frameborder="0" allowfullscreen=""></iframe>
+    </div>
+
+.. raw:: html
+
+    <div id="video-container" class="col-md-6 "">
+        <p>Mini-NDN Overview </p>
+         <iframe width="400" height="230" src="https://www.youtube.com/embed/Da7t8yBWzv0" frameborder="0" allowfullscreen=""> 
+    </div>
diff --git a/examples/ip_rounting_experiment.py b/examples/ip_rounting_experiment.py
new file mode 100644
index 0000000..a6a82c0
--- /dev/null
+++ b/examples/ip_rounting_experiment.py
@@ -0,0 +1,53 @@
+# -*- Mode:python; c-file-style:"gnu"; indent-tabs-mode:nil -*- */
+#
+# Copyright (C) 2015-2019, The University of Memphis,
+#                          Arizona Board of Regents,
+#                          Regents of the University of California.
+#
+# This file is part of Mini-NDN.
+# See AUTHORS.md for a complete list of Mini-NDN authors and contributors.
+#
+# Mini-NDN is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Mini-NDN is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with Mini-NDN, e.g., in COPYING.md file.
+# If not, see <http://www.gnu.org/licenses/>.
+
+from mininet.log import setLogLevel, info
+
+from minindn.minindn import Minindn
+from minindn.apps.app_manager import AppManager
+from minindn.apps.nfd import Nfd
+from minindn.apps.nlsr import Nlsr
+from minindn.helpers.ip_routing_helper import IPRoutingHelper
+
+if __name__ == '__main__':
+    setLogLevel('info')
+
+    Minindn.cleanUp()
+    Minindn.verifyDependencies()
+
+    ndn = Minindn()
+
+    ndn.start()
+
+    info('Starting NFD on nodes\n')
+    nfds = AppManager(ndn, ndn.net.hosts, Nfd)
+    info('Starting NLSR on nodes\n')
+    nlsrs = AppManager(ndn, ndn.net.hosts, Nlsr)
+
+    # Calculate all routes for IP routing
+    IPRoutingHelper.calcAllRoutes(ndn.net)
+    info("IP routes configured, start ping\n")
+
+    ndn.net.pingAll()
+
+    ndn.net.stop()
diff --git a/ndn/experiments/ip_rounting_experiment.py b/examples/mnndn.py
similarity index 63%
rename from ndn/experiments/ip_rounting_experiment.py
rename to examples/mnndn.py
index 75c87ce..d989633 100644
--- a/ndn/experiments/ip_rounting_experiment.py
+++ b/examples/mnndn.py
@@ -21,27 +21,29 @@
 # along with Mini-NDN, e.g., in COPYING.md file.
 # If not, see <http://www.gnu.org/licenses/>.
 
-from ndn.experiments.experiment import Experiment
-from ndn.apps.routing_helper import IPRoutingHelper
+from mininet.log import setLogLevel, info
 
-from mininet.log import info
+from minindn.minindn import Minindn
+from minindn.util import MiniNDNCLI
+from minindn.apps.app_manager import AppManager
+from minindn.apps.nfd import Nfd
+from minindn.apps.nlsr import Nlsr
 
+if __name__ == '__main__':
+    setLogLevel('info')
 
-class IpRoutingExperiment(Experiment):
+    Minindn.cleanUp()
+    Minindn.verifyDependencies()
 
-    def __init__(self, args):
-        Experiment.__init__(self, args)
+    ndn = Minindn()
 
-    def setup(self):
-        pass
+    ndn.start()
 
-    def run(self):
+    info('Starting NFD on nodes\n')
+    nfds = AppManager(ndn, ndn.net.hosts, Nfd)
+    info('Starting NLSR on nodes\n')
+    nlsrs = AppManager(ndn, ndn.net.hosts, Nlsr)
 
-        # Calculate all routes for IP routing
-        IPRoutingHelper.calcAllRoutes(self.net)
-        info("IP routes configured, start ping\n")
+    MiniNDNCLI(ndn.net)
 
-        self.net.pingAll()
-
-
-Experiment.register("ip-routing", IpRoutingExperiment)
+    ndn.stop()
diff --git a/examples/nlsr/advertised-delayed-start.py b/examples/nlsr/advertised-delayed-start.py
new file mode 100644
index 0000000..8856fbd
--- /dev/null
+++ b/examples/nlsr/advertised-delayed-start.py
@@ -0,0 +1,79 @@
+# -*- Mode:python; c-file-style:"gnu"; indent-tabs-mode:nil -*- */
+#
+# Copyright (C) 2015-2019, The University of Memphis,
+#                          Arizona Board of Regents,
+#                          Regents of the University of California.
+#
+# This file is part of Mini-NDN.
+# See AUTHORS.md for a complete list of Mini-NDN authors and contributors.
+#
+# Mini-NDN is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Mini-NDN is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with Mini-NDN, e.g., in COPYING.md file.
+# If not, see <http://www.gnu.org/licenses/>.
+
+import time
+import sys
+
+from mininet.log import setLogLevel, info
+from mininet.topo import Topo
+
+from minindn.minindn import Minindn
+from minindn.apps.app_manager import AppManager
+from minindn.apps.nfd import Nfd
+from minindn.apps.nlsr import Nlsr
+
+from nlsr_common import getParser
+
+if __name__ == '__main__':
+    setLogLevel('info')
+
+    topo = Topo()
+    h1 = topo.addHost('h1')
+    h2 = topo.addHost('h2')
+    topo.addLink(h1, h2, delay='10ms')
+
+    ndn = Minindn(parser=getParser(), topo=topo)
+    args = ndn.args
+
+    ndn.start()
+
+    nfds = AppManager(ndn, ndn.net.hosts, Nfd)
+    nlsrs = AppManager(ndn, [], Nlsr)
+
+    host1 = ndn.net.hosts[0]
+    nlsrs.startOnNode(host1, security=args.security, faceType=args.faceType,
+                      nFaces=args.faces, routingType=args.routingType)
+
+
+    expectedTotalCount = 500
+    for i in range(0, expectedTotalCount):
+        host1.cmd('nlsrc advertise /long/name/to/exceed/max/packet/size/host1/{}'.format(i))
+
+    time.sleep(60)
+
+    host2 = ndn.net.hosts[1]
+    nlsrs.startOnNode(host2, security=args.security, faceType=args.faceType,
+                      nFaces=args.faces, routingType=args.routingType)
+
+    time.sleep(60)
+
+    advertiseCount = int(host2.cmd('nfdc fib | grep host1 | wc -l'))
+    info(advertiseCount)
+    if advertiseCount == expectedTotalCount:
+        info('\nSuccessfully advertised {} prefixes\n'.format(expectedTotalCount))
+    else:
+        info('\nAdvertising {} prefixes failed. Exiting...\n'.format(expectedTotalCount))
+        ndn.stop()
+        sys.exit(1)
+
+    ndn.stop()
diff --git a/examples/nlsr/delayed_start.py b/examples/nlsr/delayed_start.py
new file mode 100644
index 0000000..59b2f54
--- /dev/null
+++ b/examples/nlsr/delayed_start.py
@@ -0,0 +1,64 @@
+# -*- Mode:python; c-file-style:"gnu"; indent-tabs-mode:nil -*- */
+#
+# Copyright (C) 2015-2019, The University of Memphis,
+#                          Arizona Board of Regents,
+#                          Regents of the University of California.
+#
+# This file is part of Mini-NDN.
+# See AUTHORS.md for a complete list of Mini-NDN authors and contributors.
+#
+# Mini-NDN is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Mini-NDN is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with Mini-NDN, e.g., in COPYING.md file.
+# If not, see <http://www.gnu.org/licenses/>.
+
+import time
+
+from mininet.log import setLogLevel, info
+
+from minindn.minindn import Minindn
+from minindn.apps.app_manager import AppManager
+from minindn.apps.nfd import Nfd
+from minindn.apps.nlsr import Nlsr
+from minindn.helpers.experiment import Experiment
+
+from nlsr_common import getParser
+
+if __name__ == '__main__':
+    setLogLevel('info')
+
+    ndn = Minindn(parser=getParser())
+    args = ndn.args
+
+    ndn.start()
+
+    nfds = AppManager(ndn, ndn.net.hosts, Nfd)
+    nlsrs = AppManager(ndn, [], Nlsr)
+
+    i = 1
+    info('Starting NLSR on nodes\n')
+    for host in ndn.net.hosts:
+        nlsrs.startOnNode(host, security=args.security, sync=args.sync, faceType=args.faceType,
+                          nFaces=args.faces, routingType=args.routingType)
+
+        # Wait 1/2 minute between starting NLSRs
+        # Wait 1 hour before starting last NLSR
+        if i == len(ndn.net.hosts) - 1:
+            info('Sleeping 1 hour before starting last NLSR\n')
+            time.sleep(3600)
+        else:
+            time.sleep(30)
+        i += 1
+
+    Experiment.checkConvergence(ndn, ndn.net.hosts, args.ctime, quit=True)
+
+    ndn.stop()
diff --git a/examples/nlsr/mcn_failure.py b/examples/nlsr/mcn_failure.py
new file mode 100644
index 0000000..1bd0449
--- /dev/null
+++ b/examples/nlsr/mcn_failure.py
@@ -0,0 +1,90 @@
+# -*- Mode:python; c-file-style:"gnu"; indent-tabs-mode:nil -*- */
+#
+# Copyright (C) 2015-2019, The University of Memphis,
+#                          Arizona Board of Regents,
+#                          Regents of the University of California.
+#
+# This file is part of Mini-NDN.
+# See AUTHORS.md for a complete list of Mini-NDN authors and contributors.
+#
+# Mini-NDN is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Mini-NDN is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with Mini-NDN, e.g., in COPYING.md file.
+# If not, see <http://www.gnu.org/licenses/>.
+
+import time
+
+from mininet.log import setLogLevel, info
+
+from minindn.minindn import Minindn
+from minindn.util import MiniNDNCLI
+from minindn.apps.app_manager import AppManager
+from minindn.apps.nfd import Nfd
+from minindn.apps.nlsr import Nlsr
+from minindn.helpers.experiment import Experiment
+from minindn.helpers.nfdc import Nfdc
+from minindn.helpers.ndnpingclient import NDNPingClient
+
+from nlsr_common import getParser
+
+def mcnFailure(ndn, nfds, nlsrs, args):
+    Experiment.checkConvergence(ndn, ndn.net.hosts, args.ctime, quit=True)
+    if args.nPings != 0:
+        Experiment.setupPing(ndn.net.hosts, Nfdc.STRATEGY_BEST_ROUTE)
+        pingedDict = Experiment.startPctPings(ndn.net, args.nPings, args.pctTraffic)
+
+    PING_COLLECTION_TIME_BEFORE_FAILURE = 60
+    PING_COLLECTION_TIME_AFTER_RECOVERY = 120
+
+    time.sleep(PING_COLLECTION_TIME_BEFORE_FAILURE)
+
+    mcn = max(ndn.net.hosts, key=lambda host: len(host.intfNames()))
+
+    info('Bringing down node {}\n'.format(mcn.name))
+    nlsrs[mcn.name].stop()
+    nfds[mcn.name].stop()
+
+    time.sleep(args.ctime)
+
+    info('Bringing up node {}\n'.format(mcn.name))
+    nfds[mcn.name].start()
+    nlsrs[mcn.name].start()
+
+    # Restart pings
+    if args.nPings != 0:
+        Experiment.setupPing([mcn], Nfdc.STRATEGY_BEST_ROUTE)
+        for nodeToPing in pingedDict[mcn]:
+            NDNPingClient.ping(mcn, nodeToPing, PING_COLLECTION_TIME_AFTER_RECOVERY)
+
+        time.sleep(PING_COLLECTION_TIME_AFTER_RECOVERY)
+
+    Experiment.checkConvergence(ndn, ndn.net.hosts, args.ctime, quit=True)
+
+if __name__ == '__main__':
+    setLogLevel('info')
+
+    ndn = Minindn(parser=getParser())
+    args = ndn.args
+
+    ndn.start()
+
+    nfds = AppManager(ndn, ndn.net.hosts, Nfd)
+    nlsrs = AppManager(ndn, ndn.net.hosts, Nlsr, sync=args.sync,
+                       security=args.security, faceType=args.faceType,
+                       nFaces=args.faces, routingType=args.routingType)
+
+    mcnFailure(ndn, nfds, nlsrs, args)
+
+    if args.isCliEnabled:
+        MiniNDNCLI(ndn.net)
+
+    ndn.stop()
diff --git a/examples/nlsr/multiple_failure.py b/examples/nlsr/multiple_failure.py
new file mode 100644
index 0000000..48d1afd
--- /dev/null
+++ b/examples/nlsr/multiple_failure.py
@@ -0,0 +1,109 @@
+# -*- Mode:python; c-file-style:"gnu"; indent-tabs-mode:nil -*- */
+#
+# Copyright (C) 2015-2019, The University of Memphis,
+#                          Arizona Board of Regents,
+#                          Regents of the University of California.
+#
+# This file is part of Mini-NDN.
+# See AUTHORS.md for a complete list of Mini-NDN authors and contributors.
+#
+# Mini-NDN is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Mini-NDN is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with Mini-NDN, e.g., in COPYING.md file.
+# If not, see <http://www.gnu.org/licenses/>.
+
+import time
+
+from mininet.log import setLogLevel, info
+
+from minindn.minindn import Minindn
+from minindn.util import MiniNDNCLI
+from minindn.apps.app_manager import AppManager
+from minindn.apps.nfd import Nfd
+from minindn.apps.nlsr import Nlsr
+from minindn.helpers.experiment import Experiment
+from minindn.helpers.nfdc import Nfdc
+from minindn.helpers.ndnpingclient import NDNPingClient
+
+from nlsr_common import getParser
+
+def multipleFailure(ndn, nfds, nlsrs, args):
+
+    Experiment.checkConvergence(ndn, ndn.net.hosts, args.ctime, quit=True)
+    Experiment.setupPing(ndn.net.hosts, Nfdc.STRATEGY_BEST_ROUTE)
+
+    PING_COLLECTION_TIME_BEFORE_FAILURE = 60
+    FAILURE_INTERVAL = 60
+    RECOVERY_INTERVAL = 60
+
+    # This is the number of pings required to make it through the full experiment
+    nInitialPings = (PING_COLLECTION_TIME_BEFORE_FAILURE +
+                     len(ndn.net.hosts) * (FAILURE_INTERVAL + RECOVERY_INTERVAL))
+    print('Scheduling with {} initial pings'.format(nInitialPings))
+
+    pingedDict = Experiment.startPctPings(ndn.net, nInitialPings, args.pctTraffic)
+    time.sleep(PING_COLLECTION_TIME_BEFORE_FAILURE)
+
+    nNodesRemainingToFail = len(ndn.net.hosts)
+
+    for host in ndn.net.hosts:
+        # Fail the node
+        info('Bringing down node {}\n'.format(host.name))
+        nlsrs[host.name].stop()
+        nfds[host.name].stop()
+
+        # Stay in failure state for FAILURE_INTERVAL seconds
+        time.sleep(FAILURE_INTERVAL)
+
+        # Bring the node back up
+        start_time = time.time()
+        info('Bringing up node {}\n'.format(host.name))
+        nfds[host.name].start()
+        nlsrs[host.name].start()
+        Experiment.setupPing([host], Nfdc.STRATEGY_BEST_ROUTE)
+
+        recovery_time = int(time.time() - start_time)
+
+        # Number of pings required to reach the end of the test
+        nNodesRemainingToFail -= 1
+        nPings = ((RECOVERY_INTERVAL - recovery_time) +
+                  nNodesRemainingToFail * (FAILURE_INTERVAL + RECOVERY_INTERVAL))
+
+        info('Scheduling with {} remaining pings\n'.format(nPings))
+
+        # Restart pings
+        for nodeToPing in pingedDict[host]:
+            NDNPingClient.ping(host, nodeToPing, nPings)
+
+        time.sleep(RECOVERY_INTERVAL - recovery_time)
+
+    #Experiment.checkConvergence(ndn, ndn.net.hosts, args.ctime, quit=True)
+
+if __name__ == '__main__':
+    setLogLevel('info')
+
+    ndn = Minindn(parser=getParser())
+    args = ndn.args
+
+    ndn.start()
+
+    nfds = AppManager(ndn, ndn.net.hosts, Nfd)
+    nlsrs = AppManager(ndn, ndn.net.hosts, Nlsr, sync=args.sync,
+                       security=args.security, faceType=args.faceType,
+                       nFaces=args.faces, routingType=args.routingType)
+
+    multipleFailure(ndn, nfds, nlsrs, args)
+
+    if args.isCliEnabled:
+        MiniNDNCLI(ndn.net)
+
+    ndn.stop()
diff --git a/examples/nlsr/nlsr_common.py b/examples/nlsr/nlsr_common.py
new file mode 100644
index 0000000..5ec7f7c
--- /dev/null
+++ b/examples/nlsr/nlsr_common.py
@@ -0,0 +1,57 @@
+# -*- Mode:python; c-file-style:"gnu"; indent-tabs-mode:nil -*- */
+#
+# Copyright (C) 2015-2019, The University of Memphis,
+#                          Arizona Board of Regents,
+#                          Regents of the University of California.
+#
+# This file is part of Mini-NDN.
+# See AUTHORS.md for a complete list of Mini-NDN authors and contributors.
+#
+# Mini-NDN is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Mini-NDN is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with Mini-NDN, e.g., in COPYING.md file.
+# If not, see <http://www.gnu.org/licenses/>.
+
+import argparse
+
+def getParser():
+    parser = argparse.ArgumentParser()
+    parser.add_argument('--ctime', type=int, default=60,
+                        help='Specify convergence time for the topology (Default: 60 seconds)')
+
+    parser.add_argument('--faces', type=int, default=3,
+                        help='Specify number of max faces per prefix for NLSR 0-60')
+
+    parser.add_argument('--routing', dest='routingType', default='link-state',
+                        choices=['link-state', 'hr', 'dry'],
+                        help='''Choose routing type, dry = link-state is used
+                                but hr is calculated for comparision.''')
+
+    parser.add_argument('--sync', dest='sync', default='psync',
+                        choices=['chronosync', 'psync'],
+                        help='choose the sync protocol to be used by NLSR.')
+
+    parser.add_argument('--security', action='store_true', dest='security',
+                        help='Enables NLSR security')
+
+    parser.add_argument('--face-type', dest='faceType', default='udp', choices=['udp', 'tcp'])
+
+    parser.add_argument('--no-cli', action='store_false', dest='isCliEnabled',
+                        help='Run experiments and exit without showing the command line interface')
+
+    parser.add_argument('--pct-traffic', dest='pctTraffic', type=float, default=1.0,
+                        help='Specify the percentage of nodes each node should ping')
+
+    parser.add_argument('--nPings', type=int, default=300,
+                        help='Number of pings to perform between each node in the experiment')
+
+    return parser
diff --git a/examples/nlsr/pingall.py b/examples/nlsr/pingall.py
new file mode 100644
index 0000000..002ace0
--- /dev/null
+++ b/examples/nlsr/pingall.py
@@ -0,0 +1,63 @@
+# -*- Mode:python; c-file-style:"gnu"; indent-tabs-mode:nil -*- */
+#
+# Copyright (C) 2015-2019, The University of Memphis,
+#                          Arizona Board of Regents,
+#                          Regents of the University of California.
+#
+# This file is part of Mini-NDN.
+# See AUTHORS.md for a complete list of Mini-NDN authors and contributors.
+#
+# Mini-NDN is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Mini-NDN is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with Mini-NDN, e.g., in COPYING.md file.
+# If not, see <http://www.gnu.org/licenses/>.
+
+import time
+
+from mininet.log import setLogLevel
+
+from minindn.minindn import Minindn
+from minindn.util import MiniNDNCLI
+from minindn.apps.app_manager import AppManager
+from minindn.apps.nfd import Nfd
+from minindn.apps.nlsr import Nlsr
+from minindn.helpers.experiment import Experiment
+from minindn.helpers.nfdc import Nfdc
+
+from nlsr_common import getParser
+
+if __name__ == '__main__':
+    setLogLevel('info')
+
+    ndn = Minindn(parser=getParser())
+    args = ndn.args
+
+    ndn.start()
+
+    nfds = AppManager(ndn, ndn.net.hosts, Nfd)
+    nlsrs = AppManager(ndn, ndn.net.hosts, Nlsr, sync=args.sync,
+                       security=args.security, faceType=args.faceType,
+                       nFaces=args.faces, routingType=args.routingType,
+                       logLevel='ndn.*=TRACE:nlsr.*=TRACE')
+
+    Experiment.checkConvergence(ndn, ndn.net.hosts, args.ctime, quit=False)
+
+    if args.nPings != 0:
+        Experiment.setupPing(ndn.net.hosts, Nfdc.STRATEGY_BEST_ROUTE)
+        Experiment.startPctPings(ndn.net, args.nPings, args.pctTraffic)
+
+        time.sleep(args.nPings + 10)
+
+    if args.isCliEnabled:
+        MiniNDNCLI(ndn.net)
+
+    ndn.stop()
diff --git a/examples/nlsr/prefix_propogation.py b/examples/nlsr/prefix_propogation.py
new file mode 100644
index 0000000..7ea027c
--- /dev/null
+++ b/examples/nlsr/prefix_propogation.py
@@ -0,0 +1,84 @@
+# -*- Mode:python; c-file-style:"gnu"; indent-tabs-mode:nil -*- */
+#
+# Copyright (C) 2015-2019, The University of Memphis,
+#                          Arizona Board of Regents,
+#                          Regents of the University of California.
+#
+# This file is part of Mini-NDN.
+# See AUTHORS.md for a complete list of Mini-NDN authors and contributors.
+#
+# Mini-NDN is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Mini-NDN is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with Mini-NDN, e.g., in COPYING.md file.
+# If not, see <http://www.gnu.org/licenses/>.
+
+import time
+
+from mininet.log import setLogLevel, info
+
+from minindn.minindn import Minindn
+from minindn.util import MiniNDNCLI
+from minindn.apps.app_manager import AppManager
+from minindn.apps.nfd import Nfd
+from minindn.apps.nlsr import Nlsr
+from minindn.helpers.experiment import Experiment
+
+from nlsr_common import getParser
+
+if __name__ == '__main__':
+    setLogLevel('info')
+
+    ndn = Minindn(parser=getParser())
+    args = ndn.args
+
+    ndn.start()
+
+    nfds = AppManager(ndn, ndn.net.hosts, Nfd)
+    nlsrs = AppManager(ndn, ndn.net.hosts, Nlsr, sync=args.sync,
+                       security=args.security, faceType=args.faceType,
+                       nFaces=args.faces, routingType=args.routingType)
+
+    Experiment.checkConvergence(ndn, ndn.net.hosts, args.ctime, quit=True)
+
+    firstNode = ndn.net.hosts[0]
+
+    if args.security:
+        firstNode.cmd('ndnsec-set-default /ndn/{}-site/%C1.Operator/op'.format(firstNode.name))
+
+    info('Testing advertise\n')
+    firstNode.cmd('nlsrc advertise /testPrefix')
+    time.sleep(30)
+
+    for host in ndn.net.hosts:
+        if host.name != firstNode.name:
+            if (int(host.cmd('nfdc fib | grep testPrefix | wc -l')) != 1 or
+               int(host.cmd('nlsrc status | grep testPrefix | wc -l')) != 1):
+                info('Advertise test failed\n')
+                ndn.stop()
+                sys.exit(1)
+
+    info('Testing withdraw\n')
+    firstNode.cmd('nlsrc withdraw /testPrefix')
+    time.sleep(30)
+
+    for host in ndn.net.hosts:
+        if host.name != firstNode.name:
+            if (int(host.cmd('nfdc fib | grep testPrefix | wc -l')) != 0 or
+               int(host.cmd('nlsrc status | grep testPrefix | wc -l')) != 0):
+                info('Withdraw test failed\n')
+                ndn.stop()
+                sys.exit(1)
+
+    if args.isCliEnabled:
+        MiniNDNCLI(ndn.net)
+
+    ndn.stop()
diff --git a/examples/psync/full_sync.py b/examples/psync/full_sync.py
new file mode 100644
index 0000000..1a54a73
--- /dev/null
+++ b/examples/psync/full_sync.py
@@ -0,0 +1,81 @@
+# -*- Mode:python; c-file-style:"gnu"; indent-tabs-mode:nil -*- */
+#
+# Copyright (C) 2015-2019, The University of Memphis,
+#                          Arizona Board of Regents,
+#                          Regents of the University of California.
+#
+# This file is part of Mini-NDN.
+# See AUTHORS.md for a complete list of Mini-NDN authors and contributors.
+#
+# Mini-NDN is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Mini-NDN is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with Mini-NDN, e.g., in COPYING.md file.
+# If not, see <http://www.gnu.org/licenses/>.
+
+import time
+import sys
+
+from mininet.log import setLogLevel, info
+
+from minindn.minindn import Minindn
+from minindn.apps.app_manager import AppManager
+from minindn.apps.nfd import Nfd
+from minindn.helpers.nfdc import Nfdc
+
+def registerRouteToAllNeighbors(ndn, host, syncPrefix):
+    for node in ndn.net.hosts:
+        for neighbor in node.connectionsTo(host):
+            ip = node.IP(neighbor[0])
+            Nfdc.createFace(host, ip)
+            Nfdc.registerRoute(host, syncPrefix, ip)
+
+if __name__ == '__main__':
+    setLogLevel('info')
+
+    ndn = Minindn()
+    args = ndn.args
+
+    ndn.start()
+
+    nfds = AppManager(ndn, ndn.net.hosts, Nfd)
+
+    syncPrefix = "/sync"
+    numUserPrefixesPerNode = 2
+    maxUpdatesPerUserPrefixPerNode = 3
+
+    for host in ndn.net.hosts:
+        Nfdc.setStrategy(host, syncPrefix, Nfdc.STRATEGY_MULTICAST)
+        registerRouteToAllNeighbors(ndn, host, syncPrefix)
+
+    info('Starting psync-full-sync on all the nodes\n')
+    for host in ndn.net.hosts:
+        host.cmd('export NDN_LOG=examples.FullSyncApp=INFO')
+        host.cmd('psync-full-sync {} {} {} {} &> psync.logs &'
+                 .format(syncPrefix, host.name, numUserPrefixesPerNode,
+                         maxUpdatesPerUserPrefixPerNode))
+
+    info('Sleeping 5 minutes for convergence\n')
+    # Estimated time for 4 node default topology
+    time.sleep(300)
+
+    totalUpdates = int(host.cmd('grep -r Update {}/*/psync.logs | wc -l'
+                                .format(args.workDir)))
+
+    expectedUpdates = (maxUpdatesPerUserPrefixPerNode *
+                      len(ndn.net.hosts) * (len(ndn.net.hosts) - 1) * numUserPrefixesPerNode)
+
+    if totalUpdates == expectedUpdates:
+        info('PSync full sync has successfully converged.\n')
+    else:
+        info('PSync full sync convergence was not successful. Exiting...\n')
+        ndn.stop()
+        sys.exit(1)
diff --git a/examples/psync/partial_sync.py b/examples/psync/partial_sync.py
new file mode 100644
index 0000000..13f2761
--- /dev/null
+++ b/examples/psync/partial_sync.py
@@ -0,0 +1,67 @@
+# -*- Mode:python; c-file-style:"gnu"; indent-tabs-mode:nil -*- */
+#
+# Copyright (C) 2015-2019, The University of Memphis,
+#                          Arizona Board of Regents,
+#                          Regents of the University of California.
+#
+# This file is part of Mini-NDN.
+# See AUTHORS.md for a complete list of Mini-NDN authors and contributors.
+#
+# Mini-NDN is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Mini-NDN is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with Mini-NDN, e.g., in COPYING.md file.
+# If not, see <http://www.gnu.org/licenses/>.
+
+import time
+import sys
+
+from mininet.log import setLogLevel, info
+from mininet.topo import Topo
+
+from minindn.minindn import Minindn
+from minindn.apps.app_manager import AppManager
+from minindn.apps.nfd import Nfd
+
+if __name__ == '__main__':
+    setLogLevel('info')
+
+    topo = Topo()
+    topo.addHost('h1')
+
+    ndn = Minindn(topo=topo)
+    args = ndn.args
+
+    ndn.start()
+
+    nfds = AppManager(ndn, ndn.net.hosts, Nfd)
+
+    host1 = ndn.net.hosts[0]
+    host1.cmd('export NDN_LOG=examples.PartialSyncProducerApp=INFO')
+    host1.cmd('psync-producer /sync /{} 10 1 &> producer.log &'.format(host1.name))
+    time.sleep(1)
+
+    host1.cmd('export NDN_LOG=examples.PartialSyncConsumerApp=INFO:$NDN_LOG')
+    host1.cmd('psync-consumer /sync 5 &> consumer.log &')
+
+    info('Sleeping 90 seconds for convergence\n')
+    time.sleep(90)
+
+    consumerSubs = int(host1.cmd('cat consumer.log | grep -c Subscribing'))
+    consumerUpdates = int(host1.cmd('cat consumer.log | grep -c Update'))
+    producerPublish = int(host1.cmd('cat producer.log | grep -c Publish'))
+
+    if consumerSubs == 5 and consumerUpdates == 5 and producerPublish == 10:
+        info('PSync partial sync has successfully converged.\n')
+    else:
+        info('PSync partial sync convergence was not successful. Exiting...\n')
+        ndn.stop()
+        sys.exit(1)
diff --git a/examples/static_routing_experiment.py b/examples/static_routing_experiment.py
new file mode 100644
index 0000000..a5d9b04
--- /dev/null
+++ b/examples/static_routing_experiment.py
@@ -0,0 +1,100 @@
+# -*- Mode:python; c-file-style:"gnu"; indent-tabs-mode:nil -*- */
+#
+# Copyright (C) 2015-2019, The University of Memphis,
+#                          Arizona Board of Regents,
+#                          Regents of the University of California.
+#
+# This file is part of Mini-NDN.
+# See AUTHORS.md for a complete list of Mini-NDN authors and contributors.
+#
+# Mini-NDN is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Mini-NDN is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with Mini-NDN, e.g., in COPYING.md file.
+# If not, see <http://www.gnu.org/licenses/>.
+
+import argparse
+import sys
+
+from mininet.log import setLogLevel, info
+from mininet.topo import Topo
+
+from minindn.minindn import Minindn
+from minindn.util import MiniNDNCLI
+from minindn.apps.app_manager import AppManager
+from minindn.apps.nfd import Nfd
+from minindn.helpers.ndn_routing_helper import NdnRoutingHelper
+
+if __name__ == '__main__':
+    setLogLevel('info')
+
+    Minindn.cleanUp()
+    Minindn.verifyDependencies()
+
+    parser = argparse.ArgumentParser()
+    parser.add_argument('--face-type', dest='faceType', default='udp', choices=['udp', 'tcp'])
+    parser.add_argument('--routing', dest='routingType', default='link-state',
+                         choices=['link-state', 'hr', 'dry'],
+                         help='''Choose routing type, dry = link-state is used
+                                 but hr is calculated for comparision.''')
+
+    '''
+    Experiment run with default topology, test cases won't work with other topologies
+                          # With calculateNPossibleRoutes,
+            10            # routing = hr, N = All, from A, 3 routes needs to added to NFD.
+        a +++++++ b       # a - b  -- cost 10
+        +         +       # a - c  -- cost 10
+     10 +         + 10    # a - d  -- cost 20
+        +         +       # Same goes for B being a source.
+        c         d       #
+    '''
+    topo = Topo()
+    a = topo.addHost('a')
+    b = topo.addHost('b')
+    c = topo.addHost('c')
+    d = topo.addHost('d')
+    topo.addLink(a, b, delay='10ms')
+    topo.addLink(a, c, delay='10ms')
+    topo.addLink(b, d, delay='10ms')
+
+    ndn = Minindn(parser=parser, topo=topo)
+
+    ndn.start()
+
+    info('Starting NFD on nodes\n')
+    nfds = AppManager(ndn, ndn.net.hosts, Nfd)
+
+    info('Adding static routes to NFD\n')
+    grh = NdnRoutingHelper(ndn.net, ndn.args.faceType, ndn.args.routingType)
+    # For all host, pass ndn.net.hosts or a list, [ndn.net['a'], ..] or [ndn.net.hosts[0],.]
+    grh.addOrigin([ndn.net['a']], ["/abc"])
+    grh.calculateNPossibleRoutes()
+
+    '''
+    prefix "/abc" is advertise from node A, it should be reachable from all other nodes.
+    '''
+    routesFromA = ndn.net['a'].cmd("nfdc route | grep -v '/localhost/nfd'")
+    if '/ndn/b-site/b' not in routesFromA or \
+       '/ndn/c-site/c' not in routesFromA or \
+       '/ndn/d-site/d' not in routesFromA:
+        info("Route addition failed\n")
+
+    routesToPrefix = ndn.net['b'].cmd("nfdc fib | grep '/abc'")
+    if '/abc' not in routesToPrefix:
+        info("Missing route to advertised prefix, Route addition failed\n")
+        ndn.net.stop()
+        sys.exit(1)
+
+    info('Route addition to NFD completed\n')
+
+    MiniNDNCLI(ndn.net)
+
+    ndn.stop()
diff --git a/install.sh b/install.sh
index c5fe190..ab99329 100755
--- a/install.sh
+++ b/install.sh
@@ -1,7 +1,7 @@
 #!/bin/bash
 # -*- Mode:bash; c-file-style:"gnu"; indent-tabs-mode:nil -*- */
 #
-# Copyright (C) 2015-2018, The University of Memphis,
+# Copyright (C) 2015-2019, The University of Memphis,
 #                          Arizona Board of Regents,
 #                          Regents of the University of California.
 #
@@ -85,93 +85,85 @@
     fi
 fi
 
-function forwarder {
-    if [[ $cxx != true ]]; then
-        ndncxx
-        cxx="true"
-    fi
+NDN_SRC="ndn-src"
 
-    if [[ $DIST == Ubuntu || $DIST == Debian ]]; then
-        $install libpcap-dev pkg-config
-    fi
+NDN_GITHUB="https://github.com/named-data"
 
-    if [[ $DIST == Fedora ]]; then
-        $install libpcap-devel
-    fi
+NDN_CXX_VERSION="master"
+NFD_VERSION="master"
+PSYNC_VERSION="master"
+CHRONOSYNC_VERSION="master"
+NLSR_VERSION="master"
+NDN_TOOLS_VERSION="master"
 
-    git clone --depth 1 https://github.com/named-data/NFD
-    cd NFD
-    ./waf configure --without-websocket
-    ./waf
-    sudo ./waf install
-    cd ../
+if [ $SUDO_USER ]; then
+    REAL_USER=$SUDO_USER
+else
+    REAL_USER=$(whoami)
+fi
+
+function patchDummy {
+    git -C $NDN_SRC/ndn-cxx apply $(pwd)/patches/ndn-cxx-dummy-keychain-from-ndnsim.patch
+    if [[ "$?" -ne 0 ]]; then
+        echo "Patch might already be applied"
+    fi
 }
 
-function routing {
-    if [[ $cxx != true ]]; then
-        ndncxx
-        cxx="true"
+function ndn_install {
+    mkdir -p $NDN_SRC
+    name=$1
+    version=$2
+    wafOptions=$3
+
+    if [[ $version == "master" ]]; then
+        if [[ -d "$NDN_SRC/$name" ]]; then
+            pushd $NDN_SRC/$name
+            git checkout master
+        else
+            git clone --depth 1 $NDN_GITHUB/$name $NDN_SRC/$name
+            pushd $NDN_SRC/$name
+        fi
+    else
+        if [[ -d $NDN_SRC/$name ]]; then
+            pushd $NDN_SRC/$name
+            if [[ $(git rev-parse --is-shallow-repository) == "true" ]]; then
+                git fetch --unshallow
+                git fetch --all
+            fi
+        else
+            git clone $NDN_GITHUB/$name $NDN_SRC/$name
+            pushd $NDN_SRC/$name
+        fi
+        git checkout $version -b version-$version || git checkout version-$version
     fi
 
-    git clone --depth 1 https://github.com/named-data/PSync
-    cd PSync
-    ./waf configure
-    ./waf
-    sudo ./waf install
-    sudo ldconfig
-    cd ../
-
-    git clone --depth 1 https://github.com/named-data/ChronoSync
-    cd ChronoSync
-    ./waf configure
-    ./waf
-    sudo ./waf install
-    sudo ldconfig
-    cd ../
-
-    git clone --depth 1 https://github.com/named-data/NLSR
-    cd NLSR
-    ./waf configure
-    ./waf
-    sudo ./waf install
-    cd ../
+    # User must use the same python version as root to use ./waf outside of this script
+    sudo -E -u $REAL_USER ./waf configure $wafOptions
+    sudo -E -u $REAL_USER ./waf && sudo ./waf install && sudo ldconfig
+    popd
 }
 
-function ndncxx {
+function ndn {
     if [[ updated != true ]]; then
         $update
         updated="true"
     fi
 
     if [[ $DIST == Ubuntu || $DIST == Debian ]]; then
-        $install git libsqlite3-dev libboost-all-dev make g++ libssl-dev
+        $install git libsqlite3-dev libboost-all-dev make g++ libssl-dev libpcap-dev pkg-config python-pip
     fi
 
     if [[ $DIST == Fedora ]]; then
-        $install gcc-c++ sqlite-devel boost-devel openssl-devel
+        $install gcc-c++ sqlite-devel boost-devel openssl-devel libpcap-devel python-pip
     fi
 
-    git clone --depth 1 https://github.com/named-data/ndn-cxx
-    cd ndn-cxx
-    ./waf configure
-    ./waf
-    sudo ./waf install
-    sudo ldconfig
-    cd ../
-}
-
-function tools {
-    if [[ $cxx != true ]]; then
-        ndncxx
-        cxx="true"
-    fi
-
-    git clone --depth 1 https://github.com/named-data/ndn-tools
-    cd ndn-tools
-    ./waf configure
-    ./waf
-    sudo ./waf install
-    cd ../
+    ndn_install ndn-cxx $NDN_CXX_VERSION
+    ndn_install NFD $NFD_VERSION --without-websocket
+    ndn_install PSync $PSYNC_VERSION --with-examples
+    ndn_install ChronoSync $CHRONOSYNC_VERSION
+    ndn_install NLSR $NLSR_VERSION
+    ndn_install ndn-tools $NDN_TOOLS_VERSION
+    infoedit
 }
 
 function mininet {
@@ -185,16 +177,17 @@
     fi
 
     git clone --depth 1 https://github.com/mininet/mininet
-    cd mininet
-    sudo ./util/install.sh -fnv
-    cd ../
+    pushd mininet
+    sudo ./util/install.sh -nv
+    popd
 }
 
 function infoedit {
-    git clone --depth 1 https://github.com/NDN-Routing/infoedit.git
-    cd infoedit
+    git clone --depth 1 https://github.com/NDN-Routing/infoedit.git $NDN_SRC/infoedit
+    pushd $NDN_SRC/infoedit
+    rm infoedit
     sudo make install
-    cd ../
+    popd
 }
 
 function minindn {
@@ -220,7 +213,7 @@
     sudo cp topologies/minindn.ucla.conf "$install_dir"
     sudo cp topologies/minindn.testbed.conf "$install_dir"
     sudo cp topologies/current-testbed.conf "$install_dir"
-    sudo python setup.py clean --all install
+    sudo python setup.py develop
 }
 
 function ndn_cpp {
@@ -238,14 +231,14 @@
         return
     fi
 
-    git clone --depth 1 https://github.com/named-data/ndn-cpp
-    cd ndn-cpp
+    git clone --depth 1 $NDN_GITHUB/ndn-cpp $NDN_SRC/ndn-cpp
+    pushd $NDN_SRC/ndn-cpp
     ./configure
     proc=$(nproc)
     make -j$proc
     sudo make install
     sudo ldconfig
-    cd ..
+    popd
 }
 
 function pyNDN {
@@ -264,13 +257,13 @@
     fi
 
     sudo pip install cryptography trollius protobuf pytest mock
-    git clone --depth 1 https://github.com/named-data/PyNDN2
-    cd PyNDN2
+    git clone --depth 1 $NDN_GITHUB/PyNDN2 $NDN_SRC/PyNDN2
+    pushd $NDN_SRC/PyNDN2
     # Update the user's PYTHONPATH.
     echo "export PYTHONPATH=\$PYTHONPATH:`pwd`/python" >> ~/.bashrc
     # Also update root's PYTHONPATH in case of running under sudo.
     echo "export PYTHONPATH=\$PYTHONPATH:`pwd`/python" | sudo tee -a /root/.bashrc > /dev/null
-    cd ..
+    popd
 }
 
 function ndn_js {
@@ -291,7 +284,7 @@
     sudo ln -fs /usr/bin/nodejs /usr/bin/node
     sudo npm install -g mocha
     sudo npm install rsa-keygen sqlite3
-    git clone --depth 1 https://github.com/named-data/ndn-js
+    git clone --depth 1 $NDN_GITHUB/ndn-js $NDN_SRC/ndn-js
 }
 
 function jNDN {
@@ -309,30 +302,10 @@
         return
     fi
 
-    git clone --depth 1 https://github.com/named-data/jndn
-    cd jndn
+    git clone --depth 1 $NDN_GITHUB/jndn $NDN_SRC/jndn
+    pushd $NDN_SRC/jndn
     mvn install
-    cd ..
-}
-
-function argcomplete {
-    if [[ $SHELL == "/bin/bash" ]]; then
-        $install bash-completion
-        $install python-argcomplete
-        if ! grep -q 'eval "$(register-python-argcomplete minindn)"' ~/.bashrc; then
-            echo 'eval "$(register-python-argcomplete minindn)"' >> ~/.bashrc
-        fi
-        source ~/.bashrc
-    elif [[ $SHELL == "/bin/zsh" ]] || [[ $SHELL == "/usr/bin/zsh" ]]; then
-        $install bash-completion
-        $install python-argcomplete
-        if ! grep -z -q 'autoload bashcompinit\sbashcompinit\seval "$(register-python-argcomplete minindn)"' ~/.zshrc; then
-            echo -e 'autoload bashcompinit\nbashcompinit\neval "$(register-python-argcomplete minindn)"' >> ~/.zshrc
-        fi
-        source ~/.zshrc
-    else
-        echo "Skipping argomplete install..."
-    fi
+    popd
 }
 
 function commonClientLibraries {
@@ -342,47 +315,56 @@
     jNDN
 }
 
+function buildDocumentation {
+    sphinxInstalled=$(pip show sphinx | wc -l)
+    sphinxRtdInstalled=$(pip show sphinx_rtd_theme | wc -l)
+    if [[ $sphinxInstalled -eq "0" ]]; then
+        pip install sphinx
+    fi
+
+    if [[ $sphinxRtdInstalled -eq "0" ]]; then
+        pip install sphinx_rtd_theme
+    fi
+    cd docs
+    make clean
+    make html
+}
+
 function usage {
     printf '\nUsage: %s [-a]\n\n' $(basename $0) >&2
 
     printf 'options:\n' >&2
     printf -- ' -a: install all the required dependencies\n' >&2
-    printf -- ' -b: install autocomplete for Bash and Zsh users\n' >&2
-    printf -- ' -e: install infoedit\n' >&2
-    printf -- ' -f: install NFD\n' >&2
+    printf -- ' -c: install Common Client Libraries\n' >&2
+    printf -- ' -d: build documentation\n' >&2
+    printf -- ' -h: print this (H)elp message\n' >&2
     printf -- ' -i: install mini-ndn\n' >&2
     printf -- ' -m: install mininet and dependencies\n' >&2
-    printf -- ' -r: install NLSR\n' >&2
-    printf -- ' -t: install tools\n' >&2
-    printf -- ' -c: install Common Client Libraries\n' >&2
+    printf -- ' -n: install NDN dependencies of mini-ndn including infoedit\n' >&2
+    printf -- ' -p: patch ndn-cxx with dummy key chain\n' >&2
     exit 2
 }
 
 if [[ $# -eq 0 ]]; then
     usage
 else
-    while getopts 'abemfrtic' OPTION
+    while getopts 'acdhimnp' OPTION
     do
         case $OPTION in
         a)
-        forwarder
-        minindn
+        ndn
         mininet
-        routing
-        tools
-        infoedit
-        argcomplete
+        minindn
         commonClientLibraries
         break
         ;;
-        b)    argcomplete;;
-        e)    infoedit;;
-        f)    forwarder;;
+        c)    commonClientLibraries;;
+        d)    buildDocumentation;;
+        h)    usage;;
         i)    minindn;;
         m)    mininet;;
-        r)    routing;;
-        t)    tools;;
-        c)    commonClientLibraries;;
+        n)    ndn;;
+        p)    patchDummy;;
         ?)    usage;;
         esac
     done
diff --git a/minindn/__init__.py b/minindn/__init__.py
new file mode 100644
index 0000000..2b8877c
--- /dev/null
+++ b/minindn/__init__.py
@@ -0,0 +1 @@
+__version__ = '0.5.0'
diff --git a/ndn/apps/__init__.py b/minindn/apps/__init__.py
similarity index 100%
rename from ndn/apps/__init__.py
rename to minindn/apps/__init__.py
diff --git a/minindn/apps/app_manager.py b/minindn/apps/app_manager.py
new file mode 100644
index 0000000..bfe2ddc
--- /dev/null
+++ b/minindn/apps/app_manager.py
@@ -0,0 +1,56 @@
+# -*- Mode:python; c-file-style:"gnu"; indent-tabs-mode:nil -*- */
+#
+# Copyright (C) 2015-2019, The University of Memphis,
+#                          Arizona Board of Regents,
+#                          Regents of the University of California.
+#
+# This file is part of Mini-NDN.
+# See AUTHORS.md for a complete list of Mini-NDN authors and contributors.
+#
+# Mini-NDN is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Mini-NDN is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with Mini-NDN, e.g., in COPYING.md file.
+# If not, see <http://www.gnu.org/licenses/>.
+
+from mininet.node import Node
+
+class AppManager(object):
+    def __init__(self, minindn, hosts, cls, **appParams):
+        self.cls = cls
+        self.apps = []
+        for host in hosts:
+            # Don't run NDN apps on switches
+            if isinstance(host, Node):
+                self.startOnNode(host, **appParams)
+
+        minindn.cleanups.append(self.cleanup)
+
+    def startOnNode(self, host, **appParams):
+        app = self.cls(host, **appParams)
+        app.start()
+        self.apps.append(app)
+
+    def cleanup(self):
+        for app in self.apps:
+            app.stop()
+
+    def __getitem__(self, nodeName):
+        for app in self.apps:
+            if app.node.name == nodeName:
+                return app
+        return None
+
+    def __iter__(self):
+        return self.apps.__iter__()
+
+    def __next__(self):
+        return self.apps.__next__()
diff --git a/minindn/apps/application.py b/minindn/apps/application.py
new file mode 100644
index 0000000..6970cb7
--- /dev/null
+++ b/minindn/apps/application.py
@@ -0,0 +1,48 @@
+# -*- Mode:python; c-file-style:"gnu"; indent-tabs-mode:nil -*- */
+#
+# Copyright (C) 2015-2019, The University of Memphis,
+#                          Arizona Board of Regents,
+#                          Regents of the University of California.
+#
+# This file is part of Mini-NDN.
+# See AUTHORS.md for a complete list of Mini-NDN authors and contributors.
+#
+# Mini-NDN is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Mini-NDN is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with Mini-NDN, e.g., in COPYING.md file.
+# If not, see <http://www.gnu.org/licenses/>.
+
+from minindn.util import getPopen
+
+class Application(object):
+    def __init__(self, node):
+        self.node = node
+        self.process = None
+        self.logfile = None
+        self.homeDir = self.node.params['params']['homeDir']
+
+        # Make directory for log file
+        self.logDir = '{}/log'.format(self.homeDir)
+        self.node.cmd('mkdir -p {}'.format(self.logDir))
+
+    def start(self, command, logfile, envDict=None):
+        if self.process is None:
+            self.logfile = open('{}/{}'.format(self.logDir, logfile), 'w')
+            self.process = getPopen(self.node, command.split(), envDict,
+                                    stdout=self.logfile, stderr=self.logfile)
+
+    def stop(self):
+        if self.process is not None:
+            self.process.kill()
+            self.process = None
+        if self.logfile is not None:
+            self.logfile.close()
diff --git a/minindn/apps/nfd.py b/minindn/apps/nfd.py
new file mode 100644
index 0000000..7130526
--- /dev/null
+++ b/minindn/apps/nfd.py
@@ -0,0 +1,74 @@
+# -*- Mode:python; c-file-style:"gnu"; indent-tabs-mode:nil -*- */
+#
+# Copyright (C) 2015-2019, The University of Memphis,
+#                          Arizona Board of Regents,
+#                          Regents of the University of California.
+#
+# This file is part of Mini-NDN.
+# See AUTHORS.md for a complete list of Mini-NDN authors and contributors.
+#
+# Mini-NDN is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Mini-NDN is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with Mini-NDN, e.g., in COPYING.md file.
+# If not, see <http://www.gnu.org/licenses/>.
+
+from minindn.apps.application import Application
+from minindn.util import copyExistentFile
+from minindn.minindn import Minindn
+
+class Nfd(Application):
+
+    def __init__(self, node, logLevel='NONE', csSize=65536,
+                 csPolicy='lru', csUnsolicitedPolicy='drop-all'):
+        Application.__init__(self, node)
+
+        self.logLevel = node.params['params'].get('nfd-log-level', logLevel)
+
+        self.confFile = '{}/nfd.conf'.format(self.homeDir)
+        self.logFile = 'nfd.log'
+        self.sockFile = '/var/run/{}.sock'.format(node.name)
+        self.ndnFolder = '{}/.ndn'.format(self.homeDir)
+        self.clientConf = '{}/client.conf'.format(self.ndnFolder)
+
+        # Copy nfd.conf file from /usr/local/etc/ndn or /etc/ndn to the node's home directory
+        # Use nfd.conf as default configuration for NFD, else use the sample
+        possibleConfPaths = ['/usr/local/etc/ndn/nfd.conf.sample', '/usr/local/etc/ndn/nfd.conf',
+                             '/etc/ndn/nfd.conf.sample', '/etc/ndn/nfd.conf']
+        copyExistentFile(node, possibleConfPaths, self.confFile)
+
+        # Set log level
+        node.cmd('infoedit -f {} -s log.default_level -v {}'.format(self.confFile, self.logLevel))
+        # Open the conf file and change socket file name
+        node.cmd('infoedit -f {} -s face_system.unix.path -v {}'.format(self.confFile, self.sockFile))
+
+        # Set CS parameters
+        node.cmd('infoedit -f {} -s tables.cs_max_packets -v {}'.format(self.confFile, csSize))
+        node.cmd('infoedit -f {} -s tables.cs_policy -v {}'.format(self.confFile, csPolicy))
+        node.cmd('infoedit -f {} -s tables.cs_unsolicited_policy -v {}'.format(self.confFile, csUnsolicitedPolicy))
+
+        # Make NDN folder
+        node.cmd('mkdir -p {}'.format(self.ndnFolder))
+
+        # Copy client configuration to host
+        possibleClientConfPaths = ['/usr/local/etc/ndn/client.conf.sample', '/etc/ndn/client.conf.sample']
+        copyExistentFile(node, possibleClientConfPaths, self.clientConf)
+
+        # Change the unix socket
+        node.cmd('sudo sed -i "s|nfd.sock|{}.sock|g" {}'.format(node.name, self.clientConf))
+
+        if not Minindn.ndnSecurityDisabled:
+            # Generate key and install cert for /localhost/operator to be used by NFD
+            node.cmd('ndnsec-keygen /localhost/operator | ndnsec-install-cert -')
+
+    def start(self):
+        Application.start(self, 'nfd --config {}'.format(self.confFile), logfile=self.logFile)
+        Minindn.sleep(2)
diff --git a/minindn/apps/nlsr.py b/minindn/apps/nlsr.py
new file mode 100644
index 0000000..36ef971
--- /dev/null
+++ b/minindn/apps/nlsr.py
@@ -0,0 +1,258 @@
+# -*- Mode:python; c-file-style:"gnu"; indent-tabs-mode:nil -*- */
+#
+# Copyright (C) 2015-2019, The University of Memphis,
+#                          Arizona Board of Regents,
+#                          Regents of the University of California.
+#
+# This file is part of Mini-NDN.
+# See AUTHORS.md for a complete list of Mini-NDN authors and contributors.
+#
+# Mini-NDN is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Mini-NDN is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with Mini-NDN, e.g., in COPYING.md file.
+# If not, see <http://www.gnu.org/licenses/>.
+
+import shutil
+import os, sys
+
+from mininet.clean import sh
+from mininet.examples.cluster import RemoteMixin
+from mininet.log import warn
+from mininet.node import Switch
+
+from minindn.apps.application import Application
+from minindn.util import scp, copyExistentFile
+from minindn.helpers.nfdc import Nfdc
+from minindn.minindn import Minindn
+
+class Nlsr(Application):
+    ROUTING_LINK_STATE = 'link-state'
+    ROUTING_HYPERBOLIC = 'hr'
+    ROUTING_DRY_RUN = 'dry'
+    SYNC_PSYNC = 'psync'
+    SYNC_CHRONOSYNC = 'chronosync'
+
+    def __init__(self, node, logLevel='NONE', security=False, sync=SYNC_PSYNC,
+                 faceType='udp', nFaces=3, routingType=ROUTING_LINK_STATE):
+        Application.__init__(self, node)
+
+        self.network = '/ndn/'
+        self.node = node
+        self.parameters = self.node.params['params']
+
+        if self.parameters.get('nlsr-log-level', None) != None:
+            logLevel = self.parameters.get('nlsr-log-level')
+
+        if logLevel in ['NONE', 'WARN', 'INFO', 'DEBUG', 'TRACE']:
+            self.envDict = {'NDN_LOG': 'nlsr.*={}'.format(logLevel)}
+        else:
+            self.envDict = {'NDN_LOG': logLevel}
+
+        self.logFile = 'nlsr.log'
+        self.routerName = '/{}C1.Router/cs/{}'.format('%', node.name)
+        self.confFile = '{}/nlsr.conf'.format(self.homeDir)
+        self.security = security
+        self.sync = sync
+        self.faceType = faceType
+        self.infocmd = 'infoedit -f nlsr.conf'
+
+        self.parameters = self.node.params['params']
+
+        self.nFaces = nFaces
+        if routingType == Nlsr.ROUTING_HYPERBOLIC:
+            self.hyperbolicState = 'on'
+        elif routingType == Nlsr.ROUTING_DRY_RUN:
+            self.hyperbolicState = 'dry-run'
+        else:
+            self.hyperbolicState = 'off'
+        self.hyperRadius = self.parameters.get('radius', 0.0)
+        self.hyperAngle = self.parameters.get('angle', 0.0)
+
+        if ((self.hyperbolicState == 'on' or self.hyperbolicState == 'dry-run') and
+            (self.hyperRadius == 0.0 or self.hyperAngle == 0.0)):
+            warn('Hyperbolic coordinates in topology file are either missing or misconfigured.')
+            warn('Check that each node has one radius value and one or two angle value(s).')
+            sys.exit(1)
+
+        self.neighborIPs = []
+        possibleConfPaths = ['/usr/local/etc/ndn/nlsr.conf.sample', '/etc/ndn/nlsr.conf.sample']
+        copyExistentFile(node, possibleConfPaths, '{}/nlsr.conf'.format(self.homeDir))
+
+        self.createConfigFile()
+
+        if security and not Minindn.ndnSecurityDisabled:
+            self.createKeysAndCertificates()
+
+    def start(self):
+        self.createFaces()
+        Application.start(self, 'nlsr -f {}'.format(self.confFile), self.logFile, self.envDict)
+        Minindn.sleep(1)
+
+    def createFaces(self):
+        for ip in self.neighborIPs:
+            Nfdc.createFace(self.node, ip, self.faceType, isPermanent=True)
+
+    @staticmethod
+    def createKey(host, name, outputFile):
+        host.cmd('ndnsec-keygen {} > {}'.format(name, outputFile))
+
+    @staticmethod
+    def createCertificate(host, signer, keyFile, outputFile):
+        host.cmd('ndnsec-certgen -s {} -r {} > {}'.format(signer, keyFile, outputFile))
+
+    def createKeysAndCertificates(self):
+        securityDir = '{}/security'.format(self.parameters['workDir'])
+
+        if not os.path.exists(securityDir):
+            os.mkdir(securityDir)
+
+        rootName = self.network
+        rootCertFile = '{}/root.cert'.format(securityDir)
+        if not os.path.isfile(rootCertFile):
+            # Create root certificate
+            sh('ndnsec-keygen {}'.format(rootName)) # Installs a self-signed cert into the system
+            sh('ndnsec-cert-dump -i {} > {}'.format(rootName, rootCertFile))
+
+        # Create necessary certificates for each site
+        nodeSecurityFolder = '{}/security'.format(self.homeDir)
+
+        self.node.cmd('mkdir -p {}'.format(nodeSecurityFolder))
+
+        # Create temp folders for remote nodes on this machine (localhost) to store site.key file
+        # from RemoteNodes
+        if not os.path.exists(nodeSecurityFolder) and \
+            isinstance(self.node, RemoteMixin) and self.node.isRemote:
+            os.makedirs(nodeSecurityFolder)
+
+        shutil.copyfile('{}/root.cert'.format(securityDir),
+                        '{}/root.cert'.format(nodeSecurityFolder))
+
+        # Create site certificate
+        siteName = '{}{}-site'.format(self.network, self.node.name)
+        siteKeyFile = '{}/site.keys'.format(nodeSecurityFolder)
+        siteCertFile = '{}/site.cert'.format(nodeSecurityFolder)
+        Nlsr.createKey(self.node, siteName, siteKeyFile)
+
+        # Copy siteKeyFile from remote for ndnsec-certgen
+        if isinstance(self.node, RemoteMixin) and self.node.isRemote:
+            login = 'mininet@{}'.format(self.node.server)
+            src = '{}:{}'.format(login, siteKeyFile)
+            dst = siteKeyFile
+            scp(src, dst)
+
+        # Root key is in root namespace, must sign site key and then install on host
+        sh('ndnsec-certgen -s {} -r {} > {}'.format(rootName, siteKeyFile, siteCertFile))
+
+        # Copy root.cert and site.cert from localhost to remote host
+        if isinstance(self.node, RemoteMixin) and self.node.isRemote:
+            login = 'mininet@{}'.format(self.node.server)
+            src = '{}/site.cert'.format(nodeSecurityFolder)
+            src2 = '{}/root.cert'.format(nodeSecurityFolder)
+            dst = '{}:/tmp/'.format(login)
+            scp(src, src2, dst)
+            self.node.cmd('mv /tmp/*.cert {}'.format(nodeSecurityFolder))
+
+        self.node.cmd('ndnsec-cert-install -f {}'.format(siteCertFile))
+
+        # Create and install operator certificate
+        opName = '{}/%C1.Operator/op'.format(siteName)
+        opKeyFile = '{}/op.keys'.format(nodeSecurityFolder)
+        opCertFile = '{}/op.cert'.format(nodeSecurityFolder)
+        Nlsr.createKey(self.node, opName, opKeyFile)
+        Nlsr.createCertificate(self.node, siteName, opKeyFile, opCertFile)
+        self.node.cmd('ndnsec-cert-install -f {}'.format(opCertFile))
+
+        # Create and install router certificate
+        routerName = '{}/%C1.Router/cs/{}'.format(siteName, self.node.name)
+        routerKeyFile = '{}/router.keys'.format(nodeSecurityFolder)
+        routerCertFile = '{}/router.cert'.format(nodeSecurityFolder)
+        Nlsr.createKey(self.node, routerName, routerKeyFile)
+        Nlsr.createCertificate(self.node, opName, routerKeyFile, routerCertFile)
+        self.node.cmd('ndnsec-cert-install -f {}'.format(routerCertFile))
+
+    def createConfigFile(self):
+
+        self.__editGeneralSection()
+        self.__editNeighborsSection()
+        self.__editHyperbolicSection()
+        self.__editFibSection()
+        self.__editAdvertisingSection()
+        self.__editSecuritySection()
+
+    def __editGeneralSection(self):
+
+        self.node.cmd('{} -s general.network -v {}'.format(self.infocmd, self.network))
+        self.node.cmd('{} -s general.site -v /{}-site'.format(self.infocmd, self.node.name))
+        self.node.cmd('{} -s general.router -v /%C1.Router/cs/{}'.format(self.infocmd, self.node.name))
+        self.node.cmd('{} -s general.state-dir -v {}/log'.format(self.infocmd, self.homeDir))
+        self.node.cmd('{} -s general.sync-protocol -v {}'.format(self.infocmd, self.sync))
+
+    def __editNeighborsSection(self):
+
+        self.node.cmd('{} -d neighbors.neighbor'.format(self.infocmd))
+        for intf in self.node.intfList():
+            link = intf.link
+            if not link:
+                continue
+
+            node1, node2 = link.intf1.node, link.intf2.node
+
+            # Todo: add some switch support
+            if isinstance(node1, Switch) or isinstance(node2, Switch):
+                continue
+
+            if node1 == self.node:
+                other = node2
+                ip = other.IP(str(link.intf2))
+            else:
+                other = node1
+                ip = other.IP(str(link.intf1))
+
+            linkCost = intf.params.get('delay', '10ms').replace('ms', '')
+
+            self.neighborIPs.append(ip)
+
+            self.node.cmd('{} -a neighbors.neighbor \
+                          <<<\'name {}{}-site/%C1.Router/cs/{} face-uri {}://{}\n link-cost {}\''
+                          .format(self.infocmd, self.network, other.name, other.name,
+                                  self.faceType, ip, linkCost))
+
+    def __editHyperbolicSection(self):
+
+        self.node.cmd('{} -s hyperbolic.state -v {}'.format(self.infocmd, self.hyperbolicState))
+        self.node.cmd('{} -s hyperbolic.radius -v {}'.format(self.infocmd, self.hyperRadius))
+        self.node.cmd('{} -s hyperbolic.angle -v {}'.format(self.infocmd, self.hyperAngle))
+
+    def __editFibSection(self):
+
+        self.node.cmd('{} -s fib.max-faces-per-prefix  -v {}'.format(self.infocmd, self.nFaces))
+
+    def __editAdvertisingSection(self):
+
+        self.node.cmd('{} -d advertising.prefix'.format(self.infocmd))
+        self.node.cmd('{} -s advertising.prefix -v {}{}-site/{}'
+                      .format(self.infocmd, self.network, self.node.name, self.node.name))
+
+    def __editSecuritySection(self):
+
+        self.node.cmd('{} -d security.cert-to-publish'.format(self.infocmd))
+        if not self.security:
+            self.node.cmd('{} -s security.validator.trust-anchor.type -v any'.format(self.infocmd))
+            self.node.cmd('{} -d security.validator.trust-anchor.file-name'.format(self.infocmd))
+            self.node.cmd('{} -s security.prefix-update-validator.trust-anchor.type -v any'.format(self.infocmd))
+            self.node.cmd('{} -d security.prefix-update-validator.trust-anchor.file-name'.format(self.infocmd))
+        else:
+            self.node.cmd('{} -s security.validator.trust-anchor.file-name -v security/root.cert'.format(self.infocmd))
+            self.node.cmd('{} -s security.prefix-update-validator.trust-anchor.file-name -v security/site.cert'.format(self.infocmd))
+            self.node.cmd('{} -p security.cert-to-publish -v security/site.cert'.format(self.infocmd))
+            self.node.cmd('{} -p security.cert-to-publish -v security/op.cert'.format(self.infocmd))
+            self.node.cmd('{} -p security.cert-to-publish -v security/router.cert'.format(self.infocmd))
diff --git a/ndn/apps/__init__.py b/minindn/helpers/__init__.py
similarity index 100%
copy from ndn/apps/__init__.py
copy to minindn/helpers/__init__.py
diff --git a/minindn/helpers/experiment.py b/minindn/helpers/experiment.py
new file mode 100644
index 0000000..226684e
--- /dev/null
+++ b/minindn/helpers/experiment.py
@@ -0,0 +1,107 @@
+# -*- Mode:python; c-file-style:"gnu"; indent-tabs-mode:nil -*- */
+#
+# Copyright (C) 2015-2019, The University of Memphis,
+#                          Arizona Board of Regents,
+#                          Regents of the University of California.
+#
+# This file is part of Mini-NDN.
+# See AUTHORS.md for a complete list of Mini-NDN authors and contributors.
+#
+# Mini-NDN is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Mini-NDN is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with Mini-NDN, e.g., in COPYING.md file.
+# If not, see <http://www.gnu.org/licenses/>.
+
+import time
+import sys
+from itertools import cycle
+
+from mininet.log import info
+
+from minindn.helpers.nfdc import Nfdc
+from minindn.helpers.ndnpingclient import NDNPingClient
+
+class Experiment(object):
+    @staticmethod
+    def checkConvergence(ndn, hosts, convergenceTime, quit=False):
+        # Wait for convergence time period
+        info('Waiting {} seconds for convergence...\n'.format(convergenceTime))
+        time.sleep(convergenceTime)
+        info('...done\n')
+
+        # To check whether all the nodes of NLSR have converged
+        didNlsrConverge = True
+
+        # Checking for convergence
+        for host in hosts:
+            statusRouter = host.cmd('nfdc fib list | grep site/%C1.Router/cs/')
+            statusPrefix = host.cmd('nfdc fib list | grep ndn | grep site | grep -v Router')
+            didNodeConverge = True
+            for node in hosts:
+                # Node has its own router name in the fib list, but not name prefix
+                if (('/ndn/{}-site/%C1.Router/cs/{}'.format(node.name, node.name)) not in statusRouter or
+                      host.name != node.name and ('/ndn/{}-site/{}'.format(node.name, node.name)) not in statusPrefix):
+                    didNodeConverge = False
+                    didNlsrConverge = False
+
+            host.cmd('echo {} > convergence-result &'.format(didNodeConverge))
+
+        if not didNlsrConverge:
+            info('NLSR has not converged. Exiting...\n')
+            if quit:
+                ndn.stop()
+                sys.exit(1)
+        else:
+            info('NLSR has converged successfully.\n')
+
+        return didNlsrConverge
+
+    @staticmethod
+    def setupPing(hosts, strategy):
+        for host in hosts:
+            host.cmd('mkdir -p ping-data')
+            Nfdc.setStrategy(host, '/ndn/', strategy)
+            host.cmd('ndnpingserver /ndn/{}-site/{} > ping-server &'.format(host.name, host.name))
+
+    @staticmethod
+    def startPctPings(net, nPings, pctTraffic=1.0):
+        nNodesToPing = int(round(len(net.hosts) * pctTraffic))
+        info('Each node will ping {} node(s)\n'.format(nNodesToPing))
+        # Temporarily store all the nodes being pinged by a particular node
+        nodesPingedList = []
+        pingedDict = {}
+
+        for host in net.hosts:
+            # Create a circular list
+            pool = cycle(net.hosts)
+
+            # Move iterator to current node
+            next(x for x in pool if host.name == x.name)
+
+            # Track number of nodes to ping scheduled for this node
+            nNodesScheduled = 0
+
+            while nNodesScheduled < nNodesToPing:
+                other = next(pool)
+
+                # Do not ping self
+                if host.name != other.name:
+                    NDNPingClient.ping(host, other, nPings)
+                    nodesPingedList.append(other)
+
+                # Always increment because in 100% case a node should not ping itself
+                nNodesScheduled = nNodesScheduled + 1
+
+            pingedDict[host] = nodesPingedList
+            nodesPingedList = []
+
+        return pingedDict
diff --git a/ndn/apps/routing_helper.py b/minindn/helpers/ip_routing_helper.py
similarity index 90%
rename from ndn/apps/routing_helper.py
rename to minindn/helpers/ip_routing_helper.py
index 4c89e48..8c2bddf 100644
--- a/ndn/apps/routing_helper.py
+++ b/minindn/helpers/ip_routing_helper.py
@@ -21,11 +21,10 @@
 # along with Mini-NDN, e.g., in COPYING.md file.
 # If not, see <http://www.gnu.org/licenses/>.
 
-from igraph import *
+from igraph import Graph
 from mininet.log import info
 
-
-class LinkInfo:
+class LinkInfo(object):
     """
     This class is used to encapsule link information (IP and interface names).
     """
@@ -36,8 +35,7 @@
         self.end_intf_name = end_intf_name
         self.end_ip = end_ip
 
-
-class IPRoutingHelper:
+class IPRoutingHelper(object):
     """The routing helper allows to run IP-based evaluations with Mini-NDN. It configures static IP
     routes to all nodes, which means that all nodes can reach all other nodes in the network
     reachable, even when relaying is required.
@@ -79,9 +77,9 @@
         mini_links = net.links
 
         # Enabling IP forwaring on all nodes
-        info("Configure IP forwarding on all nodes\n")
+        info('Configure IP forwarding on all nodes\n')
         for node in mini_nodes:
-            node.cmd("sysctl -w net.ipv4.ip_forward=1")
+            node.cmd('sysctl -w net.ipv4.ip_forward=1')
 
         # Calculate igraph to calculate all shortest paths between nodes
         node_names = [node.name for node in mini_nodes]
@@ -107,11 +105,11 @@
                     shortest_path = paths[0]
                     shortest_path_with_nodenames = []
                     for node in shortest_path:
-                        shortest_path_with_nodenames.append(networkGraph.vs["name"][node])
+                        shortest_path_with_nodenames.append(networkGraph.vs['name'][node])
                     named_paths.append(shortest_path_with_nodenames)
 
-        # Iterate over all paths and configure the routes using the "route add"
-        info("Configure routes on all nodes\n")
+        # Iterate over all paths and configure the routes using the 'route add'
+        info('Configure routes on all nodes\n')
         for path in named_paths:
             start_node = path[0]
             end_node = path[-1]
@@ -125,12 +123,12 @@
                 addr = mini_end.intfs[intf].ip
                 if len(path) == 2:
                     # For direct connection, configure exit interface
-                    info("[{}] route add -host {} dev {}\n".format(start_node, addr, start_intf))
-                    mini_start.cmd("route add -host {} dev {}".format(addr, start_intf))
+                    info('[{}] route add -host {} dev {}\n'.format(start_node, addr, start_intf))
+                    mini_start.cmd('route add -host {} dev {}'.format(addr, start_intf))
                 elif len(path) > 2:
                     # For longer paths, configure next hop as gateway
                     gateway_ip = link_info.end_ip
-                    info("[{}] route add -host {} dev {} gw {}\n"
+                    info('[{}] route add -host {} dev {} gw {}\n'
                          .format(start_node, addr, start_intf, gateway_ip))
-                    mini_start.cmd("route add -host {} dev {} gw {}"
+                    mini_start.cmd('route add -host {} dev {} gw {}'
                                    .format(addr, start_intf, gateway_ip))
diff --git a/ndn/apps/calculate_routing.py b/minindn/helpers/ndn_routing_helper.py
similarity index 68%
rename from ndn/apps/calculate_routing.py
rename to minindn/helpers/ndn_routing_helper.py
index ce1966f..38d841a 100644
--- a/ndn/apps/calculate_routing.py
+++ b/minindn/helpers/ndn_routing_helper.py
@@ -1,5 +1,4 @@
-# -*- Mode:python; c-file-style:"gnu"; indent-tabs-mode:nil -*- */
-#!/usr/bin/env python
+ # -*- Mode:python; c-file-style:"gnu"; indent-tabs-mode:nil -*- */
 #
 # Copyright (C) 2015-2019, The University of Memphis
 #
@@ -20,24 +19,121 @@
 # along with Mini-NDN, e.g., in COPYING.md file.
 # If not, see <http://www.gnu.org/licenses/>.
 
+# IMPORTANT! This feature is in highly experimental phase and may go several changes
+# in future
+
 '''
 This module will compute link state, hyperbolic and geohyperbolic
 routes and their costs from the given Mini-NDN topology
 '''
 
 import heapq
-import math
-from collections import defaultdict
 from math import sin, cos, sinh, cosh, acos, acosh
 import json
 import operator
+from collections import defaultdict
 
-from mininet.log import info, debug, error
+from mininet.log import info, debug, error, warn
+from minindn.helpers.nfdc import Nfdc as nfdc
 
 UNKNOWN_DISTANCE = -1
 HYPERBOLIC_COST_ADJUSTMENT_FACTOR = 1000
 
-class CalculateRoutes():
+def dijkstra(graph, start, end, ignoredNode=None):
+    """
+    Compute shortest path and cost from a given source to a destination
+    using Dijkstra algorithm
+
+    :param Graph graph: given network topology/graph
+    :param Start start: source node in a given network graph/topology
+    :end End end: destination node in a given network graph/topology
+    :param Node ignoredNode: node to ignore computing shortest path from
+    """
+    queue = [(0, start, [])]
+    seen = set()
+    while True:
+        (cost, v, path) = heapq.heappop(queue)
+        if v not in seen:
+            path = path + [v]
+            seen.add(v)
+            if v == end:
+                debug("Distance from {} to {} is {}".format(start, end, cost))
+                return cost, path
+            for (_next, c) in graph[v].items():
+                if _next != ignoredNode: # Ignore path going via ignoreNode
+                    heapq.heappush(queue, (cost + c, _next, path))
+
+        if not queue: # return if no path exist from source - destination except via ignoreNode
+            debug("Distance from {} to {} is {}".format(start, end, cost))
+            return cost, None
+
+def calculateAngularDistance(angleVectorI, angleVectorJ):
+    """
+    For hyperbolic/geohyperbolic routing algorithm, this function computes angular distance between
+    two nodes. A node can have two or more than two angular coordinates.
+
+    :param AngleVectorI angleVectorI: list of angular coordinate of a give node I
+    :param AngleVectorJ angleVectorJ: list of angular coordinate of a give node J
+
+    ref: https://en.wikipedia.org/wiki/N-sphere#Spherical_coordinates
+
+    """
+    innerProduct = 0.0
+    if len(angleVectorI) != len(angleVectorJ):
+        error("Angle vector sizes do not match")
+        return UNKNOWN_DISTANCE
+
+    # Calculate x0 of the vectors
+    x0i = cos(angleVectorI[0])
+    x0j = cos(angleVectorJ[0])
+
+    # Calculate xn of the vectors
+    xni = sin(angleVectorI[len(angleVectorI) - 1])
+    xnj = sin(angleVectorJ[len(angleVectorJ) - 1])
+
+    # Do the aggregation of the (n-1) coordinates (if there is more than one angle)
+    # i.e contraction of all (n-1)-dimensional angular coordinates to one variable
+    for k in range(0, len(angleVectorI)-1):
+        xni *= sin(angleVectorI[k])
+        xnj *= sin(angleVectorJ[k])
+
+    innerProduct += (x0i * x0j) + (xni * xnj)
+
+    if len(angleVectorI) > 1:
+        for m in range(1, len(angleVectorI)):
+            # Calculate euclidean coordinates given the angles and assuming R_sphere = 1
+            xmi = cos(angleVectorI[m])
+            xmj = cos(angleVectorJ[m])
+            for l in range(0, m):
+                xmi *= sin(angleVectorI[l])
+                xmj *= sin(angleVectorJ[l])
+
+        innerProduct += xmi * xmj
+
+    # ArcCos of the inner product gives the angular distance
+    # between two points on a d-dimensional sphere
+    angularDist = acos(innerProduct)
+    debug("Angular distance from {} to {} is {}".format(angleVectorI, angleVectorJ, angularDist))
+    return angularDist
+
+def getHyperbolicDistance(sourceNode, destNode):
+    """
+    Return hyperbolic or geohyperbolic distance between two nodes. The distance is computed
+    on the basis of following algorithm/mathematics
+    ref: https://en.wikipedia.org/wiki/Hyperbolic_geometry
+    """
+    r1 = [key for key in sourceNode][0]
+    r2 = [key for key in destNode][0]
+
+    zeta = 1.0
+    dtheta = calculateAngularDistance(sourceNode[r1], destNode[r2])
+    hyperbolicDistance = (1./zeta) * acosh(cosh(zeta * r1) * cosh(zeta * r2) -\
+                                           sinh(zeta * r1) * sinh(zeta * r2) * cos(dtheta))
+
+    debug("Distance from {} to {} is {}".format(sourceNode, destNode, hyperbolicDistance))
+    return hyperbolicDistance
+
+class _CalculateRoutes(object):
     """
     Creates a route calculation object, which is used to compute routes from a node to
     every other nodes in a given topology topology using hyperbolic or geohyperbolic
@@ -51,14 +147,20 @@
         self.nodeDict = defaultdict(dict)
         self.routingType = routingType
         for host in netObj.hosts:
-            radius = float(host.params['params']['radius'])
-            angles = [float(x) for x in host.params['params']['angle'].split(',')]
+            if 'radius' in host.params['params']:
+                radius = float(host.params['params']['radius'])
+            else:
+                radius = 0.0
+            if 'angles' in host.params['params']:
+                angles = [float(x) for x in host.params['params']['angle'].split(',')]
+            else:
+                angles = 0.0
             self.nodeDict[host.name][radius] = angles
 
-        for link in netObj.topo.links_conf:
-            linkDelay = int(link.linkDict['delay'].replace("ms", ""))
-            self.adjacenctMatrix[link.h1][link.h2] = linkDelay
-            self.adjacenctMatrix[link.h2][link.h1] = linkDelay
+        for link in netObj.topo.links(withInfo=True):
+            linkDelay = int(link[2]['delay'].replace("ms", ""))
+            self.adjacenctMatrix[link[0]][link[1]] = linkDelay
+            self.adjacenctMatrix[link[1]][link[0]] = linkDelay
 
     def getNestedDictionary(self):
         return defaultdict(self.getNestedDictionary)
@@ -169,96 +271,100 @@
         debug("Shortest Distance Matrix: {}".format(json.dumps(distanceMatrixViaNeighbor)))
         return distanceMatrixViaNeighbor
 
-def dijkstra(graph, start, end, ignoredNode = None):
+class NdnRoutingHelper(object):
     """
-    Compute shortest path and cost from a given source to a destination
-    using Dijkstra algorithm
+    This module is a helper class which helps to create face and register routes
+    to NFD from a given node to all of its neighbors.
 
-    :param Graph graph: given network topology/graph
-    :param Start start: source node in a given network graph/topology
-    :end End end: destination node in a given network graph/topology
-    :param Node ignoredNode: node to ignore computing shortest path from
-    """
-    queue = [(0, start, [])]
-    seen = set()
-    while True:
-        (cost, v, path) = heapq.heappop(queue)
-        if v not in seen:
-            path = path + [v]
-            seen.add(v)
-            if v == end:
-                debug("Distance from {} to {} is {}".format(start, end, cost))
-                return cost, path
-            for (_next, c) in graph[v].iteritems():
-                if _next != ignoredNode: # Ignore path going via ignoreNode
-                    heapq.heappush(queue, (cost + c, _next, path))
-
-        if not queue: # return if no path exist from source - destination except via ignoreNode
-            debug("Distance from {} to {} is {}".format(start, end, cost))
-            return cost, None
-
-def calculateAngularDistance(angleVectorI, angleVectorJ):
-    """
-    For hyperbolic/geohyperbolic routing algorithm, this function computes angular distance between
-    two nodes. A node can have two or more than two angular coordinates.
-
-    :param AngleVectorI angleVectorI: list of angular coordinate of a give node I
-    :param AngleVectorJ angleVectorJ: list of angular coordinate of a give node J
-
-    ref: https://en.wikipedia.org/wiki/N-sphere#Spherical_coordinates
+    :param NetObject netObject: Mininet net object
+    :param FaceType faceType: UDP, Ethernet etc.
+    :param Routing routingType: (optional) Routing algorithm, link-state or hr etc
 
     """
-    innerProduct = 0.0
-    if len(angleVectorI) != len(angleVectorJ):
-        error("Angle vector sizes do not match")
-        return UNKNOWN_DISTANCE
+    def __init__(self, netObject, faceType=nfdc.PROTOCOL_UDP, routingType="link-state"):
+        self.net = netObject
+        self.faceType = faceType
+        self.routingType = routingType
+        self.routes = []
+        self.namePrefixes = {host_name.name: [] for host_name in self.net.hosts}
+        self.routeObject = _CalculateRoutes(self.net, self.routingType)
 
-    # Calculate x0 of the vectors
-    x0i = cos(angleVectorI[0])
-    x0j = cos(angleVectorJ[0])
+    def globalRoutingHelperHandler(self):
+        for host in self.net.hosts:
+            neighborIPs = self.getNeighbor(host)
+            self.createFaces(host, neighborIPs)
+            self.routeAdd(host, neighborIPs)
 
-    # Calculate xn of the vectors
-    xni = sin(angleVectorI[len(angleVectorI) - 1])
-    xnj = sin(angleVectorJ[len(angleVectorJ) - 1])
+        info('Processed all the routes to NFD\n')
 
-    # Do the aggregation of the (n-1) coordinates (if there is more than one angle)
-    # i.e contraction of all (n-1)-dimensional angular coordinates to one variable
-    for k in range(0, len(angleVectorI)-1):
-        xni *= sin(angleVectorI[k])
-        xnj *= sin(angleVectorJ[k])
+    def addOrigin(self, nodes, prefix):
+        """
+        Add prefix/s as origin on node/s
 
-    innerProduct += (x0i * x0j) + (xni * xnj)
+        :param Prefix prefix: Prefix that is originated by node/s (as producer) for this prefix
+        :param Nodes nodes: List of nodes from net object
+        """
+        for node in nodes:
+            self.namePrefixes[node.name] = prefix
 
-    if (len(angleVectorI) > 1):
-        for m in range(1, len(angleVectorI)):
-            # Calculate euclidean coordinates given the angles and assuming R_sphere = 1
-            xmi = cos(angleVectorI[m])
-            xmj = cos(angleVectorJ[m])
-            for l in range (0, m):
-                xmi *= sin(angleVectorI[l])
-                xmj *= sin(angleVectorJ[l])
+    def calculateNPossibleRoutes(self, nFaces=0):
+        """
+        By default, calculates all possible routes i.e. routes via all the faces of a node.
+        pass nFaces if want to compute routes via n number of faces. e.g. 2. For larger topology
+        the computation might take huge amount of time.
 
-        innerProduct += xmi * xmj
+        :param int nFaces: (optional) number of faces to consider while computing routes. Default
+          i.e. nFaces = 0 will compute all possible routes
 
-    # ArcCos of the inner product gives the angular distance
-    # between two points on a d-dimensional sphere
-    angularDist = acos(innerProduct)
-    debug("Angular distance from {} to {} is {}".format(angleVectorI, angleVectorJ, angularDist))
-    return angularDist
+        """
+        self.routes = self.routeObject.getRoutes(nFaces)
+        if self.routes:
+            self.globalRoutingHelperHandler()
+        else:
+            warn("Route computation failed\n")
 
-def getHyperbolicDistance(sourceNode, destNode):
-    """
-    Return hyperbolic or geohyperbolic distance between two nodes. The distance is computed
-    on the basis of following algorithm/mathematics
-    ref: https://en.wikipedia.org/wiki/Hyperbolic_geometry
-    """
-    r1 = [key for key in sourceNode][0]
-    r2 = [key for key in destNode][0]
+    def calculateRoutes(self):
+        # Calculate shortest path for every node
+        self.calculateNPossibleRoutes(nFaces=1)
 
-    zeta = 1.0
-    dtheta = calculateAngularDistance(sourceNode[r1], destNode[r2])
-    hyperbolicDistance = (1./zeta) * acosh(cosh(zeta * r1) * cosh(zeta * r2) -\
-                                           sinh(zeta * r1) * sinh(zeta * r2) * cos(dtheta))
+    def createFaces(self, node, neighborIPs):
+        for ip in neighborIPs.values():
+            nfdc.createFace(node, ip, self.faceType)
 
-    debug("Distance from {} to {} is {}".format(sourceNode, destNode, hyperbolicDistance))
-    return hyperbolicDistance
\ No newline at end of file
+    def routeAdd(self, node, neighborIPs):
+        """
+        Add route from a node to its neighbors for each prefix/s  advertised by destination node
+
+        :param Node node: source node (Mininet net.host)
+        :param IP neighborIPs: IP addresses of neighbors
+        """
+        neighbors = self.routes[node.name]
+        for route in neighbors:
+            destination = route[0]
+            cost = int(route[1])
+            nextHop = route[2]
+            defaultPrefix = "/ndn/{}-site/{}".format(destination, destination)
+            prefixes = [defaultPrefix] + self.namePrefixes[destination]
+            for prefix in prefixes:
+                # Register routes to all the available destination name prefix/s
+                nfdc.registerRoute(node, prefix, neighborIPs[nextHop], \
+                                   nfdc.PROTOCOL_UDP, cost=cost)
+    @staticmethod
+    def getNeighbor(node):
+        # Nodes to IP mapping
+        neighborIPs = defaultdict()
+        for intf in node.intfList():
+            link = intf.link
+            if link:
+                node1, node2 = link.intf1.node, link.intf2.node
+
+                if node1 == node:
+                    other = node2
+                    ip = other.IP(str(link.intf2))
+                else:
+                    other = node1
+                    ip = other.IP(str(link.intf1))
+
+                # Used later to create faces
+                neighborIPs[other.name] = ip
+        return neighborIPs
diff --git a/ndn/apps/ndn_ping_client.py b/minindn/helpers/ndnpingclient.py
similarity index 63%
rename from ndn/apps/ndn_ping_client.py
rename to minindn/helpers/ndnpingclient.py
index 7e7ff2a..8220a2d 100644
--- a/ndn/apps/ndn_ping_client.py
+++ b/minindn/helpers/ndnpingclient.py
@@ -1,6 +1,6 @@
 # -*- Mode:python; c-file-style:"gnu"; indent-tabs-mode:nil -*- */
 #
-# Copyright (C) 2015-2018, The University of Memphis,
+# Copyright (C) 2015-2019, The University of Memphis,
 #                          Arizona Board of Regents,
 #                          Regents of the University of California.
 #
@@ -23,21 +23,23 @@
 
 import time
 
-class NDNPingClient:
+# Todo: convert to app
+
+class NDNPingClient(object):
     @staticmethod
     def ping(source, dest, nPings=1, interval=None, timeout=None, starting_seq_num=None,
              identifier=None, allow_stale_data=False, print_timestamp=True, sleepTime=0.2):
-        print("Scheduling ping(s) from {} to {}".format(source.name, dest.name))
-        # Use "&" to run in background and perform parallel pings
-        source.cmd("ndnping{1}{2}{3}{4}{5}{6}{7} /ndn/{0}-site/{0} >> ping-data/{0}.txt &"
+        print('Scheduling ping(s) from {} to {}'.format(source.name, dest.name))
+        # Use '&' to run in background and perform parallel pings
+        source.cmd('ndnping{1}{2}{3}{4}{5}{6}{7} /ndn/{0}-site/{0} >> ping-data/{0}.txt &'
         .format(
             dest,
-            " -c {}".format(nPings),
-            " -i {}".format(interval) if interval else "",
-            " -o {}".format(timeout) if timeout  else "",
-            " -n {}".format(starting_seq_num) if starting_seq_num else "",
-            " -p {}".format(identifier) if identifier else "",
-            " -a" if allow_stale_data else "",
-            " -t" if print_timestamp else ""
+            ' -c {}'.format(nPings),
+            ' -i {}'.format(interval) if interval else '',
+            ' -o {}'.format(timeout) if timeout  else '',
+            ' -n {}'.format(starting_seq_num) if starting_seq_num else '',
+            ' -p {}'.format(identifier) if identifier else '',
+            ' -a' if allow_stale_data else '',
+            ' -t' if print_timestamp else ''
         ))
-        time.sleep(sleepTime)
\ No newline at end of file
+        time.sleep(sleepTime)
diff --git a/ndn/apps/nfdc.py b/minindn/helpers/nfdc.py
similarity index 62%
rename from ndn/apps/nfdc.py
rename to minindn/helpers/nfdc.py
index e772a42..4a9ebf2 100644
--- a/ndn/apps/nfdc.py
+++ b/minindn/helpers/nfdc.py
@@ -21,65 +21,65 @@
 # along with Mini-NDN, e.g., in COPYING.md file.
 # If not, see <http://www.gnu.org/licenses/>.
 
-import time
 from mininet.log import debug
+from minindn.minindn import Minindn
 
 SLEEP_TIME = 0.2
 
-class Nfdc:
-    STRATEGY_ASF = "asf"
-    STRATEGY_BEST_ROUTE = "best-route"
-    STRATEGY_MULTICAST = "multicast"
-    STRATEGY_NCC = "ncc"
-    PROTOCOL_UDP = "udp"
-    PROTOCOL_TCP = "tcp"
-    PROTOCOL_ETHER = "ether"
+class Nfdc(object):
+    STRATEGY_ASF = 'asf'
+    STRATEGY_BEST_ROUTE = 'best-route'
+    STRATEGY_MULTICAST = 'multicast'
+    STRATEGY_NCC = 'ncc'
+    PROTOCOL_UDP = 'udp'
+    PROTOCOL_TCP = 'tcp'
+    PROTOCOL_ETHER = 'ether'
 
     @staticmethod
     def registerRoute(node, namePrefix, remoteNodeAddress, protocol=PROTOCOL_UDP, origin=255,
                       cost=0, inheritFlag=True, captureFlag=False, expirationInMillis=None):
-        cmd = ("nfdc route add {} {}://{} origin {} cost {} {}{}").format(
+        cmd = ('nfdc route add {} {}://{} origin {} cost {} {}{}').format(
             namePrefix,
             protocol,
             remoteNodeAddress,
             origin,
             cost,
-            "no-inherit " if not inheritFlag else "",
-            "capture " if captureFlag else "",
-            "expires {}".format(expirationInMillis) if expirationInMillis else ""
+            'no-inherit ' if not inheritFlag else '',
+            'capture ' if captureFlag else '',
+            'expires {}'.format(expirationInMillis) if expirationInMillis else ''
         )
 
         debug(node.cmd(cmd))
-        time.sleep(SLEEP_TIME)
+        Minindn.sleep(SLEEP_TIME)
 
     @staticmethod
     def unregisterRoute(node, namePrefix, remoteNodeAddress, origin=255):
-        cmd = "nfdc route remove {} {} {}".format(namePrefix, remoteNodeAddress, origin)
+        cmd = 'nfdc route remove {} {} {}'.format(namePrefix, remoteNodeAddress, origin)
         debug(node.cmd(cmd))
-        time.sleep(SLEEP_TIME)
+        Minindn.sleep(SLEEP_TIME)
 
     @staticmethod
-    def createFace(node, remoteNodeAddress, protocol="udp", isPermanent=False):
-        cmd = ("nfdc face create {}://{} {}".format(
+    def createFace(node, remoteNodeAddress, protocol='udp', isPermanent=False):
+        cmd = ('nfdc face create {}://{} {}'.format(
             protocol,
             remoteNodeAddress,
-            "permanent" if isPermanent else "persistent"
+            'permanent' if isPermanent else 'persistent'
         ))
         debug(node.cmd(cmd))
-        time.sleep(SLEEP_TIME)
+        Minindn.sleep(SLEEP_TIME)
 
     @staticmethod
-    def destroyFace(node, remoteNodeAddress, protocol="udp"):
-        debug(node.cmd("nfdc face destroy {}://{}".format(protocol, remoteNodeAddress)))
-        time.sleep(SLEEP_TIME)
+    def destroyFace(node, remoteNodeAddress, protocol='udp'):
+        debug(node.cmd('nfdc face destroy {}://{}'.format(protocol, remoteNodeAddress)))
+        Minindn.sleep(SLEEP_TIME)
 
     @staticmethod
     def setStrategy(node, namePrefix, strategy):
-        cmd = "nfdc strategy set {} ndn:/localhost/nfd/strategy/{}".format(namePrefix, strategy)
+        cmd = 'nfdc strategy set {} ndn:/localhost/nfd/strategy/{}'.format(namePrefix, strategy)
         debug(node.cmd(cmd))
-        time.sleep(SLEEP_TIME)
+        Minindn.sleep(SLEEP_TIME)
 
     @staticmethod
     def unsetStrategy(node, namePrefix):
         debug(node.cmd("nfdc strategy unset {}".format(namePrefix)))
-        time.sleep(SLEEP_TIME)
\ No newline at end of file
+        Minindn.sleep(SLEEP_TIME)
diff --git a/ndn/process_monitor.py b/minindn/helpers/process_monitor.py
similarity index 74%
rename from ndn/process_monitor.py
rename to minindn/helpers/process_monitor.py
index da41b1d..4fa3738 100644
--- a/ndn/process_monitor.py
+++ b/minindn/helpers/process_monitor.py
@@ -1,6 +1,6 @@
 # -*- Mode:python; c-file-style:"gnu"; indent-tabs-mode:nil -*- */
 #
-# Copyright (C) 2015-2018, The University of Memphis,
+# Copyright (C) 2015-2019, The University of Memphis,
 #                          Arizona Board of Regents,
 #                          Regents of the University of California.
 #
@@ -23,27 +23,26 @@
 
 import time
 
-from subprocess import call
 from threading import Timer
 
-class ProcessMonitor:
+class ProcessMonitor(object):
     def __init__(self, processId, processName, outputDir, interval=1):
         self._processId = processId.strip()
         self._processName = processName
-        self._statFile = "/proc/{}/stat".format(self._processId)
-        self._logFile = "{}/{}-{}-stat.txt".format(outputDir, self._processName, self._processId)
+        self._statFile = '/proc/{}/stat'.format(self._processId)
+        self._logFile = '{}/{}-{}-stat.txt'.format(outputDir, self._processName, self._processId)
         self._interval = interval
 
     def _recordStats(self):
         try:
-            with open(self._statFile, "r") as stat:
+            with open(self._statFile, 'r') as stat:
                 currentTime = int(time.time())
-                with open(self._logFile, "a") as log:
+                with open(self._logFile, 'a') as log:
                     for line in stat:
-                        log.write("{} {}".format(currentTime, line))
+                        log.write('{} {}'.format(currentTime, line))
         except IOError as e:
-            print "I/O error({0}): {1}".format(e.errno, e.strerror)
-            print "No process with PID={}".format(self._processId)
+            print('I/O error({0}): {1}'.format(e.errno, e.strerror))
+            print('No process with PID={}'.format(self._processId))
             return
 
         self.start() # Reschedule event
diff --git a/minindn/minindn.py b/minindn/minindn.py
new file mode 100644
index 0000000..847e003
--- /dev/null
+++ b/minindn/minindn.py
@@ -0,0 +1,213 @@
+# -*- Mode:python; c-file-style:"gnu"; indent-tabs-mode:nil -*- */
+#
+# Copyright (C) 2015-2019, The University of Memphis,
+#                          Arizona Board of Regents,
+#                          Regents of the University of California.
+#
+# This file is part of Mini-NDN.
+# See AUTHORS.md for a complete list of Mini-NDN authors and contributors.
+#
+# Mini-NDN is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Mini-NDN is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with Mini-NDN, e.g., in COPYING.md file.
+# If not, see <http://www.gnu.org/licenses/>.
+
+import argparse
+import sys
+import time
+import os
+import configparser
+from subprocess import call, check_output
+
+from mininet.topo import Topo
+from mininet.net import Mininet
+from mininet.link import TCLink
+from mininet.node import Switch
+from mininet.util import ipStr, ipParse
+from mininet.log import info, error
+
+class Minindn(object):
+    """ This class provides the following features to the user:
+        1) Wrapper around Mininet object with option to pass topology directly
+           1.1) Users can pass custom argument parser to extend the default on here
+        2) Parses the topology file given via command line if user does not pass a topology object
+        3) Provides way to stop Mini-NDN via stop
+           3.1) Applications register their clean up function with this class
+        4) Sets IPs on neighbors for connectivity required in a switch-less topology
+        5) Some other utility functions
+    """
+    ndnSecurityDisabled = False
+
+    def __init__(self, parser=argparse.ArgumentParser(), topo=None, topoFile=None, **mininetParams):
+        """Create MiniNDN object
+        parser: Parent parser of Mini-NDN parser
+        topo: Mininet topo object (optional)
+        topoFile: Mininet topology file location (optional)
+        mininetParams: Any params to pass to Mininet
+        """
+        self.parser = Minindn.parseArgs(parser)
+        self.args = self.parser.parse_args()
+
+        self.workDir = self.args.workDir
+        self.resultDir = self.args.resultDir
+
+        if not topoFile:
+            # Args has default topology if none specified
+            self.topoFile = self.args.topoFile
+        else:
+            self.topoFile = topoFile
+
+        if topo is None:
+            try:
+                info('Using topology file {}\n'.format(self.topoFile))
+                self.topo = self.processTopo(self.topoFile)
+            except configparser.NoSectionError as e:
+                info('Error reading config file: {}\n'.format(e))
+                sys.exit(1)
+        else:
+            self.topo = topo
+
+        self.net = Mininet(topo=self.topo, link=TCLink, **mininetParams)
+
+        for host in self.net.hosts:
+            if 'params' not in host.params:
+                host.params['params'] = {}
+
+            homeDir = '{}/{}'.format(self.workDir, host.name)
+            host.params['params']['homeDir'] = homeDir
+            host.cmd('mkdir -p {}'.format(homeDir))
+            host.cmd('export HOME={} && cd ~'.format(homeDir))
+
+        self.cleanups = []
+
+        if not self.net.switches:
+            self.ethernetPairConnectivity()
+
+        try:
+            Minindn.ndnSecurityDisabled = '/dummy/KEY/-%9C%28r%B8%AA%3B%60' in \
+                                          check_output('ndnsec-get-default -k'.split()). \
+                                          decode('utf-8').split('\n')
+            info('Dummy key chain patch is installed in ndn-cxx. Security will be disabled.\n')
+        except:
+            pass
+
+    @staticmethod
+    def parseArgs(parent):
+        parser = argparse.ArgumentParser(prog='minindn', parents=[parent], add_help=False)
+
+        # nargs='?' required here since optional argument
+        parser.add_argument('topoFile', nargs='?', default='/usr/local/etc/mini-ndn/default-topology.conf',
+                            help='If no template_file is given, topologies/default-topology.conf will be used.')
+
+        parser.add_argument('--work-dir', action='store', dest='workDir', default='/tmp/minindn',
+                            help='Specify the working directory; default is /tmp/minindn')
+
+        parser.add_argument('--result-dir', action='store', dest='resultDir', default=None,
+                            help='Specify the full path destination folder where experiment results will be moved')
+
+        return parser
+
+    def ethernetPairConnectivity(self):
+        ndnNetBase = '10.0.0.0'
+        interfaces = []
+        for host in self.net.hosts:
+            for intf in host.intfList():
+                link = intf.link
+                node1, node2 = link.intf1.node, link.intf2.node
+
+                if isinstance(node1, Switch) or isinstance(node2, Switch):
+                    continue
+
+                if link.intf1 not in interfaces and link.intf2 not in interfaces:
+                    interfaces.append(link.intf1)
+                    interfaces.append(link.intf2)
+                    node1.setIP(ipStr(ipParse(ndnNetBase) + 1) + '/30', intf=link.intf1)
+                    node2.setIP(ipStr(ipParse(ndnNetBase) + 2) + '/30', intf=link.intf2)
+                    ndnNetBase = ipStr(ipParse(ndnNetBase) + 4)
+
+    @staticmethod
+    def processTopo(topoFile):
+        config = configparser.ConfigParser(delimiters=' ')
+        config.read(topoFile)
+        topo = Topo()
+
+        items = config.items('nodes')
+        for item in items:
+            name = item[0].split(':')[0]
+
+            params = {}
+            for param in item[1].split(' '):
+                if param == '_':
+                    continue
+                params[param.split('=')[0]] = param.split('=')[1]
+
+            topo.addHost(name, params=params)
+
+        try:
+            items = config.items('switches')
+            for item in items:
+                name = item[0].split(':')[0]
+                topo.addSwitch(name)
+        except configparser.NoSectionError:
+            # Switches are optional
+            pass
+
+        items = config.items('links')
+        for item in items:
+            link = item[0].split(':')
+
+            params = {}
+            for param in item[1].split(' '):
+                key = param.split('=')[0]
+                value = param.split('=')[1]
+                if key in ['bw', 'jitter', 'max_queue_size']:
+                    value = int(value)
+                if key == 'loss':
+                    value = float(value)
+                params[key] = value
+
+            topo.addLink(link[0], link[1], **params)
+
+        return topo
+
+    def start(self):
+        self.net.start()
+        time.sleep(3)
+
+    def stop(self):
+        for cleanup in self.cleanups:
+            cleanup()
+        self.net.stop()
+
+    @staticmethod
+    def cleanUp():
+        devnull = open(os.devnull, 'w')
+        call('nfd-stop', stdout=devnull, stderr=devnull)
+        call('mn --clean'.split(), stdout=devnull, stderr=devnull)
+
+    @staticmethod
+    def verifyDependencies():
+        """Prevent MiniNDN from running without necessary dependencies"""
+        dependencies = ['nfd', 'nlsr', 'infoedit', 'ndnping', 'ndnpingserver']
+        devnull = open(os.devnull, 'w')
+        # Checks that each program is in the system path
+        for program in dependencies:
+            if call(['which', program], stdout=devnull):
+                error('{} is missing from the system path! Exiting...\n'.format(program))
+                sys.exit(1)
+        devnull.close()
+
+    @staticmethod
+    def sleep(seconds):
+        # sleep is not required if ndn-cxx is using in-memory keychain
+        if not Minindn.ndnSecurityDisabled:
+            time.sleep(seconds)
diff --git a/minindn/util.py b/minindn/util.py
new file mode 100644
index 0000000..dd46557
--- /dev/null
+++ b/minindn/util.py
@@ -0,0 +1,77 @@
+# -*- Mode:python; c-file-style:"gnu"; indent-tabs-mode:nil -*- */
+#
+# Copyright (C) 2015-2019, The University of Memphis,
+#                          Arizona Board of Regents,
+#                          Regents of the University of California.
+#
+# This file is part of Mini-NDN.
+# See AUTHORS.md for a complete list of Mini-NDN authors and contributors.
+#
+# Mini-NDN is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Mini-NDN is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with Mini-NDN, e.g., in COPYING.md file.
+# If not, see <http://www.gnu.org/licenses/>.
+
+import sys
+from os.path import isfile
+from subprocess import call
+from mininet.cli import CLI
+
+sshbase = ['ssh', '-q', '-t', '-i/home/mininet/.ssh/id_rsa']
+scpbase = ['scp', '-i', '/home/mininet/.ssh/id_rsa']
+devnull = open('/dev/null', 'w')
+
+def ssh(login, cmd):
+    rcmd = sshbase + [login, cmd]
+    call(rcmd, stdout=devnull, stderr=devnull)
+
+def scp(*args):
+    tmp = []
+    for arg in args:
+        tmp.append(arg)
+    rcmd = scpbase + tmp
+    call(rcmd, stdout=devnull, stderr=devnull)
+
+def copyExistentFile(node, fileList, destination):
+    for f in fileList:
+        if isfile(f):
+            node.cmd('cp {} {}'.format(f, destination))
+            break
+    if not isfile(destination):
+        fileName = destination.split('/')[-1]
+        raise IOError('{} not found in expected directory.'.format(fileName))
+
+def popenGetEnv(node, envDict=None):
+    env = {}
+    homeDir = node.params['params']['homeDir']
+    printenv = node.popen('printenv'.split(), cwd=homeDir).communicate()[0].decode('utf-8')
+    for var in printenv.split('\n'):
+        if var == '':
+            break
+        p = var.split('=')
+        env[p[0]] = p[1]
+    env['HOME'] = homeDir
+
+    if envDict is not None:
+        for key, value in envDict.items():
+            env[key] = str(value)
+
+    return env
+
+def getPopen(host, cmd, envDict=None, **params):
+    return host.popen(cmd, cwd=host.params['params']['homeDir'],
+                      env=popenGetEnv(host, envDict), **params)
+
+class MiniNDNCLI(CLI):
+    prompt = 'mini-ndn> '
+    def __init__(self, mininet, stdin=sys.stdin, script=None):
+        CLI.__init__(self, mininet, stdin, script)
diff --git a/ndn/__init__.py b/ndn/__init__.py
deleted file mode 100644
index e497565..0000000
--- a/ndn/__init__.py
+++ /dev/null
@@ -1,24 +0,0 @@
-# -*- Mode:python; c-file-style:"gnu"; indent-tabs-mode:nil -*- */
-#
-# Copyright (C) 2015-2018, The University of Memphis,
-#                          Arizona Board of Regents,
-#                          Regents of the University of California.
-#
-# This file is part of Mini-NDN.
-# See AUTHORS.md for a complete list of Mini-NDN authors and contributors.
-#
-# Mini-NDN is free software: you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# Mini-NDN is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with Mini-NDN, e.g., in COPYING.md file.
-# If not, see <http://www.gnu.org/licenses/>.
-
-import experiment_manager as ExperimentManager
diff --git a/ndn/apps/ndn_global_routing_helper.py b/ndn/apps/ndn_global_routing_helper.py
deleted file mode 100644
index b05d697..0000000
--- a/ndn/apps/ndn_global_routing_helper.py
+++ /dev/null
@@ -1,127 +0,0 @@
- # -*- Mode:python; c-file-style:"gnu"; indent-tabs-mode:nil -*- */
-#
-# Copyright (C) 2015-2019, The University of Memphis
-#
-# This file is part of Mini-NDN.
-# See AUTHORS.md for a complete list of Mini-NDN authors and contributors.
-#
-# Mini-NDN is free software: you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# Mini-NDN is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with Mini-NDN, e.g., in COPYING.md file.
-# If not, see <http://www.gnu.org/licenses/>.
-
-# IMPORTANT! This feature is in highly experimental phase and may go several changes
-# in future
-
-from mininet.log import info
-from ndn.apps.nfdc import Nfdc as nfdc
-from collections import defaultdict
-from ndn.apps.calculate_routing import CalculateRoutes
-from mininet.log import warn
-
-class GlobalRoutingHelper():
-    """
-    This module is a helper class which helps to create face and register routes
-    to NFD from a given node to all of its neighbors.
-
-    :param NetObject netObject: Mininet net object
-    :param FaceType faceType: UDP, Ethernet etc.
-    :param Routing routingType: (optional) Routing algorithm, link-state or hr etc
-
-    """
-    def __init__(self, netObject, faceType=nfdc.PROTOCOL_UDP, routingType="link-state"):
-        self.net = netObject
-        self.faceType = faceType
-        self.routingType = routingType
-        self.routes = []
-        self.namePrefixes = {host_name.name: [] for host_name in self.net.hosts}
-        self.routeObject = CalculateRoutes(self.net, self.routingType)
-
-    def globalRoutingHelperHandler(self):
-        for host in self.net.hosts:
-            neighborIPs = self.getNeighbor(host)
-            self.createFaces(host, neighborIPs)
-            self.routeAdd(host, neighborIPs)
-
-        info('Processed all the routes to NFD\n')
-
-    def addOrigin(self, nodes, prefix):
-        """
-        Add prefix/s as origin on node/s
-
-        :param Prefix prefix: Prefix that is originated by node/s (as producer) for this prefix
-        :param Nodes nodes: List of nodes from net object
-        """
-        for node in nodes:
-            self.namePrefixes[node.name] = prefix
-
-    def calculateNPossibleRoutes(self, nFaces=0):
-        """
-        By default, calculates all possible routes i.e. routes via all the faces of a node.
-        pass nFaces if want to compute routes via n number of faces. e.g. 2. For larger topology
-        the computation might take huge amount of time.
-
-        :param int nFaces: (optional) number of faces to consider while computing routes. Default
-          i.e. nFaces = 0 will compute all possible routes
-
-        """
-        self.routes = self.routeObject.getRoutes(nFaces)
-        if self.routes:
-            self.globalRoutingHelperHandler()
-        else:
-            warn("Route computation failed\n")
-
-    def calculateRoutes(self):
-        # Calculate shortest path for every node
-        calculateNPossibleRoutes(self, nFaces=1)
-
-    def createFaces(self, node, neighborIPs):
-        for ip in neighborIPs.values():
-            nfdc.createFace(node, ip, self.faceType)
-
-    def routeAdd(self, node, neighborIPs):
-        """
-        Add route from a node to its neighbors for each prefix/s  advertised by destination node
-
-        :param Node node: source node (Mininet net.host)
-        :param IP neighborIPs: IP addresses of neighbors
-        """
-        neighbors = self.routes[node.name]
-        for route in neighbors:
-            destination = route[0]
-            cost = int(route[1])
-            nextHop = route[2]
-            defaultPrefix = "/ndn/{}-site/{}".format(destination, destination)
-            prefixes = [defaultPrefix] + self.namePrefixes[destination]
-            for prefix in prefixes:
-                # Register routes to all the available destination name prefix/s
-                nfdc.registerRoute(node, prefix, neighborIPs[nextHop], \
-                                   nfdc.PROTOCOL_UDP, cost=cost)
-    @staticmethod
-    def getNeighbor(node):
-        # Nodes to IP mapping
-        neighborIPs = defaultdict()
-        for intf in node.intfList():
-            link = intf.link
-            if link:
-                node1, node2 = link.intf1.node, link.intf2.node
-
-                if node1 == node:
-                    other = node2
-                    ip = other.IP(str(link.intf2))
-                else:
-                    other = node1
-                    ip = other.IP(str(link.intf1))
-
-                # Used later to create faces
-                neighborIPs[other.name] = ip
-        return neighborIPs
\ No newline at end of file
diff --git a/ndn/apps/nlsr.py b/ndn/apps/nlsr.py
deleted file mode 100644
index 79a5fb6..0000000
--- a/ndn/apps/nlsr.py
+++ /dev/null
@@ -1,242 +0,0 @@
-# -*- Mode:python; c-file-style:"gnu"; indent-tabs-mode:nil -*- */
-#
-# Copyright (C) 2015-2018, The University of Memphis,
-#                          Arizona Board of Regents,
-#                          Regents of the University of California.
-#
-# This file is part of Mini-NDN.
-# See AUTHORS.md for a complete list of Mini-NDN authors and contributors.
-#
-# Mini-NDN is free software: you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# Mini-NDN is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with Mini-NDN, e.g., in COPYING.md file.
-# If not, see <http://www.gnu.org/licenses/>.
-
-from mininet.clean import sh
-from mininet.examples.cluster import RemoteMixin
-from mininet.log import info
-
-from ndn.ndn_application import NdnApplication
-from ndn.util import ssh, scp, copyExistentFile
-from ndn.apps.nfdc import Nfdc
-
-import shutil
-import os
-import textwrap
-from subprocess import call
-import time
-
-NETWORK="/ndn/"
-
-class Nlsr(NdnApplication):
-    def __init__(self, node, options):
-        NdnApplication.__init__(self, node)
-        self.config = NlsrConfigGenerator(node, options)
-
-        self.node = node
-        self.routerName = "/{}C1.Router/cs/{}".format('%', node.name)
-        self.confFile = "{}/nlsr.conf".format(node.homeFolder)
-
-        # Make directory for log file
-        self.logDir = "{}/log".format(node.homeFolder)
-        self.node.cmd("mkdir {}".format(self.logDir))
-
-    def start(self, sleepTime = 1):
-        self.node.cmd("export NDN_LOG=nlsr.*={}".format(self.node.params["params"].get("nlsr-log-level", "DEBUG")))
-        NdnApplication.start(self, "nlsr -f {} > log/nlsr.log 2>&1 &".format(self.confFile))
-        time.sleep(sleepTime)
-
-    def createFaces(self):
-        for ip in self.config.neighborIPs:
-            Nfdc.createFace(self.node, ip, self.config.faceType, isPermanent=True)
-
-    @staticmethod
-    def createKey(host, name, outputFile):
-        host.cmd("ndnsec-keygen {} > {}".format(name, outputFile))
-
-    @staticmethod
-    def createCertificate(host, signer, keyFile, outputFile):
-        host.cmd("ndnsec-certgen -s {} -r {} > {}".format(signer, keyFile, outputFile))
-
-    @staticmethod
-    def createKeysAndCertificates(net, workDir):
-        securityDir = "{}/security".format(workDir)
-
-        if not os.path.exists(securityDir):
-            os.mkdir(securityDir)
-
-        # Create root certificate
-        rootName = NETWORK
-        sh("ndnsec-keygen {}".format(rootName)) # Installs a self-signed cert into the system
-        sh("ndnsec-cert-dump -i {} > {}/root.cert".format(rootName, securityDir))
-
-        # Create necessary certificates for each site
-        for host in net.hosts:
-            nodeSecurityFolder = "{}/security".format(host.homeFolder)
-
-            host.cmd("mkdir -p %s" % nodeSecurityFolder)
-
-            # Create temp folders for remote nodes on this machine (localhost) to store site.key file
-            # from RemoteNodes
-            if not os.path.exists(nodeSecurityFolder) and isinstance(host, RemoteMixin) and host.isRemote:
-                os.makedirs(nodeSecurityFolder)
-
-            shutil.copyfile("{}/root.cert".format(securityDir), "{}/root.cert".format(nodeSecurityFolder))
-
-            # Create site certificate
-            siteName = "{}{}-site".format(NETWORK, host.name)
-            siteKeyFile = "{}/site.keys".format(nodeSecurityFolder)
-            siteCertFile = "{}/site.cert".format(nodeSecurityFolder)
-            Nlsr.createKey(host, siteName, siteKeyFile)
-
-            # Copy siteKeyFile from remote for ndnsec-certgen
-            if isinstance(host, RemoteMixin) and host.isRemote:
-                login = "mininet@{}".format(host.server)
-                src = "{}:{}".format(login, siteKeyFile)
-                dst = siteKeyFile
-                scp(src, dst)
-
-            # Root key is in root namespace, must sign site key and then install on host
-            sh("ndnsec-certgen -s {} -r {} > {}".format(rootName, siteKeyFile, siteCertFile))
-
-            # Copy root.cert and site.cert from localhost to remote host
-            if isinstance(host, RemoteMixin) and host.isRemote:
-                login = "mininet@{}".format(host.server)
-                src = "{}/site.cert".format(nodeSecurityFolder)
-                src2 = "{}/root.cert".format(nodeSecurityFolder)
-                dst = "{}:/tmp/".format(login)
-                scp(src, src2, dst)
-                host.cmd("mv /tmp/*.cert {}".format(nodeSecurityFolder))
-
-            host.cmd("ndnsec-cert-install -f {}".format(siteCertFile))
-
-            # Create and install operator certificate
-            opName = "{}/%C1.Operator/op".format(siteName)
-            opKeyFile = "{}/op.keys".format(nodeSecurityFolder)
-            opCertFile = "{}/op.cert".format(nodeSecurityFolder)
-            Nlsr.createKey(host, opName, opKeyFile)
-            Nlsr.createCertificate(host, siteName, opKeyFile, opCertFile)
-            host.cmd("ndnsec-cert-install -f {}".format(opCertFile))
-
-            # Create and install router certificate
-            routerName = "{}/%C1.Router/cs/{}".format(siteName, host.name)
-            routerKeyFile = "{}/router.keys".format(nodeSecurityFolder)
-            routerCertFile = "{}/router.cert".format(nodeSecurityFolder)
-            Nlsr.createKey(host, routerName, routerKeyFile)
-            Nlsr.createCertificate(host, opName, routerKeyFile, routerCertFile)
-            host.cmd("ndnsec-cert-install -f {}".format(routerCertFile))
-
-class NlsrConfigGenerator:
-
-    ROUTING_LINK_STATE = "ls"
-    ROUTING_HYPERBOLIC = "hr"
-
-    def __init__(self, node, options):
-        self.node = node
-        self.isSecurityEnabled = options.nlsrSecurity
-        self.faceType = options.faceType
-        self.infocmd = "infoedit -f nlsr.conf"
-
-        parameters = node.params["params"]
-
-        self.nFaces = options.nFaces
-        if options.routingType == "hr":
-            self.hyperbolicState = "on"
-        elif options.routingType == "dry":
-            self.hyperbolicState = "dry-run"
-        else:
-            self.hyperbolicState = "off"
-        self.hyperRadius = parameters.get("radius", 0.0)
-        self.hyperAngle = parameters.get("angle", 0.0)
-
-        if ((self.hyperbolicState == "on" or self.hyperbolicState == "dry-run") and
-            (self.hyperRadius == 0.0 or self.hyperAngle == 0.0)):
-                info('Hyperbolic coordinates in topology file are either missing or misconfigured.')
-                info('Check that each node has one radius value and one or two angle value(s).')
-                sys.exit(1)
-
-        self.neighborIPs = []
-        possibleConfPaths = ["/usr/local/etc/ndn/nlsr.conf.sample", "/etc/ndn/nlsr.conf.sample"]
-        copyExistentFile(node, possibleConfPaths, "{}/nlsr.conf".format(self.node.homeFolder))
-
-        self.createConfigFile()
-
-    def createConfigFile(self):
-
-        self.__editGeneralSection()
-        self.__editNeighborsSection()
-        self.__editHyperbolicSection()
-        self.__editFibSection()
-        self.__editAdvertisingSection()
-        self.__editSecuritySection()
-
-    def __editGeneralSection(self):
-
-        self.node.cmd("{} -s general.network -v {}".format(self.infocmd, NETWORK))
-        self.node.cmd("{} -s general.site -v /{}-site".format(self.infocmd, self.node.name))
-        self.node.cmd("{} -s general.router -v /%C1.Router/cs/{}".format(self.infocmd, self.node.name))
-        self.node.cmd("{} -s general.state-dir -v {}/log".format(self.infocmd, self.node.homeFolder))
-
-    def __editNeighborsSection(self):
-
-        self.node.cmd("{} -d neighbors.neighbor".format(self.infocmd))
-        for intf in self.node.intfList():
-            link = intf.link
-            if link:
-                node1, node2 = link.intf1.node, link.intf2.node
-
-                if node1 == self.node:
-                    other = node2
-                    ip = other.IP(str(link.intf2))
-                else:
-                    other = node1
-                    ip = other.IP(str(link.intf1))
-
-                linkCost = intf.params.get("delay", "10ms").replace("ms", "")
-
-                Nfdc.createFace(self.node, ip, self.faceType, isPermanent=True)
-                self.neighborIPs.append(ip)
-
-                self.node.cmd("{} -a neighbors.neighbor \
-                              <<<\'name {}{}-site/%C1.Router/cs/{} face-uri {}://{}\n link-cost {}\'"
-                              .format(self.infocmd, NETWORK, other.name, other.name, self.faceType, ip, linkCost))
-
-    def __editHyperbolicSection(self):
-
-        self.node.cmd("{} -s hyperbolic.state -v {}".format(self.infocmd, self.hyperbolicState))
-        self.node.cmd("{} -s hyperbolic.radius -v {}".format(self.infocmd, self.hyperRadius))
-        self.node.cmd("{} -s hyperbolic.angle -v {}".format(self.infocmd, self.hyperAngle))
-
-    def __editFibSection(self):
-
-        self.node.cmd("{} -s fib.max-faces-per-prefix  -v {}".format(self.infocmd, self.nFaces))
-
-    def __editAdvertisingSection(self):
-
-        self.node.cmd("{} -d advertising.prefix".format(self.infocmd))
-        self.node.cmd("{} -s advertising.prefix -v {}{}-site/{}"
-                      .format(self.infocmd, NETWORK, self.node.name, self.node.name))
-
-    def __editSecuritySection(self):
-
-        self.node.cmd("{} -d security.cert-to-publish".format(self.infocmd))
-        if self.isSecurityEnabled is False:
-            self.node.cmd("{} -s security.validator.trust-anchor.type -v any".format(self.infocmd))
-            self.node.cmd("{} -d security.validator.trust-anchor.file-name".format(self.infocmd))
-            self.node.cmd("{} -s security.prefix-update-validator.trust-anchor.type -v any".format(self.infocmd))
-            self.node.cmd("{} -d security.prefix-update-validator.trust-anchor.file-name".format(self.infocmd))
-        else:
-            self.node.cmd("{} -s security.validator.trust-anchor.file-name -v security/root.cert".format(self.infocmd))
-            self.node.cmd("{} -s security.prefix-update-validator.trust-anchor.file-name -v security/site.cert".format(self.infocmd))
-            self.node.cmd("{} -p security.cert-to-publish -v security/site.cert".format(self.infocmd))
-            self.node.cmd("{} -p security.cert-to-publish -v security/op.cert".format(self.infocmd))
-            self.node.cmd("{} -p security.cert-to-publish -v security/router.cert".format(self.infocmd))
diff --git a/ndn/conf_parser.py b/ndn/conf_parser.py
deleted file mode 100644
index b24435a..0000000
--- a/ndn/conf_parser.py
+++ /dev/null
@@ -1,206 +0,0 @@
-# -*- Mode:python; c-file-style:"gnu"; indent-tabs-mode:nil -*- */
-#
-# Copyright (C) 2015-2019, The University of Memphis,
-#                          Arizona Board of Regents,
-#                          Regents of the University of California.
-#
-# This file is part of Mini-NDN.
-# See AUTHORS.md for a complete list of Mini-NDN authors and contributors.
-#
-# Mini-NDN is free software: you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# Mini-NDN is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with Mini-NDN, e.g., in COPYING.md file.
-# If not, see <http://www.gnu.org/licenses/>.
-#
-# This file incorporates work covered by the following copyright and
-# permission notice:
-#
-#   Mininet 2.2.1 License
-#
-#   Copyright (c) 2013-2015 Open Networking Laboratory
-#   Copyright (c) 2009-2012 Bob Lantz and The Board of Trustees of
-#   The Leland Stanford Junior University
-#
-#   Original authors: Bob Lantz and Brandon Heller
-#
-#   We are making Mininet available for public use and benefit with the
-#   expectation that others will use, modify and enhance the Software and
-#   contribute those enhancements back to the community. However, since we
-#   would like to make the Software available for broadest use, with as few
-#   restrictions as possible permission is hereby granted, free of charge, to
-#   any person obtaining a copy of this Software to deal in the Software
-#   under the copyrights without restriction, including without limitation
-#   the rights to use, copy, modify, merge, publish, distribute, sublicense,
-#   and/or sell copies of the Software, and to permit persons to whom the
-#   Software is furnished to do so, subject to the following conditions:
-#
-#   The above copyright notice and this permission notice shall be included
-#   in all copies or substantial portions of the Software.
-#
-#   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
-#   OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
-#   MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
-#   IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
-#   CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
-#   TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
-#   SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-#
-#   The name and trademarks of copyright holder(s) may NOT be used in
-#   advertising or publicity pertaining to the Software or any derivatives
-#   without specific, written prior permission.
-
-import ConfigParser, re
-import shlex
-import sys
-from mininet.log import error
-
-class confNDNHost():
-
-    def __init__(self, name, app='', params='', cpu=None, cores=None, cache=None):
-        self.name = name
-        self.app = app
-        self.params = params
-        self.cpu = cpu
-        self.cores = cores
-        self.cache = cache
-
-    def __repr__(self):
-        return " Name: {} App: {} Params: {} CPU: {} Cores: {} Cores: {} Cache: {}" \
-               .format(self.name, self.app, self.params, self.cpu, self.cores, self.cache)
-
-class confNdnSwitch:
-    def __init__(self, name):
-        self.name = name
-
-class confNDNLink():
-
-    def __init__(self,h1,h2,linkDict=None):
-        self.h1 = h1
-        self.h2 = h2
-        self.linkDict = linkDict
-
-    def __repr__(self):
-        return "h1: {} h2: {} params: {}".format(self.h1, self.h2, self.linkDict)
-
-def parse_hosts(conf_arq):
-    'Parse hosts section from the conf file.'
-    config = ConfigParser.RawConfigParser()
-    config.read(conf_arq)
-
-    hosts = []
-
-    items = config.items('nodes')
-
-    # makes a first-pass read to hosts section to find empty host sections
-    coordinates = []
-    for item in items:
-        name = item[0]
-        rest = item[1].split()
-        # check for the duplicate coordinates
-        if "radius" in item[1]:
-            if item[1] in coordinates:
-                error("FATAL: Duplicate Coordinate, \"{}\" used by multiple nodes\n" \
-                      .format(item[1]))
-                sys.exit(1)
-            else:
-                coordinates.append(item[1])
-        if len(rest) == 0:
-            config.set('nodes', name, '_')
-    # updates 'items' list
-    items = config.items('nodes')
-
-    # makes a second-pass read to hosts section to properly add hosts
-    for item in items:
-
-        name = item[0]
-
-        rest = shlex.split(item[1])
-
-        uris = rest
-        params = {}
-        cpu = None
-        cores = None
-        cache = None
-
-        for uri in uris:
-            if re.match("cpu",uri):
-                cpu = float(uri.split('=')[1])
-            elif re.match("cores",uri):
-                cores = uri.split('=')[1]
-            elif re.match("cache",uri):
-                cache = uri.split('=')[1]
-            elif re.match("mem",uri):
-                mem = uri.split('=')[1]
-            elif re.match("app",uri):
-                app = uri.split('=')[1]
-            elif re.match("_", uri):
-                app = ""
-            else:
-                params[uri.split('=')[0]] = uri.split('=')[1]
-
-        hosts.append(confNDNHost(name, app, params, cpu, cores, cache))
-
-    return hosts
-
-def parse_switches(conf_arq):
-    'Parse switches section from the conf file.'
-    config = ConfigParser.RawConfigParser()
-    config.read(conf_arq)
-
-    switches = []
-
-    try:
-        items = config.items('switches')
-    except ConfigParser.NoSectionError:
-        return switches
-
-    for item in items:
-        name = item[0]
-        switches.append(confNdnSwitch(name))
-
-    return switches
-
-def parse_links(conf_arq):
-    'Parse links section from the conf file.'
-    arq = open(conf_arq, 'r')
-
-    links = []
-    linkSectionFlag = False
-
-    for line in arq:
-        if linkSectionFlag:
-            args = line.split()
-
-            # checks for non-empty line
-            if len(args) == 0:
-                continue
-
-            h1, h2 = args.pop(0).split(':')
-
-            link_dict = {}
-
-            for arg in args:
-                arg_name, arg_value = arg.split('=')
-                key = arg_name
-                value = arg_value
-                if key in ['bw','jitter','max_queue_size']:
-                    value = int(value)
-                if key in ['loss']:
-                    value = float(value)
-                link_dict[key] = value
-
-            links.append(confNDNLink(h1,h2,link_dict))
-
-        elif line == "[links]\n":
-            linkSectionFlag = True
-
-    return links
diff --git a/ndn/experiment_manager.py b/ndn/experiment_manager.py
deleted file mode 100644
index 0be9f92..0000000
--- a/ndn/experiment_manager.py
+++ /dev/null
@@ -1,97 +0,0 @@
-# -*- Mode:python; c-file-style:"gnu"; indent-tabs-mode:nil -*- */
-#
-# Copyright (C) 2015-2018, The University of Memphis,
-#                          Arizona Board of Regents,
-#                          Regents of the University of California.
-#
-# This file is part of Mini-NDN.
-# See AUTHORS.md for a complete list of Mini-NDN authors and contributors.
-#
-# Mini-NDN is free software: you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# Mini-NDN is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with Mini-NDN, e.g., in COPYING.md file.
-# If not, see <http://www.gnu.org/licenses/>.
-
-import os
-
-class _ExperimentManager:
-
-    class Error(Exception):
-        def __init__(self, what):
-            self.what = what
-        def __str__(self):
-            return repr(self.what)
-
-    instance = None
-
-    def __init__(self):
-        self.experiments = {}
-
-    def loadModules(self):
-        currentDir = os.path.dirname(__file__)
-        experimentDir = "{}/{}".format(currentDir, "experiments")
-        experimentModule = "ndn.experiments"
-
-        # Import and register experiments
-        for root, dirs, files in os.walk(experimentDir):
-            for filename in files:
-                if filename.endswith(".py") and filename != "__init__.py":
-                    module = filename.replace(".py", "")
-                    subdir = os.path.basename(root)
-                    if subdir == "experiments":
-                        __import__("{}.{}".format(experimentModule, module))
-                    else:
-                        __import__("{}.{}.{}".format(experimentModule, subdir, module))
-
-    def register(self, name, experimentClass):
-        if name not in self.experiments:
-            self.experiments[name] = experimentClass
-        else:
-            raise _ExperimentManager.Error("Experiment '{}' has already been registered".format(name))
-
-    def create(self, name, args):
-        if name in self.experiments:
-            return self.experiments[name](args)
-        else:
-            return None
-
-def __getInstance():
-    if _ExperimentManager.instance is None:
-        _ExperimentManager.instance = _ExperimentManager()
-        _ExperimentManager.instance.loadModules()
-
-    return _ExperimentManager.instance
-
-def register(name, experimentClass):
-    manager = __getInstance()
-    manager.register(name, experimentClass)
-
-def create(name, args):
-    manager = __getInstance()
-    return manager.create(name, args)
-
-def getExperimentNames():
-    manager = __getInstance()
-
-    experimentNames = []
-
-    for key in manager.experiments:
-        experimentNames.append(key)
-
-    return experimentNames
-
-def addExperimentArgs(parser):
-    # Find all experiment command line arguments and parse them.
-    manager = __getInstance()
-    for name in manager.experiments:
-        if hasattr(manager.experiments[name], "parseArguments"):
-            manager.experiments[name].parseArguments(parser)
\ No newline at end of file
diff --git a/ndn/experiments/__init__.py b/ndn/experiments/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/ndn/experiments/__init__.py
+++ /dev/null
diff --git a/ndn/experiments/arguments_experiment.py b/ndn/experiments/arguments_experiment.py
deleted file mode 100644
index 6baacd5..0000000
--- a/ndn/experiments/arguments_experiment.py
+++ /dev/null
@@ -1,49 +0,0 @@
-# -*- Mode:python; c-file-style:"gnu"; indent-tabs-mode:nil -*- */
-#
-# Copyright (C) 2015-2018, The University of Memphis,
-#                          Arizona Board of Regents,
-#                          Regents of the University of California.
-#
-# This file is part of Mini-NDN.
-# See AUTHORS.md for a complete list of Mini-NDN authors and contributors.
-#
-# Mini-NDN is free software: you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# Mini-NDN is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with Mini-NDN, e.g., in COPYING.md file.
-# If not, see <http://www.gnu.org/licenses/>.
-
-from ndn.experiments.experiment import Experiment
-
-class ArgumentsExperiment(Experiment):
-    def __init__(self, args):
-        Experiment.__init__(self, args)
-        self.ds = self.options.arguments.ds
-        self.logging = self.options.arguments.logging
-
-    def start(self):
-        pass
-
-    def setup(self):
-        pass
-
-    def run(self):
-        print("Argument ds: {}".format(self.ds))
-        print("Argument logging: {}".format(self.logging))
-
-    @staticmethod
-    def parseArguments(parser):
-        parser.add_argument("--ds", dest="ds", default="1000",
-                            help="[Arguments Experiment] Number of data streams")
-        parser.add_argument("--logging", dest="logging", action="store_true",
-                            help="[Arguments Experiment] Enable logging")
-
-Experiment.register("args-exp", ArgumentsExperiment)
\ No newline at end of file
diff --git a/ndn/experiments/experiment.py b/ndn/experiments/experiment.py
deleted file mode 100644
index 8a2f49c..0000000
--- a/ndn/experiments/experiment.py
+++ /dev/null
@@ -1,172 +0,0 @@
-# -*- Mode:python; c-file-style:"gnu"; indent-tabs-mode:nil -*- */
-#
-# Copyright (C) 2015-2018, The University of Memphis,
-#                          Arizona Board of Regents,
-#                          Regents of the University of California.
-#
-# This file is part of Mini-NDN.
-# See AUTHORS.md for a complete list of Mini-NDN authors and contributors.
-#
-# Mini-NDN is free software: you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# Mini-NDN is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with Mini-NDN, e.g., in COPYING.md file.
-# If not, see <http://www.gnu.org/licenses/>.
-
-import time
-import sys
-from itertools import cycle
-
-from mininet.log import info
-
-from ndn import ExperimentManager
-from ndn.apps.nfdc import Nfdc
-
-from ndn.apps.nlsr import Nlsr, NlsrConfigGenerator
-from ndn.apps.ndn_ping_client import NDNPingClient
-
-class Experiment:
-
-    def __init__(self, args):
-        self.net = args["net"]
-        self.options = args["options"]
-
-        # Used to restart pings on the recovered node if any
-        self.pingedDict = {}
-
-    def afterNfdStart(self):
-        pass
-
-    def start(self):
-        self.afterNfdStart()
-        if self.options.isNlsrEnabled is True:
-            self.startNlsr()
-        self.setup()
-        self.run()
-
-    def setup(self):
-        for host in self.net.hosts:
-            # Set strategy
-            Nfdc.setStrategy(host, "/ndn/", self.options.strategy)
-
-            # Start ping server
-            host.cmd("ndnpingserver /ndn/{}-site/{} > ping-server &".format(host.name, host.name))
-
-            # Create folder to store ping data
-            host.cmd("mkdir ping-data")
-
-    def startNlsr(self, checkConvergence = True):
-        # NLSR Security
-        if self.options.nlsrSecurity is True:
-            Nlsr.createKeysAndCertificates(self.net, self.options.workDir)
-
-        # NLSR initialization
-        info('Starting NLSR on nodes\n')
-        for host in self.net.hosts:
-            host.nlsr = Nlsr(host, self.options)
-            host.nlsr.start()
-
-        for host in self.net.hosts:
-            nlsrStatus = host.cmd("ps -g | grep 'nlsr -f {}/[n]lsr.conf'".format(host.homeFolder))
-            if not host.nlsr.isRunning or not nlsrStatus:
-                print("NLSR on host {} is not running. Printing log file and exiting...".format(host.name))
-                print(host.cmd("tail {}/log/nlsr.log".format(host.homeFolder)))
-                self.net.stop()
-                sys.exit(1)
-
-        if checkConvergence:
-            self.checkConvergence()
-
-    def checkConvergence(self, convergenceTime = None):
-        if convergenceTime is None:
-            convergenceTime = self.options.ctime
-
-        # Wait for convergence time period
-        print "Waiting " + str(convergenceTime) + " seconds for convergence..."
-        time.sleep(convergenceTime)
-        print "...done"
-
-        # To check whether all the nodes of NLSR have converged
-        didNlsrConverge = True
-
-        # Checking for convergence
-        for host in self.net.hosts:
-            statusRouter = host.cmd("nfdc fib list | grep site/%C1.Router/cs/")
-            statusPrefix = host.cmd("nfdc fib list | grep ndn | grep site | grep -v Router")
-            didNodeConverge = True
-            for node in self.net.hosts:
-                # Node has its own router name in the fib list, but not name prefix
-                if ( ("/ndn/{}-site/%C1.Router/cs/{}".format(node.name, node.name)) not in statusRouter or
-                      host.name != node.name and ("/ndn/{}-site/{}".format(node.name, node.name)) not in statusPrefix ):
-                    didNodeConverge = False
-                    didNlsrConverge = False
-
-            host.cmd("echo " + str(didNodeConverge) + " > convergence-result &")
-
-        if didNlsrConverge:
-            print("NLSR has successfully converged.")
-        else:
-            print("NLSR has not converged. Exiting...")
-            self.net.stop()
-            sys.exit(1)
-
-    def startPings(self):
-        for host in self.net.hosts:
-            for other in self.net.hosts:
-                # Do not ping self
-                if host.name != other.name:
-                    NDNPingClient.ping(host, other, self.options.nPings)
-
-    def failNode(self, host):
-        print("Bringing {} down".format(host.name))
-        host.nfd.stop()
-
-    def recoverNode(self, host):
-        print("Bringing {} up".format(host.name))
-        host.nfd.start()
-        host.nlsr.createFaces()
-        host.nlsr.start()
-        Nfdc.setStrategy(host, "/ndn/", self.options.strategy)
-        host.cmd("ndnpingserver /ndn/{}-site/{} > ping-server &".format(host.name, host.name))
-
-    def startPctPings(self):
-        nNodesToPing = int(round(len(self.net.hosts) * self.options.pctTraffic))
-        print "Each node will ping {} node(s)".format(nNodesToPing)
-        # Temporarily store all the nodes being pinged by a particular node
-        nodesPingedList = []
-
-        for host in self.net.hosts:
-            # Create a circular list
-            pool = cycle(self.net.hosts)
-
-            # Move iterator to current node
-            next(x for x in pool if host.name == x.name)
-
-            # Track number of nodes to ping scheduled for this node
-            nNodesScheduled = 0
-
-            while nNodesScheduled < nNodesToPing:
-                other = pool.next()
-
-                # Do not ping self
-                if host.name != other.name:
-                    NDNPingClient.ping(host, other, self.options.nPings)
-                    nodesPingedList.append(other)
-
-                # Always increment because in 100% case a node should not ping itself
-                nNodesScheduled = nNodesScheduled + 1
-
-            self.pingedDict[host] = nodesPingedList
-            nodesPingedList = []
-
-    @staticmethod
-    def register(name, experimentClass):
-        ExperimentManager.register(name, experimentClass)
diff --git a/ndn/experiments/nlsr/__init__.py b/ndn/experiments/nlsr/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/ndn/experiments/nlsr/__init__.py
+++ /dev/null
diff --git a/ndn/experiments/nlsr/advertise-delayed-start.py b/ndn/experiments/nlsr/advertise-delayed-start.py
deleted file mode 100644
index cbd8ac8..0000000
--- a/ndn/experiments/nlsr/advertise-delayed-start.py
+++ /dev/null
@@ -1,73 +0,0 @@
-# -*- Mode:python; c-file-style:"gnu"; indent-tabs-mode:nil -*- */
-#
-# Copyright (C) 2015-2018, The University of Memphis,
-#                          Arizona Board of Regents,
-#                          Regents of the University of California.
-#
-# This file is part of Mini-NDN.
-# See AUTHORS.md for a complete list of Mini-NDN authors and contributors.
-#
-# Mini-NDN is free software: you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# Mini-NDN is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with Mini-NDN, e.g., in COPYING.md file.
-# If not, see <http://www.gnu.org/licenses/>.
-
-from ndn.experiments.experiment import Experiment
-from ndn.apps.nlsr import Nlsr, NlsrConfigGenerator
-
-from mininet.log import info
-
-import time, sys
-
-class AdvertiseDelayedStartExperiment(Experiment):
-    '''Tests Name LSA data segmentation'''
-
-    def __init__(self, args):
-        Experiment.__init__(self, args)
-
-    def setup(self):
-        pass
-
-    def run(self):
-        pass
-
-    def startNlsr(self, checkConvergence = True):
-        # NLSR Security
-        if self.options.nlsrSecurity is True:
-            Nlsr.createKeysAndCertificates(self.net, self.options.workDir)
-
-        host1 = self.net.hosts[0]
-        host1.nlsr = Nlsr(host1, self.options)
-        host1.nlsr.start()
-
-        expectedTotalCount = 500
-        for i in range(0, expectedTotalCount):
-            host1.cmd("nlsrc advertise /long/name/to/exceed/max/packet/size/host1/{}".format(i))
-
-        time.sleep(60)
-
-        host2 = self.net.hosts[1]
-        host2.nlsr = Nlsr(host2, self.options)
-        host2.nlsr.start()
-
-        time.sleep(60)
-
-        advertiseCount = int(host2.cmd("nfdc fib | grep host1 | wc -l"))
-        info(advertiseCount)
-        if advertiseCount == expectedTotalCount:
-            info('\nSuccessfully advertised {} prefixes\n'.format(expectedTotalCount))
-        else:
-            info('\nAdvertising {} prefixes failed. Exiting...\n'.format(expectedTotalCount))
-            self.net.stop()
-            sys.exit(1)
-
-Experiment.register("advertise-delayed-start", AdvertiseDelayedStartExperiment)
diff --git a/ndn/experiments/nlsr/delayed-start.py b/ndn/experiments/nlsr/delayed-start.py
deleted file mode 100644
index e995c50..0000000
--- a/ndn/experiments/nlsr/delayed-start.py
+++ /dev/null
@@ -1,66 +0,0 @@
-# -*- Mode:python; c-file-style:"gnu"; indent-tabs-mode:nil -*- */
-#
-# Copyright (C) 2015-2018, The University of Memphis,
-#                          Arizona Board of Regents,
-#                          Regents of the University of California.
-#
-# This file is part of Mini-NDN.
-# See AUTHORS.md for a complete list of Mini-NDN authors and contributors.
-#
-# Mini-NDN is free software: you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# Mini-NDN is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with Mini-NDN, e.g., in COPYING.md file.
-# If not, see <http://www.gnu.org/licenses/>.
-
-from ndn.experiments.experiment import Experiment
-from ndn.apps.nlsr import Nlsr, NlsrConfigGenerator
-
-from mininet.log import info
-
-import time
-
-class NlsrDelayedStartExperiment(Experiment):
-
-    def __init__(self, args):
-        Experiment.__init__(self, args)
-
-    def setup(self):
-        pass
-
-    def run(self):
-        pass
-
-    def startNlsr(self, checkConvergence = True):
-        # NLSR Security
-        if self.options.nlsrSecurity is True:
-            Nlsr.createKeysAndCertificates(self.net, self.options.workDir)
-
-        i = 1
-        # NLSR initialization
-        info('Starting NLSR on nodes\n')
-        for host in self.net.hosts:
-            host.nlsr = Nlsr(host, self.options)
-            host.nlsr.start()
-
-            # Wait 1/2 minute between starting NLSRs
-            # Wait 1 hour before starting last NLSR
-            if i == len(self.net.hosts) - 1:
-                info('Sleeping 1 hour before starting last NLSR')
-                time.sleep(3600)
-            else:
-                time.sleep(30)
-            i += 1
-
-        if checkConvergence:
-            self.checkConvergence()
-
-Experiment.register("nlsr-delayed-start", NlsrDelayedStartExperiment)
diff --git a/ndn/experiments/nlsr/failure_experiment.py b/ndn/experiments/nlsr/failure_experiment.py
deleted file mode 100644
index 783bac4..0000000
--- a/ndn/experiments/nlsr/failure_experiment.py
+++ /dev/null
@@ -1,65 +0,0 @@
-# -*- Mode:python; c-file-style:"gnu"; indent-tabs-mode:nil -*- */
-#
-# Copyright (C) 2015-2018, The University of Memphis,
-#                          Arizona Board of Regents,
-#                          Regents of the University of California.
-#
-# This file is part of Mini-NDN.
-# See AUTHORS.md for a complete list of Mini-NDN authors and contributors.
-#
-# Mini-NDN is free software: you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# Mini-NDN is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with Mini-NDN, e.g., in COPYING.md file.
-# If not, see <http://www.gnu.org/licenses/>.
-
-from ndn.experiments.experiment import Experiment
-from ndn.apps.ndn_ping_client import NDNPingClient
-
-import time
-
-class FailureExperiment(Experiment):
-
-    def __init__(self, args):
-        args["options"].nPings = 300
-        Experiment.__init__(self, args)
-
-        self.PING_COLLECTION_TIME_BEFORE_FAILURE = 60
-        self.PING_COLLECTION_TIME_AFTER_RECOVERY = 120
-
-    def run(self):
-        self.startPctPings()
-
-        # After the pings are scheduled, collect pings for 1 minute
-        time.sleep(self.PING_COLLECTION_TIME_BEFORE_FAILURE)
-
-        # Bring down CSU
-        for host in self.net.hosts:
-            if host.name == "csu":
-                self.failNode(host)
-                break
-
-        # CSU is down for 2 minutes
-        time.sleep(120)
-
-        # Bring CSU back up
-        for host in self.net.hosts:
-            if host.name == "csu":
-                self.recoverNode(host)
-
-                for other in self.net.hosts:
-                    if host.name != other.name:
-                        NDNPingClient.ping(host, other, self.PING_COLLECTION_TIME_AFTER_RECOVERY)
-
-        # Collect pings for more seconds after CSU is up
-        time.sleep(self.PING_COLLECTION_TIME_AFTER_RECOVERY)
-
-Experiment.register("failure", FailureExperiment)
diff --git a/ndn/experiments/nlsr/mcn_failure_experiment.py b/ndn/experiments/nlsr/mcn_failure_experiment.py
deleted file mode 100644
index cd448cc..0000000
--- a/ndn/experiments/nlsr/mcn_failure_experiment.py
+++ /dev/null
@@ -1,79 +0,0 @@
-# -*- Mode:python; c-file-style:"gnu"; indent-tabs-mode:nil -*- */
-#
-# Copyright (C) 2015-2018, The University of Memphis,
-#                          Arizona Board of Regents,
-#                          Regents of the University of California.
-#
-# This file is part of Mini-NDN.
-# See AUTHORS.md for a complete list of Mini-NDN authors and contributors.
-#
-# Mini-NDN is free software: you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# Mini-NDN is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with Mini-NDN, e.g., in COPYING.md file.
-# If not, see <http://www.gnu.org/licenses/>.
-
-from ndn.experiments.experiment import Experiment
-from ndn.apps.ndn_ping_client import NDNPingClient
-
-import time
-
-class MCNFailureExperiment(Experiment):
-
-    def __init__(self, args):
-        Experiment.__init__(self, args)
-
-        self.PING_COLLECTION_TIME_BEFORE_FAILURE = 60
-        self.PING_COLLECTION_TIME_AFTER_RECOVERY = 120
-
-    def getMostConnectedNode(self):
-        mcn = max(self.net.hosts, key=lambda host: len(host.intfNames()))
-        print "The most connected node is: {}".format(mcn.name)
-        return mcn
-
-    def setup(self):
-        if self.options.nPings != 0:
-            Experiment.setup(self)
-
-    def run(self):
-        mostConnectedNode = self.getMostConnectedNode()
-
-        if self.options.nPings != 0:
-            self.startPctPings()
-
-        # After the pings are scheduled, collect pings for 1 minute
-        time.sleep(self.PING_COLLECTION_TIME_BEFORE_FAILURE)
-
-        # Bring down MCN
-        self.failNode(mostConnectedNode)
-
-        # MCN is down for 2 minutes
-        time.sleep(int(self.options.arguments.waitTime))
-
-        # Bring MCN back up
-        self.recoverNode(mostConnectedNode)
-
-        # Restart pings
-        if self.options.nPings != 0:
-            for nodeToPing in self.pingedDict[mostConnectedNode]:
-                 NDNPingClient.ping(mostConnectedNode, nodeToPing, self.PING_COLLECTION_TIME_AFTER_RECOVERY)
-
-            # Collect pings for more seconds after MCN is up
-            time.sleep(self.PING_COLLECTION_TIME_AFTER_RECOVERY)
-        else:
-            self.checkConvergence()
-
-    @staticmethod
-    def parseArguments(parser):
-        parser.add_argument("--wait-time", dest="waitTime", default="120",
-                            help="[Experiment] Generic wait time for experiment use")
-
-Experiment.register("mcn-failure", MCNFailureExperiment)
diff --git a/ndn/experiments/nlsr/multiple_failure_experiment.py b/ndn/experiments/nlsr/multiple_failure_experiment.py
deleted file mode 100644
index efbba60..0000000
--- a/ndn/experiments/nlsr/multiple_failure_experiment.py
+++ /dev/null
@@ -1,80 +0,0 @@
-# -*- Mode:python; c-file-style:"gnu"; indent-tabs-mode:nil -*- */
-#
-# Copyright (C) 2015-2018, The University of Memphis,
-#                          Arizona Board of Regents,
-#                          Regents of the University of California.
-#
-# This file is part of Mini-NDN.
-# See AUTHORS.md for a complete list of Mini-NDN authors and contributors.
-#
-# Mini-NDN is free software: you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# Mini-NDN is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with Mini-NDN, e.g., in COPYING.md file.
-# If not, see <http://www.gnu.org/licenses/>.
-
-from ndn.experiments.experiment import Experiment
-from ndn.apps.ndn_ping_client import NDNPingClient
-
-import time
-
-class MultipleFailureExperiment(Experiment):
-
-    def __init__(self, args):
-
-        self.PING_COLLECTION_TIME_BEFORE_FAILURE = 60
-
-        self.FAILURE_INTERVAL = 60
-        self.RECOVERY_INTERVAL = 60
-
-        # This is the number of pings required to make it through the full experiment
-        nInitialPings = (self.PING_COLLECTION_TIME_BEFORE_FAILURE +
-                         len(args["net"].hosts) * (self.FAILURE_INTERVAL + self.RECOVERY_INTERVAL))
-        print("Scheduling with {} initial pings".format(nInitialPings))
-
-        Experiment.__init__(self, args)
-        self.options.nPings = nInitialPings
-
-    def run(self):
-        self.startPctPings()
-
-        # After the pings are scheduled, collect pings for 1 minute
-        time.sleep(self.PING_COLLECTION_TIME_BEFORE_FAILURE)
-
-        nNodesRemainingToFail = len(self.net.hosts)
-
-        # Fail and recover each node
-        for host in self.net.hosts:
-            # Fail the node
-            self.failNode(host)
-
-            # Stay in failure state for FAILURE_INTERVAL seconds
-            time.sleep(self.FAILURE_INTERVAL)
-
-            # Bring the node back up
-            start_time = time.time()
-            self.recoverNode(host)
-            recovery_time = int(time.time() - start_time)
-
-            # Number of pings required to reach the end of the test
-            nNodesRemainingToFail -= 1
-            nPings = ((self.RECOVERY_INTERVAL - recovery_time) +
-                      nNodesRemainingToFail*(self.FAILURE_INTERVAL + self.RECOVERY_INTERVAL))
-
-            print("Scheduling with {} remaining pings".format(nPings))
-
-            # Restart pings
-            for nodeToPing in self.pingedDict[host]:
-                NDNPingClient.ping(host, nodeToPing, nPings)
-
-            time.sleep(self.RECOVERY_INTERVAL - recovery_time)
-
-Experiment.register("multiple-failure", MultipleFailureExperiment)
diff --git a/ndn/experiments/nlsr/pingall_experiment.py b/ndn/experiments/nlsr/pingall_experiment.py
deleted file mode 100644
index 56a70bd..0000000
--- a/ndn/experiments/nlsr/pingall_experiment.py
+++ /dev/null
@@ -1,49 +0,0 @@
-# -*- Mode:python; c-file-style:"gnu"; indent-tabs-mode:nil -*- */
-#
-# Copyright (C) 2015-2018, The University of Memphis,
-#                          Arizona Board of Regents,
-#                          Regents of the University of California.
-#
-# This file is part of Mini-NDN.
-# See AUTHORS.md for a complete list of Mini-NDN authors and contributors.
-#
-# Mini-NDN is free software: you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# Mini-NDN is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with Mini-NDN, e.g., in COPYING.md file.
-# If not, see <http://www.gnu.org/licenses/>.
-
-from ndn.experiments.experiment import Experiment
-
-import time
-
-class PingallExperiment(Experiment):
-
-    def __init__(self, args):
-
-        Experiment.__init__(self, args)
-        self.COLLECTION_PERIOD_BUFFER = 10
-        print "Using {} traffic".format(self.options.pctTraffic)
-
-    def setup(self):
-        if self.options.nPings != 0:
-            Experiment.setup(self)
-
-    def run(self):
-        if self.options.nPings == 0:
-            return
-
-        self.startPctPings()
-
-        # For pingall experiment sleep for the number of pings + some offset
-        time.sleep(self.options.nPings + self.COLLECTION_PERIOD_BUFFER)
-
-Experiment.register("pingall", PingallExperiment)
diff --git a/ndn/experiments/nlsr/prefix_propogation.py b/ndn/experiments/nlsr/prefix_propogation.py
deleted file mode 100644
index ae8301a..0000000
--- a/ndn/experiments/nlsr/prefix_propogation.py
+++ /dev/null
@@ -1,66 +0,0 @@
-# -*- Mode:python; c-file-style:"gnu"; indent-tabs-mode:nil -*- */
-#
-# Copyright (C) 2015-2018, The University of Memphis,
-#                          Arizona Board of Regents,
-#                          Regents of the University of California.
-#
-# This file is part of Mini-NDN.
-# See AUTHORS.md for a complete list of Mini-NDN authors and contributors.
-#
-# Mini-NDN is free software: you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# Mini-NDN is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with Mini-NDN, e.g., in COPYING.md file.
-# If not, see <http://www.gnu.org/licenses/>.
-
-import sys
-import time
-from ndn.experiments.experiment import Experiment
-
-class PrefixPropogationExperiment(Experiment):
-
-    def __init__(self, args):
-        Experiment.__init__(self, args)
-
-    def setup(self):
-        self.checkConvergence()
-
-    def run(self):
-        firstNode = self.net.hosts[0]
-
-        if self.options.nlsrSecurity:
-            firstNode.cmd("ndnsec-set-default /ndn/{}-site/%C1.Operator/op".format(firstNode.name))
-
-        print("Testing advertise")
-        firstNode.cmd("nlsrc advertise /testPrefix")
-        time.sleep(30)
-
-        for host in self.net.hosts:
-            if host.name != firstNode.name:
-                if (int(host.cmd("nfdc fib | grep testPrefix | wc -l")) != 1 or
-                   int(host.cmd("nlsrc status | grep testPrefix | wc -l")) != 1):
-                    print("Advertise test failed")
-                    self.net.stop()
-                    sys.exit(1)
-
-        print("Testing withdraw")
-        firstNode.cmd("nlsrc withdraw /testPrefix")
-        time.sleep(30)
-
-        for host in self.net.hosts:
-            if host.name != firstNode.name:
-                if (int(host.cmd("nfdc fib | grep testPrefix | wc -l")) != 0 or
-                   int(host.cmd("nlsrc status | grep testPrefix | wc -l")) != 0):
-                    print("Withdraw test failed")
-                    self.net.stop()
-                    sys.exit(1)
-
-Experiment.register("prefix-propogation", PrefixPropogationExperiment)
diff --git a/ndn/experiments/psync/__init__.py b/ndn/experiments/psync/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/ndn/experiments/psync/__init__.py
+++ /dev/null
diff --git a/ndn/experiments/psync/psync-full.py b/ndn/experiments/psync/psync-full.py
deleted file mode 100644
index 206993e..0000000
--- a/ndn/experiments/psync/psync-full.py
+++ /dev/null
@@ -1,75 +0,0 @@
-# -*- Mode:python; c-file-style:"gnu"; indent-tabs-mode:nil -*- */
-#
-# Copyright (C) 2015-2019, The University of Memphis,
-#                          Arizona Board of Regents,
-#                          Regents of the University of California.
-#
-# This file is part of Mini-NDN.
-# See AUTHORS.md for a complete list of Mini-NDN authors and contributors.
-#
-# Mini-NDN is free software: you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# Mini-NDN is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with Mini-NDN, e.g., in COPYING.md file.
-# If not, see <http://www.gnu.org/licenses/>.
-
-from ndn.experiments.experiment import Experiment
-from ndn.apps.nfdc import Nfdc
-
-import time
-import sys
-
-class PSyncFull(Experiment):
-
-    def __init__(self, args):
-        Experiment.__init__(self, args)
-        self.syncPrefix = "/sync"
-        self.numUserPrefixesPerNode = 2
-        self.maxUpdatesPerUserPrefixPerNode = 3
-
-    def registerRouteToAllNeighbors(self, host):
-        for node in self.net.hosts:
-            for neighbor in node.connectionsTo(host):
-                ip = node.IP(neighbor[0])
-                Nfdc.createFace(host, ip)
-                Nfdc.registerRoute(host, self.syncPrefix, ip)
-
-    def start(self):
-        for host in self.net.hosts:
-            Nfdc.setStrategy(host, self.syncPrefix, "multicast")
-            self.registerRouteToAllNeighbors(host)
-
-        print("Starting psync-full-sync on all the nodes")
-        for host in self.net.hosts:
-            host.cmd("export NDN_LOG=examples.FullSyncApp=INFO")
-            host.cmd("psync-full-sync {} {} {} {} &> psync.logs &"
-                     .format(self.syncPrefix, host.name, self.numUserPrefixesPerNode,
-                             self.maxUpdatesPerUserPrefixPerNode))
-
-        print("Sleeping 5 minutes for convergence")
-        # Estimated time for 4 node default topology
-        time.sleep(300)
-
-        totalUpdates = int(host.cmd("grep -r Update {}/*/psync.logs | wc -l"
-                                    .format(self.options.workDir)))
-
-        expectedUpdates = (self.maxUpdatesPerUserPrefixPerNode *
-                          len(self.net.hosts) * (len(self.net.hosts) - 1) *
-                          self.numUserPrefixesPerNode)
-
-        if totalUpdates == expectedUpdates:
-            print("PSync full sync has successfully converged.")
-        else:
-            print("PSync full sync convergence was not successful. Exiting...")
-            self.net.stop()
-            sys.exit(1)
-
-Experiment.register("psync-full", PSyncFull)
diff --git a/ndn/experiments/psync/psync-partial.py b/ndn/experiments/psync/psync-partial.py
deleted file mode 100644
index 9b37281..0000000
--- a/ndn/experiments/psync/psync-partial.py
+++ /dev/null
@@ -1,59 +0,0 @@
-# -*- Mode:python; c-file-style:"gnu"; indent-tabs-mode:nil -*- */
-#
-# Copyright (C) 2015-2019, The University of Memphis,
-#                          Arizona Board of Regents,
-#                          Regents of the University of California.
-#
-# This file is part of Mini-NDN.
-# See AUTHORS.md for a complete list of Mini-NDN authors and contributors.
-#
-# Mini-NDN is free software: you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# Mini-NDN is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with Mini-NDN, e.g., in COPYING.md file.
-# If not, see <http://www.gnu.org/licenses/>.
-
-from ndn.experiments.experiment import Experiment
-from ndn.apps.nfdc import Nfdc
-
-import time
-import sys
-
-class PSyncPartial(Experiment):
-
-    def __init__(self, args):
-        Experiment.__init__(self, args)
-
-    def start(self):
-        host1 = self.net.hosts[0]
-
-        host1.cmd("export NDN_LOG=examples.PartialSyncProducerApp=INFO")
-        host1.cmd("psync-producer /sync /{} 10 1 &> producer.log &".format(host1.name))
-        time.sleep(1)
-
-        host1.cmd("export NDN_LOG=examples.PartialSyncConsumerApp=INFO:$NDN_LOG")
-        host1.cmd("psync-consumer /sync 5 &> consumer.log &")
-
-        print("Sleeping 90 seconds for convergence")
-        time.sleep(90)
-
-        consumerSubs = int(host1.cmd("cat consumer.log | grep -c Subscribing"))
-        consumerUpdates = int(host1.cmd("cat consumer.log | grep -c Update"))
-        producerPublish = int(host1.cmd("cat producer.log | grep -c Publish"))
-
-        if consumerSubs == 5 and consumerUpdates == 5 and producerPublish == 10:
-            print("PSync partial sync has successfully converged.")
-        else:
-            print("PSync partial sync convergence was not successful. Exiting...")
-            self.net.stop()
-            sys.exit(1)
-
-Experiment.register("psync-partial", PSyncPartial)
diff --git a/ndn/experiments/static_routing_experiment.py b/ndn/experiments/static_routing_experiment.py
deleted file mode 100644
index e40fbf9..0000000
--- a/ndn/experiments/static_routing_experiment.py
+++ /dev/null
@@ -1,73 +0,0 @@
-# -*- Mode:python; c-file-style:"gnu"; indent-tabs-mode:nil -*- */
-#
-# Copyright (C) 2015-2019, The University of Memphis,
-#                          Arizona Board of Regents,
-#                          Regents of the University of California.
-#
-# This file is part of Mini-NDN.
-# See AUTHORS.md for a complete list of Mini-NDN authors and contributors.
-#
-# Mini-NDN is free software: you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# Mini-NDN is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with Mini-NDN, e.g., in COPYING.md file.
-# If not, see <http://www.gnu.org/licenses/>.
-
-from ndn.experiments.experiment import Experiment
-from ndn.apps.ndn_global_routing_helper import GlobalRoutingHelper
-
-from mininet.log import info
-
-class RoutingHelperExp(Experiment):
-
-    def __init__(self, args):
-        Experiment.__init__(self, args)
-
-    def start(self):
-        """
-        Compute and add routes to NFD
-        """
-        info('Adding static routes to NFD\n')
-        grh = GlobalRoutingHelper(self.net, self.options.faceType, self.options.routingType)
-        # For all host, pass self.net.hosts or a list, [self.net['a'], ..] or [self.net.hosts[0],.]
-        grh.addOrigin([self.net['a']], ["/abc"])
-        grh.calculateNPossibleRoutes()
-
-        '''
-        Experiment run with default topology, test cases won't work with other topologies
-                              # With calculateNPossibleRoutes,
-                10            # routing = hr, N = All, from A, 3 routes needs to added to NFD.
-            A +++++++ B       # A - B  -- cost 10
-            +         +       # A - C  -- cost 10
-         10 +         + 10    # A - D  -- cost 20
-            +         +       # Same goes for B being a source.
-            C         D       #
-
-         prefix "/abc" is advertise from node A, it should be reachable from all other nodes.
-
-        '''
-        if self.options.routingType == "link-state":
-            # This test only for link-state. Similar test can be done for hyperbolic routing.
-            routesFromA = self.net['a'].cmd("nfdc route | grep -v '/localhost/nfd'")
-            if '/ndn/b-site/b' not in routesFromA or \
-               '/ndn/c-site/c' not in routesFromA or \
-               '/ndn/d-site/d' not in routesFromA:
-                info("Route addition failed\n")
-                exit(-1)
-
-            routesToPrefix = self.net['b'].cmd("nfdc fib | grep '/abc'")
-            if '/abc' not in routesToPrefix:
-                info("Missing route to advertised prefix, Route addition failed\n")
-                exit(-1)
-
-        info('Route addition to NFD completed\n')
-
-Experiment.register("centralize-routing", RoutingHelperExp)
\ No newline at end of file
diff --git a/ndn/gui.py b/ndn/gui.py
deleted file mode 100644
index 54838c5..0000000
--- a/ndn/gui.py
+++ /dev/null
@@ -1,156 +0,0 @@
-# -*- Mode:python; c-file-style:"gnu"; indent-tabs-mode:nil -*- */
-#
-# Copyright (C) 2015-2018, The University of Memphis,
-#                          Arizona Board of Regents,
-#                          Regents of the University of California.
-#
-# This file is part of Mini-NDN.
-# See AUTHORS.md for a complete list of Mini-NDN authors and contributors.
-#
-# Mini-NDN is free software: you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# Mini-NDN is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with Mini-NDN, e.g., in COPYING.md file.
-# If not, see <http://www.gnu.org/licenses/>.
-
-from Tkinter import *
-
-LOG_LEVELS = [
-    "NONE",
-    "ERROR",
-    "WARN",
-    "INFO",
-    "DEBUG",
-    "TRACE",
-    "ALL"
-]
-
-class GuiFrame(Frame):
-    def __init__(self, notebook, prefValues, appId):
-        Frame.__init__(self, notebook)
-
-        self.prefValues = prefValues
-        self.appId = appId
-
-        self.row = 0
-        self.column = 0
-
-    def addEntryBox(self, label, variable, defaultValue=""):
-        variable.set(defaultValue)
-
-        Label(self, text=label).grid(row=self.row, sticky=E)
-        entry = Entry(self, textvariable=variable)
-        entry.grid(row=self.row, column=1)
-
-        self.row += 1
-
-    def addDropDown(self, label, variable, values, defaultValue=""):
-        variable.set(defaultValue)
-
-        Label(self, text=label).grid(row=self.row, sticky=E)
-
-        self.entry = apply(OptionMenu, (self, variable) + tuple(values))
-        self.entry.grid(row=self.row, column=1)
-
-        self.row += 1
-
-    def getPreferredOrDefaultValue(self, key, defaultValue):
-        if self.appId in self.prefValues:
-            return self.prefValues[self.appId][key]
-        else:
-            return defaultValue
-
-class NfdFrame(GuiFrame):
-    def __init__(self, notebook, prefValues):
-        GuiFrame.__init__(self, notebook, prefValues, "nfd")
-
-        self.frameLabel = "NFD"
-
-        # log-level
-        self.logLevel = StringVar(self)
-        self.addDropDown("Log level:",
-                         self.logLevel,
-                         LOG_LEVELS,
-                         self.getPreferredOrDefaultValue("log-level", LOG_LEVELS[3]))
-
-    def getValues(self):
-        return {
-            "log-level": self.logLevel.get()
-        }
-
-class NlsrFrame(GuiFrame):
-
-    HYPERBOLIC_STATES = [
-        "off",
-        "on",
-        "dry-run"
-    ]
-
-    def __init__(self, notebook, prefValues):
-        GuiFrame.__init__(self, notebook, prefValues, "nlsr")
-
-        self.frameLabel = "NLSR"
-
-        # general: network
-        self.network = StringVar(self)
-        self.addEntryBox("Network:",
-                         self.network,
-                         self.getPreferredOrDefaultValue("network", "/ndn"))
-
-        # general: site
-        self.site = StringVar(self)
-        self.addEntryBox("Site:", self.site, self.getPreferredOrDefaultValue("site", "/edu/site"))
-
-        # general: router
-        self.router = StringVar(self)
-        self.addEntryBox("Router:",
-                         self.router,
-                         self.getPreferredOrDefaultValue("router", "/%C1.Router/cs/host"))
-
-        # general: log-level
-        self.logLevel = StringVar(self)
-        self.addDropDown("Log level:",
-                         self.logLevel,
-                         LOG_LEVELS,
-                         self.getPreferredOrDefaultValue("log-level", LOG_LEVELS[3]))
-
-        # hyperbolic: state
-        self.hyperbolicState = StringVar(self)
-        self.addDropDown("Hyperbolic routing:",
-                         self.hyperbolicState,
-                         self.HYPERBOLIC_STATES,
-                         self.getPreferredOrDefaultValue("hyperbolic-state", self.HYPERBOLIC_STATES[0]))
-
-        # hyperbolic: angle
-        self.angle = StringVar(self)
-        self.addEntryBox("Angle:", self.angle, self.getPreferredOrDefaultValue("angle", "0.0"))
-
-        # hyperbolic: radius
-        self.radius = StringVar(self)
-        self.addEntryBox("Radius:", self.radius, self.getPreferredOrDefaultValue("radius", "0.0"))
-
-        # fib: max-faces-per-prefix
-        self.maxFaces = StringVar(self)
-        self.addEntryBox("Max faces per prefix:",
-                         self.maxFaces,
-                         self.getPreferredOrDefaultValue("max-faces-per-prefix", "0"))
-
-    def getValues(self):
-        return {
-            "network": self.network.get(),
-            "site": self.site.get(),
-            "router": self.router.get(),
-            "log-level": self.logLevel.get(),
-            "hyperbolic-state": self.hyperbolicState.get(),
-            "angle": self.angle.get(),
-            "radius": self.radius.get(),
-            "max-faces-per-prefix": self.maxFaces.get()
-        }
diff --git a/ndn/ndn_application.py b/ndn/ndn_application.py
deleted file mode 100644
index 150c7c1..0000000
--- a/ndn/ndn_application.py
+++ /dev/null
@@ -1,49 +0,0 @@
-# -*- Mode:python; c-file-style:"gnu"; indent-tabs-mode:nil -*- */
-#
-# Copyright (C) 2015-2018, The University of Memphis,
-#                          Arizona Board of Regents,
-#                          Regents of the University of California.
-#
-# This file is part of Mini-NDN.
-# See AUTHORS.md for a complete list of Mini-NDN authors and contributors.
-#
-# Mini-NDN is free software: you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# Mini-NDN is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with Mini-NDN, e.g., in COPYING.md file.
-# If not, see <http://www.gnu.org/licenses/>.
-
-import os
-
-class NdnApplication:
-    def __init__(self, node):
-        self.node = node
-        self.isRunning = False
-        self.processId = ""
-
-    def start(self, command):
-        if self.isRunning is True:
-            try:
-                os.kill(int(self.processId), 0)
-            except OSError:
-                self.isRunning = False
-
-        if self.isRunning is False:
-            self.node.cmd(command)
-            self.processId = self.node.cmd("echo $!")[:-1]
-
-            self.isRunning = True
-
-    def stop(self):
-        if self.isRunning and self.processId != "":
-            self.node.cmd("sudo kill {}".format(self.processId))
-
-            self.isRunning = False
diff --git a/ndn/ndn_host.py b/ndn/ndn_host.py
deleted file mode 100644
index 2567794..0000000
--- a/ndn/ndn_host.py
+++ /dev/null
@@ -1,158 +0,0 @@
-# -*- Mode:python; c-file-style:"gnu"; indent-tabs-mode:nil -*- */
-#
-# Copyright (C) 2015-2018, The University of Memphis,
-#                          Arizona Board of Regents,
-#                          Regents of the University of California.
-#
-# This file is part of Mini-NDN.
-# See AUTHORS.md for a complete list of Mini-NDN authors and contributors.
-#
-# Mini-NDN is free software: you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# Mini-NDN is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with Mini-NDN, e.g., in COPYING.md file.
-# If not, see <http://www.gnu.org/licenses/>.
-#
-# This file incorporates work covered by the following copyright and
-# permission notice:
-#
-#   Mininet 2.3.0d1 License
-#
-#   Copyright (c) 2013-2016 Open Networking Laboratory
-#   Copyright (c) 2009-2012 Bob Lantz and The Board of Trustees of
-#   The Leland Stanford Junior University
-#
-#   Original authors: Bob Lantz and Brandon Heller
-#
-#   We are making Mininet available for public use and benefit with the
-#   expectation that others will use, modify and enhance the Software and
-#   contribute those enhancements back to the community. However, since we
-#   would like to make the Software available for broadest use, with as few
-#   restrictions as possible permission is hereby granted, free of charge, to
-#   any person obtaining a copy of this Software to deal in the Software
-#   under the copyrights without restriction, including without limitation
-#   the rights to use, copy, modify, merge, publish, distribute, sublicense,
-#   and/or sell copies of the Software, and to permit persons to whom the
-#   Software is furnished to do so, subject to the following conditions:
-#
-#   The above copyright notice and this permission notice shall be included
-#   in all copies or substantial portions of the Software.
-#
-#   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
-#   OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
-#   MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
-#   IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
-#   CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
-#   TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
-#   SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-#
-#   The name and trademarks of copyright holder(s) may NOT be used in
-#   advertising or publicity pertaining to the Software or any derivatives
-#   without specific, written prior permission.
-
-from mininet.node import CPULimitedHost, Host, Node
-from mininet.examples.cluster import RemoteMixin
-
-from ndn.nfd import Nfd
-
-class NdnHostCommon():
-    "Common methods of NdnHost and CpuLimitedNdnHost"
-
-    def configNdn(self):
-        self.buildPeerIp()
-
-    def buildPeerIp(self):
-        for iface in self.intfList():
-            link = iface.link
-            if link:
-                node1, node2 = link.intf1.node, link.intf2.node
-                if node1 == self:
-                    self.peerList[node2.name] = link.intf2.node.IP(link.intf2)
-                else:
-                    self.peerList[node1.name] = link.intf1.node.IP(link.intf1)
-
-    inited = False
-
-    @classmethod
-    def init(cls):
-        "Initialization for NDNHost class"
-        cls.inited = True
-
-class NdnHost(Host, NdnHostCommon):
-    "NDNHost is a Host that always runs NFD"
-
-    def __init__(self, name, **kwargs):
-
-        Host.__init__(self, name, **kwargs)
-        if not NdnHost.inited:
-            NdnHostCommon.init()
-
-        # Create home directory for a node
-        self.homeFolder = "%s/%s" % (self.params['workdir'], self.name)
-        self.cmd("mkdir -p %s" % self.homeFolder)
-        self.cmd("cd %s" % self.homeFolder)
-
-        self.nfd = None
-
-        self.peerList = {}
-
-    def config(self, app=None, cache=None, **params):
-
-        r = Node.config(self, **params)
-
-        self.setParam(r, 'app', app=app)
-        self.setParam(r, 'cache', cache=cache)
-
-        return r
-
-    def terminate(self):
-        "Stop node."
-        if self.nfd is not None:
-            self.nfd.stop()
-        Host.terminate(self)
-
-class CpuLimitedNdnHost(CPULimitedHost, NdnHostCommon):
-    '''CPULimitedNDNHost is a Host that always runs NFD and extends CPULimitedHost.
-       It should be used when one wants to limit the resources of NDN routers and hosts '''
-
-    def __init__(self, name, sched='cfs', **kwargs):
-
-        CPULimitedHost.__init__(self, name, sched, **kwargs)
-        if not NdnHost.inited:
-            NdnHostCommon.init()
-
-        # Create home directory for a node
-        self.homeFolder = "%s/%s" % (self.params['workdir'], self.name)
-        self.cmd("mkdir -p %s" % self.homeFolder)
-        self.cmd("cd %s" % self.homeFolder)
-
-        self.nfd = None
-
-        self.peerList = {}
-
-    def config(self, app=None, cpu=None, cores=None, cache=None, **params):
-
-        r = CPULimitedHost.config(self,cpu,cores, **params)
-
-        self.setParam(r, 'app', app=app)
-        self.setParam(r, 'cache', cache=cache)
-
-        return r
-
-    def terminate(self):
-        "Stop node."
-        if self.nfd is not None:
-            self.nfd.stop()
-        CPULimitedHost.terminate(self)
-
-class RemoteNdnHost(RemoteMixin, NdnHost):
-    "A node on a remote server"
-    pass
diff --git a/ndn/nfd.py b/ndn/nfd.py
deleted file mode 100644
index eb8cb34..0000000
--- a/ndn/nfd.py
+++ /dev/null
@@ -1,71 +0,0 @@
-# -*- Mode:python; c-file-style:"gnu"; indent-tabs-mode:nil -*- */
-#
-# Copyright (C) 2015-2018, The University of Memphis,
-#                          Arizona Board of Regents,
-#                          Regents of the University of California.
-#
-# This file is part of Mini-NDN.
-# See AUTHORS.md for a complete list of Mini-NDN authors and contributors.
-#
-# Mini-NDN is free software: you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# Mini-NDN is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with Mini-NDN, e.g., in COPYING.md file.
-# If not, see <http://www.gnu.org/licenses/>.
-
-import time, sys, os
-from ndn.ndn_application import NdnApplication
-from ndn.util import copyExistentFile
-
-class Nfd(NdnApplication):
-
-    def __init__(self, node, csSize):
-        NdnApplication.__init__(self, node)
-
-        self.logLevel = node.params["params"].get("nfd-log-level", "INFO")
-
-        self.confFile = "{}/nfd.conf".format(node.homeFolder)
-        self.logFile = "{}/nfd.log".format(node.homeFolder)
-        self.sockFile = "/var/run/{}.sock".format(node.name)
-        self.ndnFolder = "{}/.ndn".format(node.homeFolder)
-        self.clientConf = "{}/client.conf".format(self.ndnFolder)
-
-        # Copy nfd.conf file from /usr/local/etc/ndn or /etc/ndn to the node's home directory
-        # Use nfd.conf as default configuration for NFD, else use the sample
-        possibleConfPaths = ["/usr/local/etc/ndn/nfd.conf.sample", "/usr/local/etc/ndn/nfd.conf",
-                             "/etc/ndn/nfd.conf.sample", "/etc/ndn/nfd.conf"]
-        copyExistentFile(node, possibleConfPaths, self.confFile)
-
-        # Set log level
-        node.cmd("infoedit -f {} -s log.default_level -v {}".format(self.confFile, self.logLevel))
-        # Open the conf file and change socket file name
-        node.cmd("infoedit -f {} -s face_system.unix.path -v /var/run/{}.sock".format(self.confFile, node.name))
-
-        # Set CS size
-        node.cmd("infoedit -f {} -s tables.cs_max_packets -v {}".format(self.confFile, csSize))
-
-        # Make NDN folder
-        node.cmd("sudo mkdir {}".format(self.ndnFolder))
-
-        # Copy client configuration to host
-        possibleClientConfPaths = ["/usr/local/etc/ndn/client.conf.sample", "/etc/ndn/client.conf.sample"]
-        copyExistentFile(node, possibleClientConfPaths, self.clientConf)
-
-        # Change the unix socket
-        node.cmd("sudo sed -i 's|nfd.sock|{}.sock|g' {}".format(node.name, self.clientConf))
-
-        # Change home folder
-        node.cmd("export HOME={}".format(node.homeFolder))
-        node.cmd("ndnsec-keygen /localhost/operator | ndnsec-install-cert -")
-
-    def start(self):
-        NdnApplication.start(self, "setsid nfd --config {} > {} 2>&1 &".format(self.confFile, self.logFile))
-        time.sleep(2)
diff --git a/ndn/placer.py b/ndn/placer.py
deleted file mode 100644
index 412dc91..0000000
--- a/ndn/placer.py
+++ /dev/null
@@ -1,87 +0,0 @@
-# -*- Mode:python; c-file-style:"gnu"; indent-tabs-mode:nil -*- */
-#
-# Copyright (C) 2015-2018, The University of Memphis,
-#                          Arizona Board of Regents,
-#                          Regents of the University of California.
-#
-# This file is part of Mini-NDN.
-# See AUTHORS.md for a complete list of Mini-NDN authors and contributors.
-#
-# Mini-NDN is free software: you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# Mini-NDN is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with Mini-NDN, e.g., in COPYING.md file.
-# If not, see <http://www.gnu.org/licenses/>.
-#
-# This file incorporates work covered by the following copyright and
-# permission notice:
-#
-#   Mininet 2.3.0d1 License
-#
-#   Copyright (c) 2013-2016 Open Networking Laboratory
-#   Copyright (c) 2009-2012 Bob Lantz and The Board of Trustees of
-#   The Leland Stanford Junior University
-#
-#   Original authors: Bob Lantz and Brandon Heller
-#
-#   We are making Mininet available for public use and benefit with the
-#   expectation that others will use, modify and enhance the Software and
-#   contribute those enhancements back to the community. However, since we
-#   would like to make the Software available for broadest use, with as few
-#   restrictions as possible permission is hereby granted, free of charge, to
-#   any person obtaining a copy of this Software to deal in the Software
-#   under the copyrights without restriction, including without limitation
-#   the rights to use, copy, modify, merge, publish, distribute, sublicense,
-#   and/or sell copies of the Software, and to permit persons to whom the
-#   Software is furnished to do so, subject to the following conditions:
-#
-#   The above copyright notice and this permission notice shall be included
-#   in all copies or substantial portions of the Software.
-#
-#   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
-#   OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
-#   MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
-#   IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
-#   CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
-#   TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
-#   SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-#
-#   The name and trademarks of copyright holder(s) may NOT be used in
-#   advertising or publicity pertaining to the Software or any derivatives
-#   without specific, written prior permission.
-
-from mininet.examples.cluster import Placer
-
-nodePlace = []
-
-class PopulatePlacement():
-    def __init__( self, placeList ):
-        global nodePlace
-        nodePlace = placeList
-
-class GuidedPlacer( Placer ):
-    "Guided placement"
-    def __init__( self, *args, **kwargs ):
-        Placer.__init__( self, *args, **kwargs )
-        self.count = 0
-
-    def place( self, nodename ):
-        assert nodename  #please pylint
-        while(True):
-            global nodePlace
-            if nodePlace[self.count] != 0:
-                nodePlace[self.count] -= 1
-                # args[self.count] is not zero, hence return the server at that position
-                # so that if args[0] is 7 and servers[0] is Europa then place 7 nodes on Europa
-                return self.servers[self.count]
-            else:
-                # while makes sure we go back to the if after this
-                self.count += 1
diff --git a/ndn/remote_ndn_link.py b/ndn/remote_ndn_link.py
deleted file mode 100644
index 497c098..0000000
--- a/ndn/remote_ndn_link.py
+++ /dev/null
@@ -1,90 +0,0 @@
-# -*- Mode:python; c-file-style:"gnu"; indent-tabs-mode:nil -*- */
-#
-# Copyright (C) 2015-2018, The University of Memphis,
-#                          Arizona Board of Regents,
-#                          Regents of the University of California.
-#
-# This file is part of Mini-NDN.
-# See AUTHORS.md for a complete list of Mini-NDN authors and contributors.
-#
-# Mini-NDN is free software: you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# Mini-NDN is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with Mini-NDN, e.g., in COPYING.md file.
-# If not, see <http://www.gnu.org/licenses/>.
-#
-# This file incorporates work covered by the following copyright and
-# permission notice:
-#
-#   Mininet 2.3.0d1 License
-#
-#   Copyright (c) 2013-2016 Open Networking Laboratory
-#   Copyright (c) 2009-2012 Bob Lantz and The Board of Trustees of
-#   The Leland Stanford Junior University
-#
-#   Original authors: Bob Lantz and Brandon Heller
-#
-#   We are making Mininet available for public use and benefit with the
-#   expectation that others will use, modify and enhance the Software and
-#   contribute those enhancements back to the community. However, since we
-#   would like to make the Software available for broadest use, with as few
-#   restrictions as possible permission is hereby granted, free of charge, to
-#   any person obtaining a copy of this Software to deal in the Software
-#   under the copyrights without restriction, including without limitation
-#   the rights to use, copy, modify, merge, publish, distribute, sublicense,
-#   and/or sell copies of the Software, and to permit persons to whom the
-#   Software is furnished to do so, subject to the following conditions:
-#
-#   The above copyright notice and this permission notice shall be included
-#   in all copies or substantial portions of the Software.
-#
-#   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
-#   OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
-#   MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
-#   IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
-#   CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
-#   TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
-#   SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-#
-#   The name and trademarks of copyright holder(s) may NOT be used in
-#   advertising or publicity pertaining to the Software or any derivatives
-#   without specific, written prior permission.
-
-from mininet.link import TCLink
-from mininet.examples.cluster import RemoteLink, RemoteGRELink
-
-class RemoteNdnLink( TCLink, RemoteLink ):
-    "A RemoteLink is a link between nodes which may be on different servers"
-
-    def __init__( self, node1, node2, **kwargs ):
-        """Initialize a RemoteLink
-           see Link() for parameters"""
-        # Create links on remote node
-        self.node1 = node1
-        self.node2 = node2
-        self.tunnel = None
-        kwargs.setdefault( 'params1', {} )
-        kwargs.setdefault( 'params2', {} )
-        self.cmd = None  # satisfy pylint
-        TCLink.__init__( self, node1, node2, **kwargs )
-
-class RemoteGRENdnLink( TCLink, RemoteGRELink ):
-    def __init__(self, node1, node2, **kwargs):
-        """Initialize a RemoteLink
-           see Link() for parameters"""
-        # Create links on remote node
-        self.node1 = node1
-        self.node2 = node2
-        self.tunnel = None
-        kwargs.setdefault( 'params1', {} )
-        kwargs.setdefault( 'params2', {} )
-        self.cmd = None  # satisfy pylint
-        TCLink.__init__( self, node1, node2, **kwargs )
diff --git a/ndn/util.py b/ndn/util.py
deleted file mode 100644
index 0398453..0000000
--- a/ndn/util.py
+++ /dev/null
@@ -1,82 +0,0 @@
-# -*- Mode:python; c-file-style:"gnu"; indent-tabs-mode:nil -*- */
-#
-# Copyright (C) 2015-2018, The University of Memphis,
-#                          Arizona Board of Regents,
-#                          Regents of the University of California.
-#
-# This file is part of Mini-NDN.
-# See AUTHORS.md for a complete list of Mini-NDN authors and contributors.
-#
-# Mini-NDN is free software: you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# Mini-NDN is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with Mini-NDN, e.g., in COPYING.md file.
-# If not, see <http://www.gnu.org/licenses/>.
-
-from subprocess import call
-from mininet.cli import CLI
-import sys
-from os.path import isfile
-
-sshbase = [ 'ssh', '-q', '-t', '-i/home/mininet/.ssh/id_rsa' ]
-scpbase = [ 'scp', '-i', '/home/mininet/.ssh/id_rsa' ]
-devnull = open('/dev/null', 'w')
-
-def ssh(login, cmd):
-    rcmd = sshbase + [login, cmd]
-    call(rcmd, stdout=devnull, stderr=devnull)
-
-def scp(*args):
-    tmp = []
-    for arg in args:
-        tmp.append(arg)
-    rcmd = scpbase + tmp
-    call(rcmd, stdout=devnull, stderr=devnull)
-
-def copyExistentFile(node, fileList, destination):
-    for file in fileList:
-        if isfile(file):
-            node.cmd("cp {} {}".format(file, destination))
-            break
-    if not isfile(destination):
-        fileName = destination.split("/")[-1]
-        raise IOError("{} not found in expected directory.".format(fileName))
-
-
-class MiniNDNCLI(CLI):
-    prompt = 'mini-ndn> '
-    def __init__(self, mininet, stdin=sys.stdin, script=None):
-        CLI.__init__(self, mininet, stdin=sys.stdin, script=None)
-
-class ProgramOptions:
-    def __init__(self):
-        self.ctime = 60
-        self.experimentName = None
-        self.nFaces = 3
-        self.templateFile = "minindn.conf"
-        self.routingType = "link-state"
-        self.isNlsrEnabled = True
-        self.isCliEnabled = True
-        self.nlsrSecurity = False
-        self.nPings = 300
-        self.testbed = False
-        self.workDir = "/tmp/minindn"
-        self.resultDir = None
-        self.pctTraffic = 1.0
-        self.cluster = None
-        self.servers = None
-        self.guided = None
-        self.placer = None
-        self.tunnelType = None
-        self.faceType = "udp"
-        self.arguments = None
-        self.csSize = 65536
-        self.strategy = "best-route"
\ No newline at end of file
diff --git a/patches/ndn-cxx-dummy-keychain-from-ndnsim.patch b/patches/ndn-cxx-dummy-keychain-from-ndnsim.patch
new file mode 100644
index 0000000..a6e1b30
--- /dev/null
+++ b/patches/ndn-cxx-dummy-keychain-from-ndnsim.patch
@@ -0,0 +1,602 @@
+diff --git a/ndn-cxx/security/v2/key-chain.cpp b/ndn-cxx/security/v2/key-chain.cpp
+index 8043635..b9af6f0 100644
+--- a/ndn-cxx/security/v2/key-chain.cpp
++++ b/ndn-cxx/security/v2/key-chain.cpp
+@@ -25,6 +25,7 @@
+ #include "ndn-cxx/util/config-file.hpp"
+ #include "ndn-cxx/util/logger.hpp"
+ #include "ndn-cxx/util/sha256.hpp"
++#include "ndn-cxx/util/dummy-keychain.hpp"
+ 
+ #include "ndn-cxx/security/pib/pib-memory.hpp"
+ #include "ndn-cxx/security/pib/pib-sqlite3.hpp"
+@@ -163,7 +164,7 @@ KeyChain::getDefaultKeyParams()
+ //
+ 
+ KeyChain::KeyChain()
+-  : KeyChain(getDefaultPibLocator(), getDefaultTpmLocator(), true)
++  : KeyChain("pib-dummy", "tpm-dummy", true)
+ {
+ }
+ 
+diff --git a/ndn-cxx/util/dummy-keychain.cpp b/ndn-cxx/util/dummy-keychain.cpp
+new file mode 100644
+index 0000000..aa24465
+--- /dev/null
++++ b/ndn-cxx/util/dummy-keychain.cpp
+@@ -0,0 +1,346 @@
++/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
++/**
++ * Copyright (c) 2011-2015  Regents of the University of California.
++ *
++ * This file is part of ndnSIM. See AUTHORS for complete list of ndnSIM authors and
++ * contributors.
++ *
++ * ndnSIM is free software: you can redistribute it and/or modify it under the terms
++ * of the GNU General Public License as published by the Free Software Foundation,
++ * either version 3 of the License, or (at your option) any later version.
++ *
++ * ndnSIM is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
++ * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
++ * PURPOSE.  See the GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License along with
++ * ndnSIM, e.g., in COPYING.md file.  If not, see <http://www.gnu.org/licenses/>.
++ **/
++
++#include "dummy-keychain.hpp"
++
++#include <ndn-cxx/util/io.hpp>
++#include <ndn-cxx/security/transform/public-key.hpp>
++#include <boost/iostreams/device/array.hpp>
++#include <boost/iostreams/stream.hpp>
++
++namespace ndn {
++namespace security {
++
++static const uint8_t DUMMY_CERT[] =
++    "Bv0CqQclCAVkdW1teQgDS0VZCAgtnChyuKo7YAgCTkEICf0AAAFe3zzGfBQJGAEC"
++    "GQQANu6AFf0BJjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAOJSa1j6"
++    "JAzobrmtlUdJi38EWQZOigLykf9psImvIu7pa29Q3apBXENcV1E9687FmfY85Ec3"
++    "/onMtN7WG/wiuKiu/9eOr5WslD3VGDgxhesx80ygP0GNsN8FzsMl0lRKduXx3wG4"
++    "MCT8CX7uA4n4JbHY+0QaKUEEMRCiXcRAtF+yKfr+GaVeSemg+i/LR+6CSgpOyH0K"
++    "ogH9nlNhBn1Hxyc8X+B/nEu6P6NNEKkSnhT1jAbWtT1eL3BPGK/HNY19w9k2Ln6/"
++    "OYlhaHfB4m0oR/ePcUguQBwTgYS+40YQYUPivBsSQv3X2/7+gGLhIzA4YppwOooK"
++    "RGhbmL8zF2evmJ8CAwEAARZHGwEBHBgHFggFZHVtbXkIA0tFWQgILZwocriqO2D9"
++    "AP0m/QD+DzIwMTcxMDAyVDIyMzczNv0A/w8yMDE4MTAwMlQyMjM3MzUX/QEAMBUV"
++    "O51BEQwxp646i7IuHkuc1C/LISnOP2+wBFF2Ea1ht9MKjKkyJdmB1/GAOiR3njKd"
++    "UgRjBlownL11EwYDxkPY39RR05TmhF6PkpP81Ro/Vzv7rtSi/dxFIZXCiyuKPRUx"
++    "E0pZelPWVs3zMfqQ+8rWG89Kqs1vM0dglLBzlX9Lbim71TyLmaJaMmmBKv8+eQ22"
++    "CN71sRZOovl1kKcTHpOm61nD3C1n9GRflFtaMAXE/XU4zMJVzBv6XwQl6PCIc9H2"
++    "vjLa28ruVjhMGxqCGhziTC2eR56SUixrnEcbOKT0R+8+0AFnZIjdYglOZPcVwTVB"
++    "G6OxECJOuSoREcd1Ww==;";
++
++static const uint8_t DUMMY_SIGNATURE[] =
++    {0x17, 0xfd, 0x01, 0x00, 0x93, 0x15, 0x09, 0x49, 0x79, 0x9e, 0xb7, 0x9c, 0xd3, 0xc1, 0xbf, 0x61,
++     0x89, 0xd5, 0xd9, 0xca, 0xf2, 0xb0, 0x14, 0xae, 0x72, 0x7c, 0x1f, 0x8f, 0xf5, 0xb1, 0x70, 0xd6,
++     0x9b, 0x8f, 0xf8, 0xd7, 0x2d, 0xbc, 0x92, 0x6f, 0x7d, 0x77, 0x96, 0x46, 0xea, 0xd4, 0x7d, 0x90,
++     0xbc, 0x7a, 0xeb, 0xe2, 0x03, 0x93, 0xb1, 0xd2, 0x62, 0xec, 0x9d, 0xff, 0x9c, 0x9c, 0x2a, 0x14,
++     0x7d, 0x23, 0xca, 0x29, 0x3d, 0x15, 0x1a, 0x40, 0x42, 0x2c, 0x59, 0x33, 0x8a, 0xf7, 0xc0, 0x6b,
++     0xc4, 0x9c, 0xf3, 0xc4, 0x99, 0xa4, 0x1a, 0x60, 0xf5, 0x28, 0x7d, 0x4c, 0xef, 0x43, 0x7d, 0xbd,
++     0x7d, 0x00, 0x51, 0xee, 0x41, 0xf5, 0x25, 0x80, 0xce, 0xe6, 0x64, 0x4f, 0x75, 0x54, 0xf3, 0xb2,
++     0x99, 0x9a, 0x0f, 0x93, 0x9a, 0x28, 0x1d, 0xfe, 0x12, 0x8a, 0xe0, 0xc1, 0x02, 0xeb, 0xa4, 0x35,
++     0x52, 0x88, 0xac, 0x44, 0x1a, 0x44, 0x82, 0x97, 0x4f, 0x5f, 0xa8, 0xd8, 0x9f, 0x67, 0x38, 0xa8,
++     0x64, 0xb6, 0x62, 0x99, 0xbd, 0x96, 0x3c, 0xf5, 0x86, 0x09, 0x5c, 0x97, 0x6b, 0x8f, 0xae, 0xe0,
++     0x60, 0xe7, 0x23, 0x98, 0x6a, 0xee, 0xc1, 0xb0, 0x14, 0xbe, 0x46, 0x2c, 0xfb, 0xa7, 0x27, 0x73,
++     0xe4, 0xf3, 0x26, 0x33, 0xba, 0x99, 0xd4, 0x01, 0x38, 0xa8, 0xf2, 0x9e, 0x87, 0xe0, 0x71, 0x0b,
++     0x25, 0x44, 0x07, 0x35, 0x88, 0xab, 0x67, 0x27, 0x56, 0x0e, 0xb5, 0xb5, 0xe8, 0x27, 0xb4, 0x49,
++     0xdc, 0xb8, 0x48, 0x31, 0xff, 0x99, 0x48, 0xab, 0x11, 0xb4, 0xa0, 0xdf, 0x8a, 0x6d, 0xff, 0x43,
++     0x69, 0x32, 0xa7, 0xbc, 0x63, 0x9d, 0x0f, 0xe0, 0x95, 0x34, 0x36, 0x25, 0x4b, 0x3e, 0x36, 0xbd,
++     0x81, 0x91, 0x0b, 0x91, 0x9f, 0x3a, 0x04, 0xa2, 0x44, 0x28, 0x19, 0xa1, 0x38, 0x21, 0x4f, 0x25,
++     0x59, 0x8a, 0x48, 0xc2};
++
++const std::string DummyPib::SCHEME = "pib-dummy";
++const std::string DummyTpm::SCHEME = "tpm-dummy";
++
++NDN_CXX_V2_KEYCHAIN_REGISTER_PIB_BACKEND(DummyPib);
++NDN_CXX_V2_KEYCHAIN_REGISTER_TPM_BACKEND(DummyTpm);
++
++DummyPib::DummyPib(const std::string& locator)
++{
++}
++
++void
++DummyPib::setTpmLocator(const std::string& tpmLocator)
++{
++  m_tpmLocator = tpmLocator;
++}
++
++std::string
++DummyPib::getTpmLocator() const
++{
++  return m_tpmLocator;
++}
++
++bool
++DummyPib::hasIdentity(const Name& identityName) const
++{
++  return true;
++}
++
++void
++DummyPib::addIdentity(const Name& identityName)
++{
++}
++
++void
++DummyPib::removeIdentity(const Name& identity)
++{
++}
++
++void
++DummyPib::clearIdentities()
++{
++}
++
++std::set<Name>
++DummyPib::getIdentities() const
++{
++  std::set<Name> identities;
++  identities.insert("/dummy");
++  return identities;
++}
++
++void
++DummyPib::setDefaultIdentity(const Name& identityName)
++{
++}
++
++Name
++DummyPib::getDefaultIdentity() const
++{
++  return "/dummy";
++}
++
++bool
++DummyPib::hasKey(const Name& keyName) const
++{
++  return true;
++}
++
++void
++DummyPib::addKey(const Name& identity, const Name& keyName,
++                 const uint8_t* key, size_t keyLen)
++{
++}
++
++void
++DummyPib::removeKey(const Name& keyName)
++{
++}
++
++Buffer
++DummyPib::getKeyBits(const Name& keyName) const
++{
++    typedef boost::iostreams::stream<boost::iostreams::array_source> arrayStream;
++    arrayStream
++    is(reinterpret_cast<const char*>(DUMMY_CERT), sizeof(DUMMY_CERT));
++    auto cert = io::load<v2::Certificate>(is, io::BASE64);
++    return cert->getPublicKey();
++}
++
++std::set<Name>
++DummyPib::getKeysOfIdentity(const Name& identity) const
++{
++  std::set<Name> keys;
++  keys.insert("/dummy/KEY/-%9C%28r%B8%AA%3B%60");
++  return keys;
++}
++
++void
++DummyPib::setDefaultKeyOfIdentity(const Name& identity, const Name& keyName)
++{
++}
++
++Name
++DummyPib::getDefaultKeyOfIdentity(const Name& identity) const
++{
++  return "/dummy/KEY/-%9C%28r%B8%AA%3B%60";
++}
++
++bool
++DummyPib::hasCertificate(const Name& certName) const
++{
++  return true;
++}
++
++void
++DummyPib::addCertificate(const v2::Certificate& certificate)
++{
++}
++
++void
++DummyPib::removeCertificate(const Name& certName)
++{
++}
++
++v2::Certificate
++DummyPib::getCertificate(const Name& certificateName) const
++{
++  static shared_ptr<v2::Certificate> cert = nullptr;
++  if (cert == nullptr) {
++    typedef boost::iostreams::stream<boost::iostreams::array_source> arrayStream;
++    arrayStream
++    is(reinterpret_cast<const char*>(DUMMY_CERT), sizeof(DUMMY_CERT));
++    cert = io::load<v2::Certificate>(is, io::BASE64);
++  }
++
++  return *cert;
++}
++
++std::set<Name>
++DummyPib::getCertificatesOfKey(const Name& keyName) const
++{
++  std::set<Name> certs;
++  certs.insert("/dummy/KEY/-%9C%28r%B8%AA%3B%60/self/%FD%00%00%01%5E%DF%3Bv%01");
++  return certs;
++}
++
++void
++DummyPib::setDefaultCertificateOfKey(const Name& keyName, const Name& certName)
++{
++}
++
++v2::Certificate
++DummyPib::getDefaultCertificateOfKey(const Name& keyName) const
++{
++  static shared_ptr<v2::Certificate> cert = nullptr;
++  if (cert == nullptr) {
++    typedef boost::iostreams::stream<boost::iostreams::array_source> arrayStream;
++    arrayStream
++    is(reinterpret_cast<const char*>(DUMMY_CERT), sizeof(DUMMY_CERT));
++    cert = io::load<v2::Certificate>(is, io::BASE64);
++  }
++
++  return *cert;
++}
++
++std::string
++DummyPib::getScheme()
++{
++  return DummyPib::SCHEME;
++}
++
++//////////////////////////////////////////////////////////////////////////////////////////
++//////////////////////////////////////////////////////////////////////////////////////////
++//////////////////////////////////////////////////////////////////////////////////////////
++//////////////////////////////////////////////////////////////////////////////////////////
++
++namespace tpm {
++
++DummyKeyHandle::DummyKeyHandle(shared_ptr<transform::PrivateKey> key)
++{
++}
++
++ConstBufferPtr
++DummyKeyHandle::doSign(DigestAlgorithm digestAlgorithm, const uint8_t* buf, size_t size) const
++{
++  return make_shared<Buffer>(DUMMY_SIGNATURE, sizeof(DUMMY_SIGNATURE));
++}
++
++bool
++DummyKeyHandle::doVerify(DigestAlgorithm digestAlgorithm, const uint8_t* buf, size_t bufLen,
++                         const uint8_t* sig, size_t sigLen) const
++{
++  return true;
++}
++
++ConstBufferPtr
++DummyKeyHandle::doDecrypt(const uint8_t* cipherText, size_t cipherTextLen) const
++{
++  throw Error("Not supported");
++}
++
++ConstBufferPtr
++DummyKeyHandle::doDerivePublicKey() const
++{
++  throw Error("Not supported");
++}
++
++} // namespace tpm
++
++//////////////////////////////////////////////////////////////////////////////////////////
++//////////////////////////////////////////////////////////////////////////////////////////
++//////////////////////////////////////////////////////////////////////////////////////////
++//////////////////////////////////////////////////////////////////////////////////////////
++
++DummyTpm::DummyTpm(const std::string& locator)
++{
++}
++
++bool
++DummyTpm::isTerminalMode() const
++{
++  return false;
++}
++
++void
++DummyTpm::setTerminalMode(bool isTerminal) const
++{
++}
++
++bool
++DummyTpm::isTpmLocked() const
++{
++  return false;
++}
++
++bool
++DummyTpm::doHasKey(const Name& keyName) const
++{
++  return false;
++}
++
++unique_ptr<tpm::KeyHandle>
++DummyTpm::doGetKeyHandle(const Name& keyName) const
++{
++  unique_ptr<tpm::KeyHandle> m_dummyKeyHandle = make_unique<tpm::DummyKeyHandle>(nullptr);
++  return m_dummyKeyHandle;
++}
++
++unique_ptr<tpm::KeyHandle>
++DummyTpm::doCreateKey(const Name& identity, const KeyParams& params)
++{
++  unique_ptr<tpm::KeyHandle> m_dummyKeyHandle = make_unique<tpm::DummyKeyHandle>(nullptr);
++  return m_dummyKeyHandle;
++}
++
++void
++DummyTpm::doDeleteKey(const Name& keyName)
++{
++  throw Error("Not supported");
++}
++
++ConstBufferPtr
++DummyTpm::doExportKey(const Name& keyName, const char* pw, size_t pwLen)
++{
++  throw Error("Not supported");
++}
++
++void
++DummyTpm::doImportKey(const Name& keyName, const uint8_t* pkcs8, size_t pkcs8Len, const char* pw, size_t pwLen)
++{
++  throw Error("Not supported");
++}
++
++std::string
++DummyTpm::getScheme()
++{
++  return DummyTpm::SCHEME;
++}
++
++} // namespace security
++} // namespace ndn
+diff --git a/ndn-cxx/util/dummy-keychain.hpp b/ndn-cxx/util/dummy-keychain.hpp
+new file mode 100644
+index 0000000..d1432a6
+--- /dev/null
++++ b/ndn-cxx/util/dummy-keychain.hpp
+@@ -0,0 +1,223 @@
++/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
++/**
++ * Copyright (c) 2011-2015  Regents of the University of California.
++ *
++ * This file is part of ndnSIM. See AUTHORS for complete list of ndnSIM authors and
++ * contributors.
++ *
++ * ndnSIM is free software: you can redistribute it and/or modify it under the terms
++ * of the GNU General Public License as published by the Free Software Foundation,
++ * either version 3 of the License, or (at your option) any later version.
++ *
++ * ndnSIM is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
++ * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
++ * PURPOSE.  See the GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License along with
++ * ndnSIM, e.g., in COPYING.md file.  If not, see <http://www.gnu.org/licenses/>.
++ **/
++
++#ifndef NDNSIM_UTILS_DUMMY_KEYCHAIN_HPP
++#define NDNSIM_UTILS_DUMMY_KEYCHAIN_HPP
++
++#include <ndn-cxx/security/key-chain.hpp>
++#include <ndn-cxx/security/security-common.hpp>
++#include <ndn-cxx/security/pib/pib-impl.hpp>
++#include <ndn-cxx/security/tpm/back-end.hpp>
++#include <ndn-cxx/security/transform/private-key.hpp>
++
++namespace ndn {
++namespace security {
++
++using pib::PibImpl;
++using tpm::BackEnd;
++using tpm::KeyHandle;
++
++class DummyPib : public PibImpl
++{
++public:
++  class Error : public PibImpl::Error
++  {
++  public:
++    explicit
++    Error(const std::string& what)
++      : PibImpl::Error(what)
++    {
++    }
++  };
++
++public:
++  explicit DummyPib(const std::string& locator);
++
++  // TPM management
++  void
++  setTpmLocator(const std::string& tpmLocator) override;
++
++  std::string
++  getTpmLocator() const override;
++
++  // Identity manangement
++  bool
++  hasIdentity(const Name& identityName) const override;
++
++  void
++  addIdentity(const Name& identityName) override;
++
++  void
++  removeIdentity(const Name& identity) override;
++
++  void
++  clearIdentities() override;
++
++  std::set<Name>
++  getIdentities() const override;
++
++  void
++  setDefaultIdentity(const Name& identityName) override;
++
++  Name
++  getDefaultIdentity() const override;
++
++  // Key management
++  bool
++  hasKey(const Name& keyName) const override;
++
++  void
++  addKey(const Name& identity, const Name& keyName, const uint8_t* key,
++         size_t keyLen) override;
++
++  void
++  removeKey(const Name& keyName) override;
++
++  Buffer
++  getKeyBits(const Name& keyName) const override;
++
++  std::set<Name>
++  getKeysOfIdentity(const Name& identity) const override;
++
++  void
++  setDefaultKeyOfIdentity(const Name& identity, const Name& keyName) override;
++
++  Name
++  getDefaultKeyOfIdentity(const Name& identity) const override;
++
++  // certificate management
++  bool
++  hasCertificate(const Name& certName) const override;
++
++  void
++  addCertificate(const v2::Certificate& certificate) override;
++
++  void
++  removeCertificate(const Name& certName) override;
++
++  v2::Certificate
++  getCertificate(const Name& certificateName) const override;
++
++  std::set<Name>
++  getCertificatesOfKey(const Name& keyName) const override;
++
++  void
++  setDefaultCertificateOfKey(const Name& keyName, const Name& certName) override;
++
++  v2::Certificate
++  getDefaultCertificateOfKey(const Name& keyName) const override;
++
++  static std::string
++  getScheme();
++
++  static const std::string SCHEME;
++
++private:
++  std::string m_tpmLocator;
++};
++
++//////////////////////////////////////////////////////////////////////////////////////////
++//////////////////////////////////////////////////////////////////////////////////////////
++
++namespace tpm {
++
++class DummyKeyHandle : public KeyHandle
++{
++public:
++  explicit
++  DummyKeyHandle(shared_ptr<transform::PrivateKey> key);
++
++private:
++  ConstBufferPtr
++  doSign(DigestAlgorithm digestAlgorithm, const uint8_t* buf, size_t size) const final;
++
++  bool
++  doVerify(DigestAlgorithm digestAlgorithm, const uint8_t* buf, size_t bufLen,
++           const uint8_t* sig, size_t sigLen) const final;
++
++  ConstBufferPtr
++  doDecrypt(const uint8_t* cipherText, size_t cipherTextLen) const final;
++
++  ConstBufferPtr
++  doDerivePublicKey() const final;
++};
++
++} // namespace tpm
++
++//////////////////////////////////////////////////////////////////////////////////////////
++//////////////////////////////////////////////////////////////////////////////////////////
++
++class DummyTpm : public BackEnd
++{
++public:
++  class Error : public BackEnd::Error
++  {
++  public:
++    explicit
++    Error(const std::string& what)
++      : BackEnd::Error(what)
++    {
++    }
++  };
++
++public:
++  explicit DummyTpm(const std::string& locator);
++
++  bool
++  isTerminalMode() const override;
++
++  void
++  setTerminalMode(bool isTerminal) const override;
++
++  bool
++  isTpmLocked() const override;
++
++  ConstBufferPtr
++  sign(const uint8_t* buf, size_t size, const Name& keyName, DigestAlgorithm digestAlgorithm) const;
++
++  static std::string
++  getScheme();
++
++private:
++  bool
++  doHasKey(const Name& keyName) const final;
++
++  unique_ptr<tpm::KeyHandle>
++  doGetKeyHandle(const Name& keyName) const final;
++
++  unique_ptr<tpm::KeyHandle>
++  doCreateKey(const Name& identity, const KeyParams& params) final;
++
++  void
++  doDeleteKey(const Name& keyName) final;
++
++  ConstBufferPtr
++  doExportKey(const Name& keyName, const char* pw, size_t pwLen) final;
++
++  void
++  doImportKey(const Name& keyName, const uint8_t* pkcs8, size_t pkcs8Len, const char* pw, size_t pwLen) final;
++
++public:
++  static const std::string SCHEME;
++};
++
++} // namespace security
++} // namespace ndn
++
++#endif // NDNSIM_UTILS_DUMMY_KEYCHAIN_HPP
diff --git a/requirements.txt b/requirements.txt
index be5de0f..12598fb 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -1 +1,2 @@
-python-igraph
\ No newline at end of file
+python-igraph
+setuptools
\ No newline at end of file
diff --git a/setup.py b/setup.py
index bac5451..328a68a 100644
--- a/setup.py
+++ b/setup.py
@@ -1,10 +1,13 @@
 #!/usr/bin/env python
 
 from setuptools import setup, find_packages
+from minindn import __version__
 
 setup(
     name = "Mini-NDN",
-    version = '0.4.0',
+    version = __version__,
+    description='Mininet based NDN emulator',
     packages = find_packages(),
-    scripts = ['bin/minindn', 'bin/minindnedit'],
 )
+
+print(find_packages())