blob: e08188346c0bf2ea74031e62aa0815b101a428f8 [file] [log] [blame]
# -*- Mode:python; c-file-style:"gnu"; indent-tabs-mode:nil -*- */
#
# Copyright (C) 2015 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.
from mininet.topo import Topo
from mininet.net import Mininet
from mininet.log import setLogLevel, output, info
from mininet.cli import CLI
from mininet.link import TCLink
from mininet.util import ipStr, ipParse
from ndn import ExperimentManager
from ndn.ndn_host import NdnHost, CpuLimitedNdnHost
from ndn.conf_parser import parse_hosts, parse_switches, parse_links
import os.path, time
import optparse
import datetime
from os.path import expanduser
import sys
from ndn.nlsr import Nlsr, NlsrConfigGenerator
from ndn.nfd import Nfd
VERSION_NUMBER = "0.1.0"
def printExperimentNames(option, opt, value, parser):
experimentNames = ExperimentManager.getExperimentNames()
print "Mini-NDN experiments:"
for experiment in experimentNames:
print " %s" % experiment
sys.exit()
def printVersion(option, opt, value, parser):
print "Mini-NDN v%s" % VERSION_NUMBER
sys.exit()
class ProgramOptions:
def __init__(self):
self.ctime = 60
self.experimentName = None
self.nFaces = 3
self.templateFile = "minindn.conf"
self.hr = False
self.isCliEnabled = True
self.nPings = 300
self.testbed = False
self.workDir = "/tmp"
self.resultDir = None
self.pctTraffic = 1.0
def createResultsDir(resultDir, faces, hr):
if faces == 0:
faces = "all"
if hr:
routingType = "/hr/"
else:
routingType = "/ls/"
resultDir = "%s/%s/faces-%s" % (str(resultDir), routingType, str(faces))
resultDir = os.path.abspath(resultDir)
if not os.path.isdir(resultDir):
os.makedirs(resultDir)
else:
print("Results directory (%s) already exists!" % resultDir)
sys.exit(1);
print "Results will be stored at: %s" % resultDir
return resultDir
def parse_args():
usage = """Usage: minindn [template_file] [ -t | --testbed ]
If no template_file is given, ndn_utils/default-topology.conf (given sample file)
will be used.
If --testbed is used, minindn will run the NDN Project Testbed.
"""
parser = optparse.OptionParser(usage)
parser.add_option("--ctime", action="store", dest="ctime", type="int", default=60,
help="Specify convergence time for the topology (Default: 60 seconds)")
parser.add_option("--experiment", action="store", dest="experiment",
help="Runs the specified experiment")
parser.add_option("--faces", action="store", dest="faces", type="int", default=3,
help="Specify number of faces 0-60")
parser.add_option("--hr", action="store_true", dest="hr", default=False,
help="--hr is used to turn on hyperbolic routing")
parser.add_option("--list-experiments", action="callback", callback=printExperimentNames,
help="Lists the names of all available experiments")
parser.add_option("--no-cli", action="store_false", dest="isCliEnabled", default=True,
help="Run experiments and exit without showing the command line interface")
parser.add_option("--nPings", action="store", dest="nPings", type="int", default=300,
help="Number of pings to perform between each node in the experiment")
parser.add_option("-t", "--testbed", action="store_true", dest="testbed", default=False,
help="instantiates NDN Testbed")
parser.add_option("--work-dir", action="store", dest="workDir", default="/tmp",
help="Specify the working directory; default is /tmp")
parser.add_option("--result-dir", action="store", dest="resultDir", default=None,
help="Specify the full path destination folder where experiment results will be moved")
parser.add_option("--pct-traffic", action="store", dest="pctTraffic", type="float", default=1.0,
help="Specify the percentage of nodes each node should ping")
parser.add_option('--version', '-V', action='callback', callback=printVersion,
help='Displays version information')
(args, arg) = parser.parse_args()
options = ProgramOptions()
options.ctime = args.ctime
options.experimentName = args.experiment
options.nFaces = args.faces
options.hr = args.hr
options.isCliEnabled = args.isCliEnabled
options.nPings = args.nPings
options.testbed = args.testbed
options.workDir = args.workDir
options.resultDir = args.resultDir
options.pctTraffic = args.pctTraffic
if options.experimentName is not None and options.experimentName not in ExperimentManager.getExperimentNames():
print("No experiment named %s" % options.experimentName)
sys.exit()
if options.experimentName is not None and options.resultDir is None:
print "No results folder specified; experiment results will remain in the working directory"
if len(arg) == 0 or len(arg) > 2:
options.templateFile = ''
else:
options.templateFile = arg[0]
return options
class NdnTopo(Topo):
def __init__(self, conf_arq, workDir, **opts):
Topo.__init__(self, **opts)
global hosts_conf
global links_conf
hosts_conf = parse_hosts(conf_arq)
switches_conf = parse_switches(conf_arq)
links_conf = parse_links(conf_arq)
self.isTCLink = False
self.isLimited = False
for host in hosts_conf:
if host.cpu != None and self.isLimited != True:
self.isLimited = True
self.addHost(host.name, app=host.app, params=host.uri_tuples, cpu=host.cpu,cores=host.cores,cache=host.cache, workdir=workDir)
for switch in switches_conf:
self.addSwitch(switch.name)
for link in 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"
template_file = options.templateFile
install_dir='/usr/local/etc/mini-ndn/'
if template_file == '':
template_file = install_dir + 'default-topology.conf'
if options.testbed:
template_file = install_dir + 'minindn.testbed.conf'
if os.path.exists(template_file) == False:
info('No template file given and default template file cannot be found. Exiting...\n')
quit()
# Update nfd.conf file used by Mini-NDN to match the currently installed version of NFD
nfdConfFile = "%s/nfd.conf" % install_dir
os.system("sudo cp /usr/local/etc/ndn/nfd.conf.sample %s" % nfdConfFile)
os.system("sudo sed -i \'s|default_level [A-Z]*$|default_level $LOG_LEVEL|g\' %s" % nfdConfFile)
if options.resultDir is not None:
options.resultDir = createResultsDir(options.resultDir, options.nFaces, options.hr)
topo = NdnTopo(template_file, options.workDir)
t = datetime.datetime.now()
if topo.isTCLink == True and topo.isLimited == True:
net = Mininet(topo,host=CpuLimitedNdnHost,link=TCLink)
elif topo.isTCLink == True and topo.isLimited == False:
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)
t2 = datetime.datetime.now()
delta = t2 - t
info('Setup time: ' + str(delta.seconds) + '\n')
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)
nodes = "" # Used later to check prefix name in checkFIB
# NLSR initialization
for host in net.hosts:
nodes += str(host.name) + ","
conf = next(x for x in hosts_conf if x.name == host.name)
host.nlsrParameters = conf.nlsrParameters
if options.nFaces is not None:
host.nlsrParameters["max-faces-per-prefix"] = options.nFaces
if options.hr is True:
host.nlsrParameters["hyperbolic-state"] = "on"
# Generate NLSR configuration file
configGenerator = NlsrConfigGenerator(host)
configGenerator.createConfigFile()
# Start NLSR
host.nlsr = Nlsr(host)
host.nlsr.start()
nodes = nodes[0:-1]
for host in net.hosts:
if 'app' in host.params:
if host.params['app'] != '':
app = host.params['app']
print "Starting " + app + " on node " + host.name
print(host.cmd(app))
# Load experiment
experimentName = options.experimentName
if experimentName is not None:
print "Loading experiment: %s" % experimentName
experimentArgs = {
"net": net,
"nodes": nodes,
"ctime": options.ctime,
"nPings": options.nPings,
"strategy": Nfd.STRATEGY_BEST_ROUTE_V3,
"pctTraffic": options.pctTraffic
}
experiment = ExperimentManager.create(experimentName, experimentArgs)
if experiment is not None:
experiment.start()
else:
print "ERROR: Experiment '%s' does not exist" % experimentName
return
if options.isCliEnabled is True:
CLI(net)
net.stop()
if options.resultDir is not None:
print("Moving results to %s" % options.resultDir)
os.system("sudo mv /%s/* %s" % (options.workDir, options.resultDir))
if __name__ == '__main__':
hosts_conf = []
links_conf = []
options = parse_args()
setLogLevel('info')
execute(options)