initial import (Boeing r1752, NRL r878)

This commit is contained in:
ahrenholz 2013-08-29 14:21:13 +00:00
commit f8f46d28be
394 changed files with 99738 additions and 0 deletions

View file

@ -0,0 +1,74 @@
#!/usr/bin/python
#
# Copyright (c)2011-2012 the Boeing Company.
# See the LICENSE file included in this distribution.
#
# Test 3D range calculation of the BasicRangeModel by adding n nodes to a WLAN
# stacked 100 units above each other (using z-axis).
#
import optparse, sys, os, datetime, time
from core import pycore
from core.misc import ipaddr
from core.misc.utils import mutecall
from core.mobility import BasicRangeModel
from core.netns.vnet import EbtablesQueue
def test(numnodes):
# node list
n = []
prefix = ipaddr.IPv4Prefix("10.83.0.0/16")
session = pycore.Session(persistent = True)
wlanid = numnodes + 1
net = session.addobj(cls = pycore.nodes.WlanNode, name = "wlan%d" % wlanid,
objid = wlanid, verbose = True)
net.setmodel(BasicRangeModel, BasicRangeModel.getdefaultvalues())
for i in xrange(1, numnodes + 1):
tmp = session.addobj(cls = pycore.nodes.LxcNode, name = "n%d" % i,
objid = i)
tmp.newnetif(net, ["%s/%s" % (prefix.addr(i), prefix.prefixlen)])
# set increasing Z coordinates
tmp.setposition(10, 10, 100*i)
n.append(tmp)
n[0].term("bash")
# wait for rate seconds to allow ebtables commands to commit
time.sleep(EbtablesQueue.rate)
#session.shutdown()
def main():
usagestr = "usage: %prog [-h] [options] [args]"
parser = optparse.OptionParser(usage = usagestr)
parser.set_defaults(numnodes = 2)
parser.add_option("-n", "--numnodes", dest = "numnodes", type = int,
help = "number of nodes to test; default = %s" %
parser.defaults["numnodes"])
def usage(msg = None, err = 0):
sys.stdout.write("\n")
if msg:
sys.stdout.write(msg + "\n\n")
parser.print_help()
sys.exit(err)
# parse command line options
(options, args) = parser.parse_args()
if options.numnodes < 2:
usage("invalid number of nodes: %s" % options.numnodes)
for a in args:
sys.stderr.write("ignoring command line argument: '%s'\n" % a)
start = datetime.datetime.now()
test(options.numnodes)
print >> sys.stderr, \
"elapsed time: %s" % (datetime.datetime.now() - start)
if __name__ == "__main__":
main()

View file

@ -0,0 +1,99 @@
#!/usr/bin/python -i
# Copyright (c)2010-2013 the Boeing Company.
# See the LICENSE file included in this distribution.
# Example CORE Python script that attaches N nodes to an EMANE 802.11abg
# network. One of the parameters is changed, the pathloss mode.
import sys, datetime, optparse
from core import pycore
from core.misc import ipaddr
from core.constants import *
from core.emane.ieee80211abg import EmaneIeee80211abgModel
# node list (count from 1)
n = [None]
def add_to_server(session):
''' Add this session to the server's list if this script is executed from
the core-daemon server.
'''
global server
try:
server.addsession(session)
return True
except NameError:
return False
def main():
usagestr = "usage: %prog [-h] [options] [args]"
parser = optparse.OptionParser(usage = usagestr)
parser.set_defaults(numnodes = 5)
parser.add_option("-n", "--numnodes", dest = "numnodes", type = int,
help = "number of nodes")
def usage(msg = None, err = 0):
sys.stdout.write("\n")
if msg:
sys.stdout.write(msg + "\n\n")
parser.print_help()
sys.exit(err)
# parse command line options
(options, args) = parser.parse_args()
if options.numnodes < 1:
usage("invalid number of nodes: %s" % options.numnodes)
for a in args:
sys.stderr.write("ignoring command line argument: '%s'\n" % a)
start = datetime.datetime.now()
# IP subnet
prefix = ipaddr.IPv4Prefix("10.83.0.0/16")
# session with some EMANE initialization
session = pycore.Session(persistent=True)
session.master = True
session.location.setrefgeo(47.57917,-122.13232,2.00000)
session.location.refscale = 150.0
session.cfg['emane_models'] = "RfPipe, Ieee80211abg, Bypass, AtdlOmni"
session.emane.loadmodels()
add_to_server(session)
# EMANE WLAN
print "creating EMANE WLAN wlan1"
wlan = session.addobj(cls = pycore.nodes.EmaneNode, name = "wlan1")
wlan.setposition(x=80,y=50)
names = EmaneIeee80211abgModel.getnames()
values = list(EmaneIeee80211abgModel.getdefaultvalues())
# TODO: change any of the EMANE 802.11 parameter values here
values[ names.index('pathlossmode') ] = 'pathloss'
session.emane.setconfig(wlan.objid, EmaneIeee80211abgModel._name, values)
services_str = "zebra|OSPFv3MDR|vtysh|IPForward"
print "creating %d nodes with addresses from %s" % \
(options.numnodes, prefix)
for i in xrange(1, options.numnodes + 1):
tmp = session.addobj(cls = pycore.nodes.CoreNode, name = "n%d" % i,
objid=i)
tmp.newnetif(wlan, ["%s/%s" % (prefix.addr(i), prefix.prefixlen)])
tmp.cmd([SYSCTL_BIN, "net.ipv4.icmp_echo_ignore_broadcasts=0"])
tmp.setposition(x=150*i,y=150)
session.services.addservicestonode(tmp, "", services_str, verbose=False)
n.append(tmp)
# this starts EMANE, etc.
session.instantiate()
# start a shell on node 1
n[1].term("bash")
print "elapsed time: %s" % (datetime.datetime.now() - start)
if __name__ == "__main__" or __name__ == "__builtin__":
main()

View file

@ -0,0 +1,209 @@
#!/usr/bin/python
# Copyright (c)2010-2012 the Boeing Company.
# See the LICENSE file included in this distribution.
#
# author: Jeff Ahrenholz <jeffrey.m.ahrenholz@boeing.com>
#
'''
howmanynodes.py - This is a CORE script that creates network namespace nodes
having one virtual Ethernet interface connected to a bridge. It continues to
add nodes until an exception occurs. The number of nodes per bridge can be
specified.
'''
import optparse, sys, os, datetime, time, shutil
try:
from core import pycore
except ImportError:
# hack for Fedora autoconf that uses the following pythondir:
if "/usr/lib/python2.6/site-packages" in sys.path:
sys.path.append("/usr/local/lib/python2.6/site-packages")
if "/usr/lib64/python2.6/site-packages" in sys.path:
sys.path.append("/usr/local/lib64/python2.6/site-packages")
if "/usr/lib/python2.7/site-packages" in sys.path:
sys.path.append("/usr/local/lib/python2.7/site-packages")
if "/usr/lib64/python2.7/site-packages" in sys.path:
sys.path.append("/usr/local/lib64/python2.7/site-packages")
from core import pycore
from core.misc import ipaddr
from core.constants import *
GBD = 1024.0 * 1024.0
def linuxversion():
''' Return a string having the Linux kernel version.
'''
f = open('/proc/version', 'r')
v = f.readline().split()
version_str = ' '.join(v[:3])
f.close()
return version_str
MEMKEYS = ('total', 'free', 'buff', 'cached', 'stotal', 'sfree')
def memfree():
''' Returns kilobytes memory [total, free, buff, cached, stotal, sfree].
useful stats are:
free memory = free + buff + cached
swap used = stotal - sfree
'''
f = open('/proc/meminfo', 'r')
lines = f.readlines()
f.close()
kbs = {}
for k in MEMKEYS:
kbs[k] = 0
for l in lines:
if l[:9] == "MemTotal:":
kbs['total'] = int(l.split()[1])
elif l[:8] == "MemFree:":
kbs['free'] = int(l.split()[1])
elif l[:8] == "Buffers:":
kbs['buff'] = int(l.split()[1])
elif l[:8] == "Cached:":
kbs['cache'] = int(l.split()[1])
elif l[:10] == "SwapTotal:":
kbs['stotal'] = int(l.split()[1])
elif l[:9] == "SwapFree:":
kbs['sfree'] = int(l.split()[1])
break
return kbs
# node list (count from 1)
nodelist = [None]
switchlist = []
def main():
usagestr = "usage: %prog [-h] [options] [args]"
parser = optparse.OptionParser(usage = usagestr)
parser.set_defaults(waittime = 0.2, numnodes = 0, bridges = 0, retries = 0,
logfile = None, services = None)
parser.add_option("-w", "--waittime", dest = "waittime", type = float,
help = "number of seconds to wait between node creation" \
" (default = %s)" % parser.defaults["waittime"])
parser.add_option("-n", "--numnodes", dest = "numnodes", type = int,
help = "number of nodes (default = unlimited)")
parser.add_option("-b", "--bridges", dest = "bridges", type = int,
help = "number of nodes per bridge; 0 = one bridge " \
"(def. = %s)" % parser.defaults["bridges"])
parser.add_option("-r", "--retry", dest = "retries", type = int,
help = "number of retries on error (default = %s)" % \
parser.defaults["retries"])
parser.add_option("-l", "--log", dest = "logfile", type = str,
help = "log memory usage to this file (default = %s)" % \
parser.defaults["logfile"])
parser.add_option("-s", "--services", dest = "services", type = str,
help = "pipe-delimited list of services added to each " \
"node (default = %s)\n(Example: 'zebra|OSPFv2|OSPFv3|" \
"vtysh|IPForward')" % parser.defaults["services"])
def usage(msg = None, err = 0):
sys.stdout.write("\n")
if msg:
sys.stdout.write(msg + "\n\n")
parser.print_help()
sys.exit(err)
(options, args) = parser.parse_args()
for a in args:
sys.stderr.write("ignoring command line argument: '%s'\n" % a)
start = datetime.datetime.now()
prefix = ipaddr.IPv4Prefix("10.83.0.0/16")
print "Testing how many network namespace nodes this machine can create."
print " - %s" % linuxversion()
mem = memfree()
print " - %.02f GB total memory (%.02f GB swap)" % \
(mem['total']/GBD, mem['stotal']/GBD)
print " - using IPv4 network prefix %s" % prefix
print " - using wait time of %s" % options.waittime
print " - using %d nodes per bridge" % options.bridges
print " - will retry %d times on failure" % options.retries
print " - adding these services to each node: %s" % options.services
print " "
lfp = None
if options.logfile is not None:
# initialize a csv log file header
lfp = open(options.logfile, "a")
lfp.write("# log from howmanynodes.py %s\n" % time.ctime())
lfp.write("# options = %s\n#\n" % options)
lfp.write("# numnodes,%s\n" % ','.join(MEMKEYS))
lfp.flush()
session = pycore.Session(persistent=True)
switch = session.addobj(cls = pycore.nodes.SwitchNode)
switchlist.append(switch)
print "Added bridge %s (%d)." % (switch.brname, len(switchlist))
i = 0
retry_count = options.retries
while True:
i += 1
# optionally add a bridge (options.bridges nodes per bridge)
try:
if options.bridges > 0 and switch.numnetif() >= options.bridges:
switch = session.addobj(cls = pycore.nodes.SwitchNode)
switchlist.append(switch)
print "\nAdded bridge %s (%d) for node %d." % \
(switch.brname, len(switchlist), i)
except Exception, e:
print "At %d bridges (%d nodes) caught exception:\n%s\n" % \
(len(switchlist), i-1, e)
break
# create a node
try:
n = session.addobj(cls = pycore.nodes.LxcNode, name = "n%d" % i)
n.newnetif(switch, ["%s/%s" % (prefix.addr(i), prefix.prefixlen)])
n.cmd([SYSCTL_BIN, "net.ipv4.icmp_echo_ignore_broadcasts=0"])
if options.services is not None:
session.services.addservicestonode(n, "", options.services,
verbose=False)
n.boot()
nodelist.append(n)
if i % 25 == 0:
print "\n%s nodes created " % i,
mem = memfree()
free = mem['free'] + mem['buff'] + mem['cached']
swap = mem['stotal'] - mem['sfree']
print "(%.02f/%.02f GB free/swap)" % (free/GBD , swap/GBD),
if lfp:
lfp.write("%d," % i)
lfp.write("%s\n" % ','.join(str(mem[x]) for x in MEMKEYS))
lfp.flush()
else:
sys.stdout.write(".")
sys.stdout.flush()
time.sleep(options.waittime)
except Exception, e:
print "At %d nodes caught exception:\n" % i, e
if retry_count > 0:
print "\nWill retry creating node %d." % i
shutil.rmtree(n.nodedir, ignore_errors = True)
retry_count -= 1
i -= 1
time.sleep(options.waittime)
continue
else:
print "Stopping at %d nodes!" % i
break
if i == options.numnodes:
print "Stopping at %d nodes due to numnodes option." % i
break
# node creation was successful at this point
retry_count = options.retries
if lfp:
lfp.flush()
lfp.close()
print "elapsed time: %s" % (datetime.datetime.now() - start)
print "Use the core-cleanup script to remove nodes and bridges."
if __name__ == "__main__":
main()

View file

@ -0,0 +1,109 @@
#!/usr/bin/python
# Copyright (c)2013 the Boeing Company.
# See the LICENSE file included in this distribution.
# This script creates a CORE session, that will connect n nodes together
# in a chain, with static routes between nodes
# number of nodes / number of hops
# 2 0
# 3 1
# 4 2
# n n - 2
#
# Use core-cleanup to clean up after this script as the session is left running.
#
import sys, datetime, optparse
from core import pycore
from core.misc import ipaddr
from core.constants import *
# node list (count from 1)
n = [None]
def add_to_server(session):
''' Add this session to the server's list if this script is executed from
the core-daemon server.
'''
global server
try:
server.addsession(session)
return True
except NameError:
return False
def main():
usagestr = "usage: %prog [-h] [options] [args]"
parser = optparse.OptionParser(usage = usagestr)
parser.set_defaults(numnodes = 5)
parser.add_option("-n", "--numnodes", dest = "numnodes", type = int,
help = "number of nodes")
def usage(msg = None, err = 0):
sys.stdout.write("\n")
if msg:
sys.stdout.write(msg + "\n\n")
parser.print_help()
sys.exit(err)
# parse command line options
(options, args) = parser.parse_args()
if options.numnodes < 1:
usage("invalid number of nodes: %s" % options.numnodes)
if options.numnodes >= 255:
usage("invalid number of nodes: %s" % options.numnodes)
for a in args:
sys.stderr.write("ignoring command line argument: '%s'\n" % a)
start = datetime.datetime.now()
session = pycore.Session(persistent=True)
add_to_server(session)
print "creating %d nodes" % options.numnodes
left = None
prefix = None
for i in xrange(1, options.numnodes + 1):
tmp = session.addobj(cls = pycore.nodes.CoreNode, name = "n%d" % i,
objid=i)
if left:
tmp.newnetif(left, ["%s/%s" % (prefix.addr(2), prefix.prefixlen)])
prefix = ipaddr.IPv4Prefix("10.83.%d.0/24" % i) # limit: i < 255
right = session.addobj(cls = pycore.nodes.PtpNet)
tmp.newnetif(right, ["%s/%s" % (prefix.addr(1), prefix.prefixlen)])
tmp.cmd([SYSCTL_BIN, "net.ipv4.icmp_echo_ignore_broadcasts=0"])
tmp.cmd([SYSCTL_BIN, "net.ipv4.conf.all.forwarding=1"])
tmp.cmd([SYSCTL_BIN, "net.ipv4.conf.default.rp_filter=0"])
tmp.setposition(x=100*i,y=150)
n.append(tmp)
left = right
prefixes = map(lambda(x): ipaddr.IPv4Prefix("10.83.%d.0/24" % x),
xrange(1, options.numnodes + 1))
# set up static routing in the chain
for i in xrange(1, options.numnodes + 1):
for j in xrange(1, options.numnodes + 1):
if j < i - 1:
gw = prefixes[i-2].addr(1)
elif j > i:
if i > len(prefixes) - 1:
continue
gw = prefixes[i-1].addr(2)
else:
continue
net = prefixes[j-1]
n[i].cmd([IP_BIN, "route", "add", str(net), "via", str(gw)])
print "elapsed time: %s" % (datetime.datetime.now() - start)
if __name__ == "__main__" or __name__ == "__builtin__":
main()

View file

@ -0,0 +1,284 @@
#!/bin/sh
#
# iperf-performance.sh
#
# (c)2013 the Boeing Company
# authors: Jeff Ahrenholz <jeffrey.m.ahrenholz@boeing.com>
#
# Utility script to automate several iperf runs.
#
# number of iperf runs per test
NUMRUNS=10
# number of seconds per run (10s is iperf default)
RUNTIME=10
# logging
LOG=/tmp/${0}.log
STAMP=`date +%Y%m%d%H%M%S`
#
# client---(loopback)---server
#
loopbacktest () {
killall iperf 2> /dev/null
echo ">> loopback iperf test"
echo "loopback" > ${LOG}
# start an iperf server in the background
# -s = server
# -y c = CSV output
echo "starting local iperf server"
iperf -s -y c >> ${LOG} &
# run an iperf client NUMRUNS times
i=1
while [ $i -le $NUMRUNS ]; do
echo "run $i/$NUMRUNS:"
iperf -t ${RUNTIME} -c localhost
sleep 0.3
i=$((i+1))
done
sleep 1
echo "stopping local iperf server"
killall -v iperf
}
#
# lxc1( client )---veth-pair---lxc2( server )
#
lxcvethtest () {
SERVERIP=10.0.0.1
CLIENTIP=10.0.0.2
SERVER=/tmp/${0}-server
CLIENT=/tmp/${0}-client
echo ">> lxc veth iperf test"
echo "lxcveth" >> ${LOG}
echo "starting lxc iperf server"
vnoded -l $SERVER.log -p $SERVER.pid -c $SERVER
ip link add name veth0.1 type veth peer name veth0
ip link set veth0 netns `cat $SERVER.pid`
vcmd -c $SERVER -- ifconfig veth0 $SERVERIP/24
vcmd -c $SERVER -- iperf -s -y c >> ${LOG} &
echo "starting lxc iperf client"
vnoded -l $CLIENT.log -p $CLIENT.pid -c $CLIENT
ip link set veth0.1 netns `cat $CLIENT.pid`
vcmd -c $CLIENT -- ifconfig veth0.1 $CLIENTIP/24
i=1
while [ $i -le $NUMRUNS ]; do
echo "run $i/$NUMRUNS:"
vcmd -c $CLIENT -- iperf -t ${RUNTIME} -c ${SERVERIP}
sleep 0.3
i=$((i+1))
done
sleep 1
echo "stopping lxc iperf server"
vcmd -c $SERVER -- killall -v iperf
echo "stopping containers"
kill -9 `cat $SERVER.pid`
kill -9 `cat $CLIENT.pid`
echo "cleaning up"
rm -f ${SERVER}*
rm -f ${CLIENT}*
}
#
# lxc1( client veth:):veth---bridge---veth:(:veth server )lxc2
#
lxcbrtest () {
SERVERIP=10.0.0.1
CLIENTIP=10.0.0.2
SERVER=/tmp/${0}-server
CLIENT=/tmp/${0}-client
BRIDGE="lxcbrtest"
echo ">> lxc bridge iperf test"
echo "lxcbr" >> ${LOG}
echo "building bridge"
brctl addbr $BRIDGE
brctl stp $BRIDGE off # disable spanning tree protocol
brctl setfd $BRIDGE 0 # disable forwarding delay
ip link set $BRIDGE up
echo "starting lxc iperf server"
vnoded -l $SERVER.log -p $SERVER.pid -c $SERVER
ip link add name veth0.1 type veth peer name veth0
ip link set veth0 netns `cat $SERVER.pid`
vcmd -c $SERVER -- ifconfig veth0 $SERVERIP/24
brctl addif $BRIDGE veth0.1
ip link set veth0.1 up
vcmd -c $SERVER -- iperf -s -y c >> ${LOG} &
echo "starting lxc iperf client"
vnoded -l $CLIENT.log -p $CLIENT.pid -c $CLIENT
ip link add name veth1.1 type veth peer name veth1
ip link set veth1 netns `cat $CLIENT.pid`
vcmd -c $CLIENT -- ifconfig veth1 $CLIENTIP/24
brctl addif $BRIDGE veth1.1
ip link set veth1.1 up
i=1
while [ $i -le $NUMRUNS ]; do
echo "run $i/$NUMRUNS:"
vcmd -c $CLIENT -- iperf -t ${RUNTIME} -c ${SERVERIP}
sleep 0.3
i=$((i+1))
done
sleep 1
echo "stopping lxc iperf server"
vcmd -c $SERVER -- killall -v iperf
echo "stopping containers"
kill -9 `cat $SERVER.pid`
kill -9 `cat $CLIENT.pid`
echo "cleaning up"
ip link set $BRIDGE down
brctl delbr $BRIDGE
rm -f ${SERVER}*
rm -f ${CLIENT}*
}
#
# n1---n2---n3--- ... ---nN
# N nodes (N-2 hops) in chain with static routing
#
chaintest () {
NUMNODES=$1
SERVERIP=10.83.$NUMNODES.1
if [ -d /tmp/pycore.* ]; then
echo "/tmp/pycore.* already exists, skipping chaintest $NUMNODES"
return
fi
echo ">> n=$NUMNODES node chain iperf test"
echo "chain$NUMNODES" >> ${LOG}
echo "running external chain CORE script with '-n $NUMNODES'"
python iperf-performance-chain.py -n $NUMNODES
echo "starting lxc iperf server on node $NUMNODES"
vcmd -c /tmp/pycore.*/n$NUMNODES -- iperf -s -y c >> ${LOG} &
echo "starting lxc iperf client"
i=1
while [ $i -le $NUMRUNS ]; do
echo "run $i/$NUMRUNS:"
vcmd -c /tmp/pycore.*/n1 -- iperf -t ${RUNTIME} -c ${SERVERIP}
sleep 0.3
i=$((i+1))
done
sleep 1
echo "stopping lxc iperf server"
vcmd -c /tmp/pycore.*/n$NUMNODES -- killall -v iperf
echo "cleaning up"
core-cleanup
}
if [ "z$1" != "z" ]; then
echo "This script takes no parameters and must be run as root."
exit 1
fi
if [ `id -u` != 0 ]; then
echo "This script must be run as root."
exit 1
fi
#
# N lxc clients >---bridge---veth:(:veth server )
#
clientstest () {
NUMCLIENTS=$1
SERVERIP=10.0.0.1
SERVER=/tmp/${0}-server
BRIDGE="lxcbrtest"
echo ">> n=$NUMCLIENTS clients iperf test"
echo "clients$NUMCLIENTS" >> ${LOG}
echo "building bridge"
brctl addbr $BRIDGE
brctl stp $BRIDGE off # disable spanning tree protocol
brctl setfd $BRIDGE 0 # disable forwarding delay
ip link set $BRIDGE up
echo "starting lxc iperf server"
vnoded -l $SERVER.log -p $SERVER.pid -c $SERVER
ip link add name veth0.1 type veth peer name veth0
ip link set veth0 netns `cat $SERVER.pid`
vcmd -c $SERVER -- ifconfig veth0 $SERVERIP/24
brctl addif $BRIDGE veth0.1
ip link set veth0.1 up
vcmd -c $SERVER -- iperf -s -y c >> ${LOG} &
i=1
CLIENTS=""
while [ $i -le $NUMCLIENTS ]; do
echo "starting lxc iperf client $i/$NUMCLIENTS"
CLIENT=/tmp/${0}-client$i
CLIENTIP=10.0.0.1$i
vnoded -l $CLIENT.log -p $CLIENT.pid -c $CLIENT
ip link add name veth1.$i type veth peer name veth1
ip link set veth1 netns `cat $CLIENT.pid`
vcmd -c $CLIENT -- ifconfig veth1 $CLIENTIP/24
brctl addif $BRIDGE veth1.$i
ip link set veth1.$i up
i=$((i+1))
CLIENTS="$CLIENTS $CLIENT"
done
j=1
while [ $j -le $NUMRUNS ]; do
echo "run $j/$NUMRUNS iperf:"
for CLIENT in $CLIENTS; do
vcmd -c $CLIENT -- iperf -t ${RUNTIME} -c ${SERVERIP} &
done
sleep ${RUNTIME} 1
j=$((j+1))
done
sleep 1
echo "stopping lxc iperf server"
vcmd -c $SERVER -- killall -v iperf
echo "stopping containers"
kill -9 `cat $SERVER.pid`
for CLIENT in $CLIENTS; do
kill -9 `cat $CLIENT.pid`
done
# time needed for processes/containers to shut down
sleep 2
echo "cleaning up"
ip link set $BRIDGE down
brctl delbr $BRIDGE
rm -f ${SERVER}*
rm -f /tmp/${0}-client*
# time needed for bridge clean-up
sleep 1
}
#
# run all tests
#
loopbacktest
lxcvethtest
lxcbrtest
chaintest 5
chaintest 10
clientstest 5
clientstest 10
clientstest 15
mv ${LOG} ${PWD}/${0}-${STAMP}.log
echo "===> results in ${PWD}/${0}-${STAMP}.log"

View file

@ -0,0 +1,572 @@
#!/usr/bin/python
# Copyright (c)2011-2012 the Boeing Company.
# See the LICENSE file included in this distribution.
# create a random topology running OSPFv3 MDR, wait and then check
# that all neighbor states are either full or two-way, and check the routes
# in zebra vs those installed in the kernel.
import os, sys, random, time, optparse, datetime
from string import Template
try:
from core import pycore
except ImportError:
# hack for Fedora autoconf that uses the following pythondir:
if "/usr/lib/python2.6/site-packages" in sys.path:
sys.path.append("/usr/local/lib/python2.6/site-packages")
if "/usr/lib64/python2.6/site-packages" in sys.path:
sys.path.append("/usr/local/lib64/python2.6/site-packages")
if "/usr/lib/python2.7/site-packages" in sys.path:
sys.path.append("/usr/local/lib/python2.7/site-packages")
if "/usr/lib64/python2.7/site-packages" in sys.path:
sys.path.append("/usr/local/lib64/python2.7/site-packages")
from core import pycore
from core.misc import ipaddr
from core.misc.utils import mutecall
from core.constants import QUAGGA_STATE_DIR
# sanity check that zebra is installed
try:
mutecall(["zebra", "-u", "root", "-g", "root", "-v"])
except OSError:
sys.stderr.write("ERROR: running zebra failed\n")
sys.exit(1)
class ManetNode(pycore.nodes.LxcNode):
""" An Lxc namespace node configured for Quagga OSPFv3 MANET MDR
"""
conftemp = Template("""\
interface eth0
ip address $ipaddr
ipv6 ospf6 instance-id 65
ipv6 ospf6 hello-interval 2
ipv6 ospf6 dead-interval 6
ipv6 ospf6 retransmit-interval 5
ipv6 ospf6 network manet-designated-router
ipv6 ospf6 diffhellos
ipv6 ospf6 adjacencyconnectivity biconnected
ipv6 ospf6 lsafullness mincostlsa
!
router ospf6
router-id $routerid
interface eth0 area 0.0.0.0
!
ip forwarding
""")
def __init__(self, core, ipaddr, routerid = None,
objid = None, name = None, nodedir = None):
if routerid is None:
routerid = ipaddr.split("/")[0]
self.ipaddr = ipaddr
self.routerid = routerid
pycore.nodes.LxcNode.__init__(self, core, objid, name, nodedir)
self.privatedir(self.confdir)
self.privatedir(QUAGGA_STATE_DIR)
def qconf(self):
return self.conftemp.substitute(ipaddr = self.ipaddr,
routerid = self.routerid)
def config(self):
filename = os.path.join(self.confdir, "Quagga.conf")
f = self.opennodefile(filename, "w")
f.write(self.qconf())
f.close()
pycore.nodes.LxcNode.config(self)
def bootscript(self):
return """\
#!/bin/sh -e
STATEDIR=%s
waitfile()
{
fname=$1
i=0
until [ -e $fname ]; do
i=$(($i + 1))
if [ $i -eq 10 ]; then
echo "file not found: $fname" >&2
exit 1
fi
sleep 0.1
done
}
mkdir -p $STATEDIR
zebra -d -u root -g root
waitfile $STATEDIR/zebra.vty
ospf6d -d -u root -g root
waitfile $STATEDIR/ospf6d.vty
vtysh -b
""" % QUAGGA_STATE_DIR
class Route(object):
""" Helper class for organzing routing table entries. """
def __init__(self, prefix = None, gw = None, metric = None):
try:
self.prefix = ipaddr.IPv4Prefix(prefix)
except Exception, e:
raise ValueError, "Invalid prefix given to Route object: %s\n%s" % \
(prefix, e)
self.gw = gw
self.metric = metric
def __eq__(self, other):
try:
return self.prefix == other.prefix and self.gw == other.gw and \
self.metric == other.metric
except:
return False
def __str__(self):
return "(%s,%s,%s)" % (self.prefix, self.gw, self.metric)
@staticmethod
def key(r):
if not r.prefix:
return 0
return r.prefix.prefix
class ManetExperiment(object):
""" A class for building an MDR network and checking and logging its state.
"""
def __init__(self, options, start):
""" Initialize with options and start time. """
self.session = None
# node list
self.nodes = []
# WLAN network
self.net = None
self.verbose = options.verbose
# dict from OptionParser
self.options = options
self.start = start
self.logbegin()
def info(self, msg):
''' Utility method for writing output to stdout. '''
print msg
sys.stdout.flush()
self.log(msg)
def warn(self, msg):
''' Utility method for writing output to stderr. '''
print >> sys.stderr, msg
sys.stderr.flush()
self.log(msg)
def logbegin(self):
""" Start logging. """
self.logfp = None
if not self.options.logfile:
return
self.logfp = open(self.options.logfile, "w")
self.log("ospfmanetmdrtest begin: %s\n" % self.start.ctime())
def logend(self):
""" End logging. """
if not self.logfp:
return
end = datetime.datetime.now()
self.log("ospfmanetmdrtest end: %s (%s)\n" % \
(end.ctime(), end - self.start))
self.logfp.flush()
self.logfp.close()
self.logfp = None
def log(self, msg):
""" Write to the log file, if any. """
if not self.logfp:
return
print >> self.logfp, msg
def logdata(self, nbrs, mdrs, lsdbs, krs, zrs):
""" Dump experiment parameters and data to the log file. """
self.log("ospfmantetmdrtest data:")
self.log("----- parameters -----")
self.log("%s" % self.options)
self.log("----- neighbors -----")
for rtrid in sorted(nbrs.keys()):
self.log("%s: %s" % (rtrid, nbrs[rtrid]))
self.log("----- mdr levels -----")
self.log(mdrs)
self.log("----- link state databases -----")
for rtrid in sorted(lsdbs.keys()):
self.log("%s lsdb:" % rtrid)
for line in lsdbs[rtrid].split("\n"):
self.log(line)
self.log("----- kernel routes -----")
for rtrid in sorted(krs.keys()):
msg = rtrid + ": "
for rt in krs[rtrid]:
msg += "%s" % rt
self.log(msg)
self.log("----- zebra routes -----")
for rtrid in sorted(zrs.keys()):
msg = rtrid + ": "
for rt in zrs[rtrid]:
msg += "%s" % rt
self.log(msg)
def topology(self, numnodes, linkprob, verbose = False):
""" Build a topology consisting of the given number of ManetNodes
connected to a WLAN and probabilty of links and set
the session, WLAN, and node list objects.
"""
# IP subnet
prefix = ipaddr.IPv4Prefix("10.14.0.0/16")
self.session = pycore.Session()
# emulated network
self.net = self.session.addobj(cls = pycore.nodes.WlanNode)
for i in xrange(1, numnodes + 1):
addr = "%s/%s" % (prefix.addr(i), 32)
tmp = self.session.addobj(cls = ManetNode, ipaddr = addr, name = "n%d" % i)
tmp.newnetif(self.net, [addr])
self.nodes.append(tmp)
# connect nodes with probability linkprob
for i in xrange(numnodes):
for j in xrange(i + 1, numnodes):
r = random.random()
if r < linkprob:
if self.verbose:
self.info("linking (%d,%d)" % (i, j))
self.net.link(self.nodes[i].netif(0), self.nodes[j].netif(0))
# force one link to avoid partitions (should check if this is needed)
j = i
while j == i:
j = random.randint(0, numnodes - 1)
if self.verbose:
self.info("linking (%d,%d)" % (i, j))
self.net.link(self.nodes[i].netif(0), self.nodes[j].netif(0))
self.nodes[i].boot()
# run the boot.sh script on all nodes to start Quagga
for i in xrange(numnodes):
self.nodes[i].cmd(["./%s" % self.nodes[i].bootsh])
def compareroutes(self, node, kr, zr):
""" Compare two lists of Route objects.
"""
kr.sort(key=Route.key)
zr.sort(key=Route.key)
if kr != zr:
self.warn("kernel and zebra routes differ")
if self.verbose:
msg = "kernel: "
for r in kr:
msg += "%s " % r
msg += "\nzebra: "
for r in zr:
msg += "%s " % r
self.warn(msg)
else:
self.info(" kernel and zebra routes match")
def comparemdrlevels(self, nbrs, mdrs):
""" Check that all routers form a connected dominating set, i.e. all
routers are either MDR, BMDR, or adjacent to one.
"""
msg = "All routers form a CDS"
for n in self.nodes:
if mdrs[n.routerid] != "OTHER":
continue
connected = False
for nbr in nbrs[n.routerid]:
if mdrs[nbr] == "MDR" or mdrs[nbr] == "BMDR":
connected = True
break
if not connected:
msg = "All routers do not form a CDS"
self.warn("XXX %s: not in CDS; neighbors: %s" % \
(n.routerid, nbrs[n.routerid]))
if self.verbose:
self.info(msg)
def comparelsdbs(self, lsdbs):
""" Check LSDBs for consistency.
"""
msg = "LSDBs of all routers are consistent"
prev = self.nodes[0]
for n in self.nodes:
db = lsdbs[n.routerid]
if lsdbs[prev.routerid] != db:
msg = "LSDBs of all routers are not consistent"
self.warn("XXX LSDBs inconsistent for %s and %s" % \
(n.routerid, prev.routerid))
i = 0
for entry in lsdbs[n.routerid].split("\n"):
preventries = lsdbs[prev.routerid].split("\n")
try:
preventry = preventries[i]
except IndexError:
preventry = None
if entry != preventry:
self.warn("%s: %s" % (n.routerid, entry))
self.warn("%s: %s" % (prev.routerid, preventry))
i += 1
prev = n
if self.verbose:
self.info(msg)
def checknodes(self):
""" Check the neighbor state and routing tables of all nodes. """
nbrs = {}
mdrs = {}
lsdbs = {}
krs = {}
zrs = {}
v = self.verbose
for n in self.nodes:
self.info("checking %s" % n.name)
nbrs[n.routerid] = Ospf6NeighState(n, verbose=v).run()
krs[n.routerid] = KernelRoutes(n, verbose=v).run()
zrs[n.routerid] = ZebraRoutes(n, verbose=v).run()
self.compareroutes(n, krs[n.routerid], zrs[n.routerid])
mdrs[n.routerid] = Ospf6MdrLevel(n, verbose=v).run()
lsdbs[n.routerid] = Ospf6Database(n, verbose=v).run()
self.comparemdrlevels(nbrs, mdrs)
self.comparelsdbs(lsdbs)
self.logdata(nbrs, mdrs, lsdbs, krs, zrs)
class Cmd:
""" Helper class for running a command on a node and parsing the result. """
args = ""
def __init__(self, node, verbose=False):
""" Initialize with a CoreNode (LxcNode) """
self.id = None
self.stdin = None
self.out = None
self.node = node
self.verbose = verbose
def info(self, msg):
''' Utility method for writing output to stdout.'''
print msg
sys.stdout.flush()
def warn(self, msg):
''' Utility method for writing output to stderr. '''
print >> sys.stderr, "XXX %s:" % self.node.routerid, msg
sys.stderr.flush()
def run(self):
""" This is the primary method used for running this command. """
self.open()
r = self.parse()
self.cleanup()
return r
def open(self):
""" Exceute call to node.popen(). """
self.id, self.stdin, self.out, self.err = \
self.node.popen((self.args))
def parse(self):
""" This method is overloaded by child classes and should return some
result.
"""
return None
def cleanup(self):
""" Close the Popen channels."""
self.stdin.close()
self.out.close()
self.err.close()
tmp = self.id.wait()
if tmp:
self.warn("nonzero exit status:", tmp)
class VtyshCmd(Cmd):
""" Runs a vtysh command. """
def open(self):
args = ("vtysh", "-c", self.args)
self.id, self.stdin, self.out, self.err = self.node.popen((args))
class Ospf6NeighState(VtyshCmd):
""" Check a node for OSPFv3 neighbors in the full/two-way states. """
args = "show ipv6 ospf6 neighbor"
def parse(self):
self.out.readline() # skip first line
nbrlist = []
for line in self.out:
field = line.split()
nbr = field[0]
state = field[3].split("/")[0]
if not state.lower() in ("full", "twoway"):
self.warn("neighbor %s state: %s" % (nbr, state))
nbrlist.append(nbr)
if len(nbrlist) == 0:
self.warn("no neighbors")
if self.verbose:
self.info(" %s has %d neighbors" % (self.node.routerid, len(nbrlist)))
return nbrlist
class Ospf6MdrLevel(VtyshCmd):
""" Retrieve the OSPFv3 MDR level for a node. """
args = "show ipv6 ospf6 mdrlevel"
def parse(self):
line = self.out.readline()
# TODO: handle multiple interfaces
field = line.split()
mdrlevel = field[4]
if not mdrlevel in ("MDR", "BMDR", "OTHER"):
self.warn("mdrlevel: %s" % mdrlevel)
if self.verbose:
self.info(" %s is %s" % (self.node.routerid, mdrlevel))
return mdrlevel
class Ospf6Database(VtyshCmd):
""" Retrieve the OSPFv3 LSDB summary for a node. """
args = "show ipv6 ospf6 database"
def parse(self):
db = ""
for line in self.out:
field = line.split()
if len(field) < 8:
continue
# filter out Age and Duration columns
filtered = field[:3] + field[4:7]
db += " ".join(filtered) + "\n"
return db
class ZebraRoutes(VtyshCmd):
""" Return a list of Route objects for a node based on its zebra
routing table.
"""
args = "show ip route"
def parse(self):
for i in xrange(0,3):
self.out.readline() # skip first three lines
r = []
prefix = None
for line in self.out:
field = line.split()
# only use OSPFv3 selected FIB routes
if field[0][:2] == "o>":
prefix = field[1]
metric = field[2].split("/")[1][:-1]
if field[0][2:] != "*":
continue
if field[3] == "via":
gw = field[4][:-1]
else:
gw = field[6][:-1]
r.append(Route(prefix, gw, metric))
prefix = None
elif prefix and field[0] == "*":
# already have prefix and metric from previous line
gw = field[2][:-1]
r.append(Route(prefix, gw, metric))
prefix = None
if len(r) == 0:
self.warn("no zebra routes")
if self.verbose:
self.info(" %s has %d zebra routes" % (self.node.routerid, len(r)))
return r
class KernelRoutes(Cmd):
""" Return a list of Route objects for a node based on its kernel
routing table.
"""
args = ("/sbin/ip", "route", "show")
def parse(self):
r = []
prefix = None
for line in self.out:
field = line.split()
if field[0] == "nexthop":
if not prefix:
# this saves only the first nexthop entry if multiple exist
continue
else:
prefix = field[0]
metric = field[-1]
tmp = prefix.split("/")
if len(tmp) < 2:
prefix += "/32"
if field[1] == "proto":
# nexthop entry is on the next line
continue
gw = field[2] # nexthop IP or interface
r.append(Route(prefix, gw, metric))
prefix = None
if len(r) == 0:
self.warn("no kernel routes")
if self.verbose:
self.info(" %s has %d kernel routes" % (self.node.routerid, len(r)))
return r
def main():
usagestr = "usage: %prog [-h] [options] [args]"
parser = optparse.OptionParser(usage = usagestr)
parser.set_defaults(numnodes = 10, linkprob = 0.35, delay = 20, seed = None)
parser.add_option("-n", "--numnodes", dest = "numnodes", type = int,
help = "number of nodes")
parser.add_option("-p", "--linkprob", dest = "linkprob", type = float,
help = "link probabilty")
parser.add_option("-d", "--delay", dest = "delay", type = float,
help = "wait time before checking")
parser.add_option("-s", "--seed", dest = "seed", type = int,
help = "specify integer to use for random seed")
parser.add_option("-v", "--verbose", dest = "verbose",
action = "store_true", help = "be more verbose")
parser.add_option("-l", "--logfile", dest = "logfile", type = str,
help = "log detailed output to the specified file")
def usage(msg = None, err = 0):
sys.stdout.write("\n")
if msg:
sys.stdout.write(msg + "\n\n")
parser.print_help()
sys.exit(err)
# parse command line options
(options, args) = parser.parse_args()
if options.numnodes < 2:
usage("invalid numnodes: %s" % options.numnodes)
if options.linkprob <= 0.0 or options.linkprob > 1.0:
usage("invalid linkprob: %s" % options.linkprob)
if options.delay < 0.0:
usage("invalid delay: %s" % options.delay)
for a in args:
sys.stderr.write("ignoring command line argument: '%s'\n" % a)
if options.seed:
random.seed(options.seed)
me = ManetExperiment(options = options, start=datetime.datetime.now())
me.info("creating topology: numnodes = %s; linkprob = %s" % \
(options.numnodes, options.linkprob))
me.topology(options.numnodes, options.linkprob)
me.info("waiting %s sec" % options.delay)
time.sleep(options.delay)
me.info("checking neighbor state and routes")
me.checknodes()
me.info("done")
me.info("elapsed time: %s" % (datetime.datetime.now() - me.start))
me.logend()
return me
if __name__ == "__main__":
me = main()

78
daemon/examples/netns/switch.py Executable file
View file

@ -0,0 +1,78 @@
#!/usr/bin/python -i
# Copyright (c)2010-2013 the Boeing Company.
# See the LICENSE file included in this distribution.
# connect n nodes to a virtual switch/hub
import sys, datetime, optparse
from core import pycore
from core.misc import ipaddr
from core.constants import *
# node list (count from 1)
n = [None]
def add_to_server(session):
''' Add this session to the server's list if this script is executed from
the core-daemon server.
'''
global server
try:
server.addsession(session)
return True
except NameError:
return False
def main():
usagestr = "usage: %prog [-h] [options] [args]"
parser = optparse.OptionParser(usage = usagestr)
parser.set_defaults(numnodes = 5)
parser.add_option("-n", "--numnodes", dest = "numnodes", type = int,
help = "number of nodes")
def usage(msg = None, err = 0):
sys.stdout.write("\n")
if msg:
sys.stdout.write(msg + "\n\n")
parser.print_help()
sys.exit(err)
# parse command line options
(options, args) = parser.parse_args()
if options.numnodes < 1:
usage("invalid number of nodes: %s" % options.numnodes)
for a in args:
sys.stderr.write("ignoring command line argument: '%s'\n" % a)
start = datetime.datetime.now()
# IP subnet
prefix = ipaddr.IPv4Prefix("10.83.0.0/16")
session = pycore.Session(persistent=True)
add_to_server(session)
# emulated Ethernet switch
switch = session.addobj(cls = pycore.nodes.SwitchNode, name = "switch")
switch.setposition(x=80,y=50)
print "creating %d nodes with addresses from %s" % \
(options.numnodes, prefix)
for i in xrange(1, options.numnodes + 1):
tmp = session.addobj(cls = pycore.nodes.CoreNode, name = "n%d" % i,
objid=i)
tmp.newnetif(switch, ["%s/%s" % (prefix.addr(i), prefix.prefixlen)])
tmp.cmd([SYSCTL_BIN, "net.ipv4.icmp_echo_ignore_broadcasts=0"])
tmp.setposition(x=150*i,y=150)
n.append(tmp)
# start a shell on node 1
n[1].term("bash")
print "elapsed time: %s" % (datetime.datetime.now() - start)
if __name__ == "__main__" or __name__ == "__builtin__":
main()

View file

@ -0,0 +1,97 @@
#!/usr/bin/python
# Copyright (c)2010-2012 the Boeing Company.
# See the LICENSE file included in this distribution.
# run iperf to measure the effective throughput between two nodes when
# n nodes are connected to a virtual hub/switch; run test for testsec
# and repeat for minnodes <= n <= maxnodes with a step size of
# nodestep
import optparse, sys, os, datetime
from core import pycore
from core.misc import ipaddr
from core.misc.utils import mutecall
try:
mutecall(["iperf", "-v"])
except OSError:
sys.stderr.write("ERROR: running iperf failed\n")
sys.exit(1)
def test(numnodes, testsec):
# node list
n = []
# IP subnet
prefix = ipaddr.IPv4Prefix("10.83.0.0/16")
session = pycore.Session()
# emulated network
net = session.addobj(cls = pycore.nodes.SwitchNode)
for i in xrange(1, numnodes + 1):
tmp = session.addobj(cls = pycore.nodes.LxcNode, name = "n%d" % i)
tmp.newnetif(net, ["%s/%s" % (prefix.addr(i), prefix.prefixlen)])
n.append(tmp)
n[0].cmd(["iperf", "-s", "-D"])
n[-1].icmd(["iperf", "-t", str(int(testsec)), "-c", str(prefix.addr(1))])
n[0].cmd(["killall", "-9", "iperf"])
session.shutdown()
def main():
usagestr = "usage: %prog [-h] [options] [args]"
parser = optparse.OptionParser(usage = usagestr)
parser.set_defaults(minnodes = 2)
parser.add_option("-m", "--minnodes", dest = "minnodes", type = int,
help = "min number of nodes to test; default = %s" %
parser.defaults["minnodes"])
parser.set_defaults(maxnodes = 2)
parser.add_option("-n", "--maxnodes", dest = "maxnodes", type = int,
help = "max number of nodes to test; default = %s" %
parser.defaults["maxnodes"])
parser.set_defaults(testsec = 10)
parser.add_option("-t", "--testsec", dest = "testsec", type = int,
help = "test time in seconds; default = %s" %
parser.defaults["testsec"])
parser.set_defaults(nodestep = 1)
parser.add_option("-s", "--nodestep", dest = "nodestep", type = int,
help = "number of nodes step size; default = %s" %
parser.defaults["nodestep"])
def usage(msg = None, err = 0):
sys.stdout.write("\n")
if msg:
sys.stdout.write(msg + "\n\n")
parser.print_help()
sys.exit(err)
# parse command line options
(options, args) = parser.parse_args()
if options.minnodes < 2:
usage("invalid min number of nodes: %s" % options.minnodes)
if options.maxnodes < options.minnodes:
usage("invalid max number of nodes: %s" % options.maxnodes)
if options.testsec < 1:
usage("invalid test time: %s" % options.testsec)
if options.nodestep < 1:
usage("invalid node step: %s" % options.nodestep)
for a in args:
sys.stderr.write("ignoring command line argument: '%s'\n" % a)
start = datetime.datetime.now()
for i in xrange(options.minnodes, options.maxnodes + 1, options.nodestep):
print >> sys.stderr, "%s node test:" % i
test(i, options.testsec)
print >> sys.stderr, ""
print >> sys.stderr, \
"elapsed time: %s" % (datetime.datetime.now() - start)
if __name__ == "__main__":
main()

View file

@ -0,0 +1,33 @@
#!/bin/sh
# Below is a transcript of creating two emulated nodes and connecting them
# together with a wired link. You can run the core-cleanup script to clean
# up after this script.
# create node 1 namespace container
vnoded -c /tmp/n1.ctl -l /tmp/n1.log -p /tmp/n1.pid
# create a virtual Ethernet (veth) pair, installing one end into node 1
ip link add name n1.0.1 type veth peer name n1.0
ip link set n1.0 netns `cat /tmp/n1.pid`
vcmd -c /tmp/n1.ctl -- ip link set n1.0 name eth0
vcmd -c /tmp/n1.ctl -- ifconfig eth0 10.0.0.1/24
# create node 2 namespace container
vnoded -c /tmp/n2.ctl -l /tmp/n2.log -p /tmp/n2.pid
# create a virtual Ethernet (veth) pair, installing one end into node 2
ip link add name n2.0.1 type veth peer name n2.0
ip link set n2.0 netns `cat /tmp/n2.pid`
vcmd -c /tmp/n2.ctl -- ip link set n2.0 name eth0
vcmd -c /tmp/n2.ctl -- ifconfig eth0 10.0.0.2/24
# bridge together nodes 1 and 2 using the other end of each veth pair
brctl addbr b.1.1
brctl setfd b.1.1 0
brctl addif b.1.1 n1.0.1
brctl addif b.1.1 n2.0.1
ip link set n1.0.1 up
ip link set n2.0.1 up
ip link set b.1.1 up
# display connectivity and ping from node 1 to node 2
brctl show
vcmd -c /tmp/n1.ctl -- ping 10.0.0.2

View file

@ -0,0 +1,772 @@
#!/usr/bin/python
# Copyright (c)2011-2012 the Boeing Company.
# See the LICENSE file included in this distribution.
#
# author: Jeff Ahrenholz <jeffrey.m.ahrenholz@boeing.com>
#
'''
wlanemanetests.py - This script tests the performance of the WLAN device in
CORE by measuring various metrics:
- delay experienced when pinging end-to-end
- maximum TCP throughput achieved using iperf end-to-end
- the CPU used and loss experienced when running an MGEN flow of UDP traffic
All MANET nodes are arranged in a row, so that any given node can only
communicate with the node to its right or to its left. Performance is measured
using traffic that travels across each hop in the network. Static /32 routing
is used instead of any dynamic routing protocol.
Various underlying network types are tested:
- bridged (the CORE default, uses ebtables)
- bridged with netem (add link effects to the bridge using tc queues)
- EMANE bypass - the bypass model just forwards traffic
- EMANE RF-PIPE - the bandwidth (bitrate) is set very high / no restrictions
- EMANE RF-PIPE - bandwidth is set similar to netem case
- EMANE RF-PIPE - default connectivity is off and pathloss events are
generated to connect the nodes in a line
Results are printed/logged in CSV format.
'''
import os, sys, time, optparse, datetime, math
from string import Template
try:
from core import pycore
except ImportError:
# hack for Fedora autoconf that uses the following pythondir:
if "/usr/lib/python2.6/site-packages" in sys.path:
sys.path.append("/usr/local/lib/python2.6/site-packages")
if "/usr/lib64/python2.6/site-packages" in sys.path:
sys.path.append("/usr/local/lib64/python2.6/site-packages")
if "/usr/lib/python2.7/site-packages" in sys.path:
sys.path.append("/usr/local/lib/python2.7/site-packages")
if "/usr/lib64/python2.7/site-packages" in sys.path:
sys.path.append("/usr/local/lib64/python2.7/site-packages")
from core import pycore
from core.misc import ipaddr
from core.misc.utils import mutecall
from core.constants import QUAGGA_STATE_DIR
from core.emane.bypass import EmaneBypassModel
from core.emane.rfpipe import EmaneRfPipeModel
import emaneeventservice
import emaneeventpathloss
# move these to core.misc.utils
def readstat():
f = open("/proc/stat", "r")
lines = f.readlines()
f.close()
return lines
def numcpus():
lines = readstat()
n = 0
for l in lines[1:]:
if l[:3] != "cpu":
break
n += 1
return n
def getcputimes(line):
# return (user, nice, sys, idle) from a /proc/stat cpu line
# assume columns are:
# cpu# user nice sys idle iowait irq softirq steal guest (man 5 proc)
items = line.split()
(user, nice, sys, idle) = map(lambda(x): int(x), items[1:5])
return [user, nice, sys, idle]
def calculatecpu(timesa, timesb):
for i in range(len(timesa)):
timesb[i] -= timesa[i]
total = sum(timesb)
if total == 0:
return 0.0
else:
# subtract % time spent in idle time
return 100 - ((100.0 * timesb[-1]) / total)
# end move these to core.misc.utils
class Cmd(object):
''' Helper class for running a command on a node and parsing the result. '''
args = ""
def __init__(self, node, verbose=False):
''' Initialize with a CoreNode (LxcNode) '''
self.id = None
self.stdin = None
self.out = None
self.node = node
self.verbose = verbose
def info(self, msg):
''' Utility method for writing output to stdout.'''
print msg
sys.stdout.flush()
def warn(self, msg):
''' Utility method for writing output to stderr. '''
print >> sys.stderr, "XXX %s:" % self.node.name, msg
sys.stderr.flush()
def run(self):
''' This is the primary method used for running this command. '''
self.open()
status = self.id.wait()
r = self.parse()
self.cleanup()
return r
def open(self):
''' Exceute call to node.popen(). '''
self.id, self.stdin, self.out, self.err = \
self.node.popen((self.args))
def parse(self):
''' This method is overloaded by child classes and should return some
result.
'''
return None
def cleanup(self):
''' Close the Popen channels.'''
self.stdin.close()
self.out.close()
self.err.close()
tmp = self.id.wait()
if tmp:
self.warn("nonzero exit status:", tmp)
class ClientServerCmd(Cmd):
''' Helper class for running a command on a node and parsing the result. '''
args = ""
client_args = ""
def __init__(self, node, client_node, verbose=False):
''' Initialize with two CoreNodes, node is the server '''
Cmd.__init__(self, node, verbose)
self.client_node = client_node
def run(self):
''' Run the server command, then the client command, then
kill the server '''
self.open() # server
self.client_open() # client
status = self.client_id.wait()
self.node.cmdresult(['killall', self.args[0]]) # stop the server
r = self.parse()
self.cleanup()
return r
def client_open(self):
''' Exceute call to client_node.popen(). '''
self.client_id, self.client_stdin, self.client_out, self.client_err = \
self.client_node.popen((self.client_args))
def parse(self):
''' This method is overloaded by child classes and should return some
result.
'''
return None
def cleanup(self):
''' Close the Popen channels.'''
self.stdin.close()
self.out.close()
self.err.close()
tmp = self.id.wait()
if tmp:
self.warn("nonzero exit status: %s" % tmp)
self.warn("command was: %s" % ((self.args, )))
class PingCmd(Cmd):
''' Test latency using ping.
'''
def __init__(self, node, verbose=False, addr=None, count=50, interval=0.1, ):
Cmd.__init__(self, node, verbose)
self.addr = addr
self.count = count
self.interval = interval
self.args = ['ping', '-q', '-c', '%s' % count, '-i', '%s' % interval,
addr]
def run(self):
if self.verbose:
self.info("%s initial test ping (max 1 second)..." % self.node.name)
(status, result) = self.node.cmdresult(["ping", "-q", "-c", "1", "-w",
"1", self.addr])
if status != 0:
self.warn("initial ping from %s to %s failed! result:\n%s" % \
(self.node.name, self.addr, result))
return (0.0, 0.0)
if self.verbose:
self.info("%s pinging %s (%d seconds)..." % \
(self.node.name, self.addr, self.count * self.interval))
return Cmd.run(self)
def parse(self):
lines = self.out.readlines()
avg_latency = 0
mdev = 0
try:
stats_str = lines[-1].split('=')[1]
stats = stats_str.split('/')
avg_latency = float(stats[1])
mdev = float(stats[3].split(' ')[0])
except Exception, e:
self.warn("ping parsing exception: %s" % e)
return (avg_latency, mdev)
class IperfCmd(ClientServerCmd):
''' Test throughput using iperf.
'''
def __init__(self, node, client_node, verbose=False, addr=None, time=10):
# node is the server
ClientServerCmd.__init__(self, node, client_node, verbose)
self.addr = addr
self.time = time
# -s server, -y c CSV report output
self.args = ["iperf", "-s", "-y", "c"]
self.client_args = ["iperf", "-c", self.addr, "-t", "%s" % self.time]
def run(self):
if self.verbose:
self.info("Launching the iperf server on %s..." % self.node.name)
self.info("Running the iperf client on %s (%s seconds)..." % \
(self.client_node.name, self.time))
return ClientServerCmd.run(self)
def parse(self):
lines = self.out.readlines()
try:
bps = int(lines[-1].split(',')[-1].strip('\n'))
except Exception, e:
self.warn("iperf parsing exception: %s" % e)
bps = 0
return bps
class MgenCmd(ClientServerCmd):
''' Run a test traffic flow using an MGEN sender and receiver.
'''
def __init__(self, node, client_node, verbose=False, addr=None, time=10,
rate=512):
ClientServerCmd.__init__(self, node, client_node, verbose)
self.addr = addr
self.time = time
self.args = ['mgen', 'event', 'listen udp 5000', 'output',
'/var/log/mgen.log']
self.rate = rate
sendevent = "ON 1 UDP DST %s/5000 PERIODIC [%s]" % \
(addr, self.mgenrate(self.rate))
stopevent = "%s OFF 1" % time
self.client_args = ['mgen', 'event', sendevent, 'event', stopevent,
'output', '/var/log/mgen.log']
@staticmethod
def mgenrate(kbps):
''' Return a MGEN periodic rate string for the given kilobits-per-sec.
Assume 1500 byte MTU, 20-byte IP + 8-byte UDP headers, leaving
1472 bytes for data.
'''
bps = (kbps / 8) * 1000.0
maxdata = 1472
pps = math.ceil(bps / maxdata)
return "%s %s" % (pps, maxdata)
def run(self):
if self.verbose:
self.info("Launching the MGEN receiver on %s..." % self.node.name)
self.info("Running the MGEN sender on %s (%s seconds)..." % \
(self.client_node.name, self.time))
return ClientServerCmd.run(self)
def cleanup(self):
''' Close the Popen channels.'''
self.stdin.close()
self.out.close()
self.err.close()
tmp = self.id.wait() # non-zero mgen exit status OK
def parse(self):
''' Check MGEN receiver's log file for packet sequence numbers, and
return the percentage of lost packets.
'''
logfile = os.path.join(self.node.nodedir, 'var.log/mgen.log')
f = open(logfile, 'r')
numlost = 0
lastseq = 0
for line in f.readlines():
fields = line.split()
if fields[1] != 'RECV':
continue
try:
seq = int(fields[4].split('>')[1])
except:
self.info("Unexpected MGEN line:\n%s" % fields)
if seq > (lastseq + 1):
numlost += seq - (lastseq + 1)
lastseq = seq
f.close()
if lastseq > 0:
loss = 100.0 * numlost / lastseq
else:
loss = 0
if self.verbose:
self.info("Receiver log shows %d of %d packets lost" % \
(numlost, lastseq))
return loss
class Experiment(object):
''' Experiment object to organize tests.
'''
def __init__(self, opt, start):
''' Initialize with opt and start time. '''
self.session = None
# node list
self.nodes = []
# WLAN network
self.net = None
self.verbose = opt.verbose
# dict from OptionParser
self.opt = opt
self.start = start
self.numping = opt.numping
self.numiperf = opt.numiperf
self.nummgen = opt.nummgen
self.logbegin()
def info(self, msg):
''' Utility method for writing output to stdout. '''
print msg
sys.stdout.flush()
self.log(msg)
def warn(self, msg):
''' Utility method for writing output to stderr. '''
print >> sys.stderr, msg
sys.stderr.flush()
self.log(msg)
def logbegin(self):
''' Start logging. '''
self.logfp = None
if not self.opt.logfile:
return
self.logfp = open(self.opt.logfile, "w")
self.log("%s begin: %s\n" % (sys.argv[0], self.start.ctime()))
self.log("%s args: %s\n" % (sys.argv[0], sys.argv[1:]))
(sysname, rel, ver, machine, nodename) = os.uname()
self.log("%s %s %s %s on %s" % (sysname, rel, ver, machine, nodename))
def logend(self):
''' End logging. '''
if not self.logfp:
return
end = datetime.datetime.now()
self.log("%s end: %s (%s)\n" % \
(sys.argv[0], end.ctime(), end - self.start))
self.logfp.flush()
self.logfp.close()
self.logfp = None
def log(self, msg):
''' Write to the log file, if any. '''
if not self.logfp:
return
print >> self.logfp, msg
def reset(self):
''' Prepare for another experiment run.
'''
if self.session:
self.session.shutdown()
del self.session
self.session = None
self.nodes = []
self.net = None
def createbridgedsession(self, numnodes, verbose = False):
''' Build a topology consisting of the given number of LxcNodes
connected to a WLAN.
'''
# IP subnet
prefix = ipaddr.IPv4Prefix("10.0.0.0/16")
self.session = pycore.Session()
# emulated network
self.net = self.session.addobj(cls = pycore.nodes.WlanNode,
name = "wlan1")
prev = None
for i in xrange(1, numnodes + 1):
addr = "%s/%s" % (prefix.addr(i), 32)
tmp = self.session.addobj(cls = pycore.nodes.CoreNode, objid = i,
name = "n%d" % i)
tmp.newnetif(self.net, [addr])
self.nodes.append(tmp)
self.session.services.addservicestonode(tmp, "router",
"IPForward", self.verbose)
self.session.services.bootnodeservices(tmp)
self.staticroutes(i, prefix, numnodes)
# link each node in a chain, with the previous node
if prev:
self.net.link(prev.netif(0), tmp.netif(0))
prev = tmp
def createemanesession(self, numnodes, verbose = False, cls = None,
values = None):
''' Build a topology consisting of the given number of LxcNodes
connected to an EMANE WLAN.
'''
prefix = ipaddr.IPv4Prefix("10.0.0.0/16")
self.session = pycore.Session()
self.session.master = True
self.session.location.setrefgeo(47.57917,-122.13232,2.00000)
self.session.location.refscale = 150.0
self.session.cfg['emane_models'] = "RfPipe, Ieee80211abg, Bypass"
self.session.emane.loadmodels()
self.net = self.session.addobj(cls = pycore.nodes.EmaneNode,
objid = numnodes + 1, name = "wlan1")
self.net.verbose = verbose
#self.session.emane.addobj(self.net)
for i in xrange(1, numnodes + 1):
addr = "%s/%s" % (prefix.addr(i), 32)
tmp = self.session.addobj(cls = pycore.nodes.CoreNode, objid = i,
name = "n%d" % i)
#tmp.setposition(i * 20, 50, None)
tmp.setposition(50, 50, None)
tmp.newnetif(self.net, [addr])
self.nodes.append(tmp)
self.session.services.addservicestonode(tmp, "router",
"IPForward", self.verbose)
if values is None:
values = cls.getdefaultvalues()
self.session.emane.setconfig(self.net.objid, cls._name, values)
self.session.emane.startup()
self.info("waiting %s sec (TAP bring-up)" % 2)
time.sleep(2)
for i in xrange(1, numnodes + 1):
tmp = self.nodes[i-1]
self.session.services.bootnodeservices(tmp)
self.staticroutes(i, prefix, numnodes)
def setnodes(self):
''' Set the sender and receiver nodes for use in this experiment,
along with the address of the receiver to be used.
'''
self.firstnode = self.nodes[0]
self.lastnode = self.nodes[-1]
self.lastaddr = self.lastnode.netif(0).addrlist[0].split('/')[0]
def staticroutes(self, i, prefix, numnodes):
''' Add static routes on node number i to the other nodes in the chain.
'''
routecmd = ["/sbin/ip", "route", "add"]
node = self.nodes[i-1]
neigh_left = ""
neigh_right = ""
# add direct interface routes first
if i > 1:
neigh_left = "%s" % prefix.addr(i - 1)
cmd = routecmd + [neigh_left, "dev", node.netif(0).name]
(status, result) = node.cmdresult(cmd)
if status != 0:
self.warn("failed to add interface route: %s" % cmd)
if i < numnodes:
neigh_right = "%s" % prefix.addr(i + 1)
cmd = routecmd + [neigh_right, "dev", node.netif(0).name]
(status, result) = node.cmdresult(cmd)
if status != 0:
self.warn("failed to add interface route: %s" % cmd)
# add static routes to all other nodes via left/right neighbors
for j in xrange(1, numnodes + 1):
if abs(j - i) < 2:
continue
addr = "%s" % prefix.addr(j)
if j < i:
gw = neigh_left
else:
gw = neigh_right
cmd = routecmd + [addr, "via", gw]
(status, result) = node.cmdresult(cmd)
if status != 0:
self.warn("failed to add route: %s" % cmd)
def setpathloss(self, numnodes):
''' Send EMANE pathloss events to connect all NEMs in a chain.
'''
service = emaneeventservice.EventService()
e = emaneeventpathloss.EventPathloss(1)
for i in xrange(1, numnodes + 1):
rxnem = i
# inform rxnem that it can hear node to the left with 10dB noise
txnem = rxnem - 1
e.set(0, txnem, 10.0, 10.0)
if txnem > 0:
service.publish(emaneeventpathloss.EVENT_ID,
emaneeventservice.PLATFORMID_ANY, rxnem,
emaneeventservice.COMPONENTID_ANY, e.export())
# inform rxnem that it can hear node to the right with 10dB noise
txnem = rxnem + 1
e.set(0, txnem, 10.0, 10.0)
if txnem <= numnodes:
service.publish(emaneeventpathloss.EVENT_ID,
emaneeventservice.PLATFORMID_ANY, rxnem,
emaneeventservice.COMPONENTID_ANY, e.export())
def setneteffects(self, bw = None, delay = None):
''' Set link effects for all interfaces attached to the network node.
'''
if not self.net:
self.warn("failed to set effects: no network node")
return
for netif in self.net.netifs():
self.net.linkconfig(netif, bw = bw, delay = delay)
def runalltests(self, title=""):
''' Convenience helper to run all defined experiment tests.
If tests are run multiple times, this returns the average of
those runs.
'''
duration = self.opt.duration
rate = self.opt.rate
if len(title) > 0:
self.info("----- running %s tests (duration=%s, rate=%s) -----" % \
(title, duration, rate))
(latency, mdev, throughput, cpu, loss) = (0,0,0,0,0)
self.info("number of runs: ping=%d, iperf=%d, mgen=%d" % \
(self.numping, self.numiperf, self.nummgen))
if self.numping > 0:
(latency, mdev) = self.pingtest(count=self.numping)
if self.numiperf > 0:
throughputs = []
for i in range(1, self.numiperf + 1):
throughput = self.iperftest(time=duration)
if self.numiperf > 1:
throughputs += throughput
time.sleep(1) # iperf is very CPU intensive
if self.numiperf > 1:
throughput = sum(throughputs) / len(throughputs)
self.info("throughputs=%s" % ["%.2f" % v for v in throughputs])
if self.nummgen > 0:
cpus = []
losses = []
for i in range(1, self.nummgen + 1):
(cpu, loss) = self.cputest(time=duration, rate=rate)
if self.nummgen > 1:
cpus += cpu,
losses += loss,
if self.nummgen > 1:
cpu = sum(cpus) / len(cpus)
loss = sum(losses) / len(losses)
self.info("cpus=%s" % ["%.2f" % v for v in cpus])
self.info("losses=%s" % ["%.2f" % v for v in losses])
return (latency, mdev, throughput, cpu, loss)
def pingtest(self, count=50):
''' Ping through a chain of nodes and report the average latency.
'''
p = PingCmd(node=self.firstnode, verbose=self.verbose,
addr = self.lastaddr, count=count, interval=0.1).run()
(latency, mdev) = p
self.info("latency (ms): %.03f, %.03f" % (latency, mdev))
return p
def iperftest(self, time=10):
''' Run iperf through a chain of nodes and report the maximum
throughput.
'''
bps = IperfCmd(node=self.lastnode, client_node=self.firstnode,
verbose=False, addr=self.lastaddr, time=time).run()
self.info("throughput (bps): %s" % bps)
return bps
def cputest(self, time=10, rate=512):
''' Run MGEN through a chain of nodes and report the CPU usage and
percent of lost packets. Rate is in kbps.
'''
if self.verbose:
self.info("%s initial test ping (max 1 second)..." % \
self.firstnode.name)
(status, result) = self.firstnode.cmdresult(["ping", "-q", "-c", "1",
"-w", "1", self.lastaddr])
if status != 0:
self.warn("initial ping from %s to %s failed! result:\n%s" % \
(self.firstnode.name, self.lastaddr, result))
return (0.0, 0.0)
lines = readstat()
cpustart = getcputimes(lines[0])
loss = MgenCmd(node=self.lastnode, client_node=self.firstnode,
verbose=False, addr=self.lastaddr,
time=time, rate=rate).run()
lines = readstat()
cpuend = getcputimes(lines[0])
percent = calculatecpu(cpustart, cpuend)
self.info("CPU usage (%%): %.02f, %.02f loss" % (percent, loss))
return percent, loss
def main():
''' Main routine when running from command-line.
'''
usagestr = "usage: %prog [-h] [options] [args]"
parser = optparse.OptionParser(usage = usagestr)
parser.set_defaults(numnodes = 10, delay = 3, duration = 10, rate = 512,
verbose = False,
numping = 50, numiperf = 1, nummgen = 1)
parser.add_option("-d", "--delay", dest = "delay", type = float,
help = "wait time before testing")
parser.add_option("-l", "--logfile", dest = "logfile", type = str,
help = "log detailed output to the specified file")
parser.add_option("-n", "--numnodes", dest = "numnodes", type = int,
help = "number of nodes")
parser.add_option("-r", "--rate", dest = "rate", type = float,
help = "kbps rate to use for MGEN CPU tests")
parser.add_option("--numping", dest = "numping", type = int,
help = "number of ping latency test runs")
parser.add_option("--numiperf", dest = "numiperf", type = int,
help = "number of iperf throughput test runs")
parser.add_option("--nummgen", dest = "nummgen", type = int,
help = "number of MGEN CPU tests runs")
parser.add_option("-t", "--time", dest = "duration", type = int,
help = "duration in seconds of throughput and CPU tests")
parser.add_option("-v", "--verbose", dest = "verbose",
action = "store_true", help = "be more verbose")
def usage(msg = None, err = 0):
sys.stdout.write("\n")
if msg:
sys.stdout.write(msg + "\n\n")
parser.print_help()
sys.exit(err)
# parse command line opt
(opt, args) = parser.parse_args()
if opt.numnodes < 2:
usage("invalid numnodes: %s" % opt.numnodes)
if opt.delay < 0.0:
usage("invalid delay: %s" % opt.delay)
if opt.rate < 0.0:
usage("invalid rate: %s" % opt.rate)
for a in args:
sys.stderr.write("ignoring command line argument: '%s'\n" % a)
results = {}
exp = Experiment(opt = opt, start=datetime.datetime.now())
# bridged
exp.info("setting up bridged tests 1/2 no link effects")
exp.info("creating topology: numnodes = %s" % \
(opt.numnodes, ))
exp.createbridgedsession(numnodes=opt.numnodes, verbose=opt.verbose)
exp.setnodes()
exp.info("waiting %s sec (node/route bring-up)" % opt.delay)
time.sleep(opt.delay)
results['0 bridged'] = exp.runalltests("bridged")
exp.info("done; elapsed time: %s" % (datetime.datetime.now() - exp.start))
# bridged with netem
exp.info("setting up bridged tests 2/2 with netem")
exp.setneteffects(bw=54000000, delay=0)
exp.info("waiting %s sec (queue bring-up)" % opt.delay)
results['1 netem'] = exp.runalltests("netem")
exp.info("shutting down bridged session")
exp.reset()
# EMANE bypass model
exp.info("setting up EMANE tests 1/2 with bypass model")
exp.createemanesession(numnodes=opt.numnodes, verbose=opt.verbose,
cls=EmaneBypassModel, values=None)
exp.setnodes()
exp.info("waiting %s sec (node/route bring-up)" % opt.delay)
time.sleep(opt.delay)
results['2 bypass'] = exp.runalltests("bypass")
exp.info("shutting down bypass session")
exp.reset()
exp.info("waiting %s sec (between EMANE tests)" % opt.delay)
time.sleep(opt.delay)
# EMANE RF-PIPE model: no restrictions (max datarate)
exp.info("setting up EMANE tests 2/4 with RF-PIPE model")
rfpipevals = list(EmaneRfPipeModel.getdefaultvalues())
rfpnames = EmaneRfPipeModel.getnames()
rfpipevals[ rfpnames.index('datarate') ] = '4294967295' # max value
rfpipevals[ rfpnames.index('pathlossmode') ] = '2ray'
rfpipevals[ rfpnames.index('defaultconnectivitymode') ] = '1'
exp.createemanesession(numnodes=opt.numnodes, verbose=opt.verbose,
cls=EmaneRfPipeModel, values=rfpipevals)
exp.setnodes()
exp.info("waiting %s sec (node/route bring-up)" % opt.delay)
time.sleep(opt.delay)
results['3 rfpipe'] = exp.runalltests("rfpipe")
exp.info("shutting down RF-PIPE session")
exp.reset()
# EMANE RF-PIPE model: 54M datarate
exp.info("setting up EMANE tests 3/4 with RF-PIPE model 54M")
rfpipevals = list(EmaneRfPipeModel.getdefaultvalues())
rfpnames = EmaneRfPipeModel.getnames()
rfpipevals[ rfpnames.index('datarate') ] = '54000'
# TX delay != propagation delay
#rfpipevals[ rfpnames.index('delay') ] = '5000'
rfpipevals[ rfpnames.index('pathlossmode') ] = '2ray'
rfpipevals[ rfpnames.index('defaultconnectivitymode') ] = '1'
exp.createemanesession(numnodes=opt.numnodes, verbose=opt.verbose,
cls=EmaneRfPipeModel, values=rfpipevals)
exp.setnodes()
exp.info("waiting %s sec (node/route bring-up)" % opt.delay)
time.sleep(opt.delay)
results['4 rfpipe54m'] = exp.runalltests("rfpipe54m")
exp.info("shutting down RF-PIPE session")
exp.reset()
# EMANE RF-PIPE model
exp.info("setting up EMANE tests 4/4 with RF-PIPE model pathloss")
rfpipevals = list(EmaneRfPipeModel.getdefaultvalues())
rfpnames = EmaneRfPipeModel.getnames()
rfpipevals[ rfpnames.index('datarate') ] = '54000'
rfpipevals[ rfpnames.index('pathlossmode') ] = 'pathloss'
rfpipevals[ rfpnames.index('defaultconnectivitymode') ] = '0'
exp.createemanesession(numnodes=opt.numnodes, verbose=opt.verbose,
cls=EmaneRfPipeModel, values=rfpipevals)
exp.setnodes()
exp.info("waiting %s sec (node/route bring-up)" % opt.delay)
time.sleep(opt.delay)
exp.info("sending pathloss events to govern connectivity")
exp.setpathloss(opt.numnodes)
results['5 pathloss'] = exp.runalltests("pathloss")
exp.info("shutting down RF-PIPE session")
exp.reset()
# summary of results in CSV format
exp.info("----- summary of results (%s nodes, rate=%s, duration=%s) -----" \
% (opt.numnodes, opt.rate, opt.duration))
exp.info("netname:latency,mdev,throughput,cpu,loss")
for test in sorted(results.keys()):
(latency, mdev, throughput, cpu, loss) = results[test]
exp.info("%s:%.03f,%.03f,%d,%.02f,%.02f" % \
(test, latency, mdev, throughput, cpu,loss))
exp.logend()
return exp
if __name__ == "__main__":
exp = main()

View file

@ -0,0 +1,98 @@
#!/usr/bin/python
# Copyright (c)2010-2012 the Boeing Company.
# See the LICENSE file included in this distribution.
# run iperf to measure the effective throughput between two nodes when
# n nodes are connected to a virtual wlan; run test for testsec
# and repeat for minnodes <= n <= maxnodes with a step size of
# nodestep
import optparse, sys, os, datetime
from core import pycore
from core.misc import ipaddr
from core.misc.utils import mutecall
try:
mutecall(["iperf", "-v"])
except OSError:
sys.stderr.write("ERROR: running iperf failed\n")
sys.exit(1)
def test(numnodes, testsec):
# node list
n = []
# IP subnet
prefix = ipaddr.IPv4Prefix("10.83.0.0/16")
session = pycore.Session()
# emulated network
net = session.addobj(cls = pycore.nodes.WlanNode)
for i in xrange(1, numnodes + 1):
tmp = session.addobj(cls = pycore.nodes.LxcNode, name = "n%d" % i)
tmp.newnetif(net, ["%s/%s" % (prefix.addr(i), prefix.prefixlen)])
n.append(tmp)
net.link(n[0].netif(0), n[-1].netif(0))
n[0].cmd(["iperf", "-s", "-D"])
n[-1].icmd(["iperf", "-t", str(int(testsec)), "-c", str(prefix.addr(1))])
n[0].cmd(["killall", "-9", "iperf"])
session.shutdown()
def main():
usagestr = "usage: %prog [-h] [options] [args]"
parser = optparse.OptionParser(usage = usagestr)
parser.set_defaults(minnodes = 2)
parser.add_option("-m", "--minnodes", dest = "minnodes", type = int,
help = "min number of nodes to test; default = %s" %
parser.defaults["minnodes"])
parser.set_defaults(maxnodes = 2)
parser.add_option("-n", "--maxnodes", dest = "maxnodes", type = int,
help = "max number of nodes to test; default = %s" %
parser.defaults["maxnodes"])
parser.set_defaults(testsec = 10)
parser.add_option("-t", "--testsec", dest = "testsec", type = int,
help = "test time in seconds; default = %s" %
parser.defaults["testsec"])
parser.set_defaults(nodestep = 1)
parser.add_option("-s", "--nodestep", dest = "nodestep", type = int,
help = "number of nodes step size; default = %s" %
parser.defaults["nodestep"])
def usage(msg = None, err = 0):
sys.stdout.write("\n")
if msg:
sys.stdout.write(msg + "\n\n")
parser.print_help()
sys.exit(err)
# parse command line options
(options, args) = parser.parse_args()
if options.minnodes < 2:
usage("invalid min number of nodes: %s" % options.minnodes)
if options.maxnodes < options.minnodes:
usage("invalid max number of nodes: %s" % options.maxnodes)
if options.testsec < 1:
usage("invalid test time: %s" % options.testsec)
if options.nodestep < 1:
usage("invalid node step: %s" % options.nodestep)
for a in args:
sys.stderr.write("ignoring command line argument: '%s'\n" % a)
start = datetime.datetime.now()
for i in xrange(options.minnodes, options.maxnodes + 1, options.nodestep):
print >> sys.stderr, "%s node test:" % i
test(i, options.testsec)
print >> sys.stderr, ""
print >> sys.stderr, \
"elapsed time: %s" % (datetime.datetime.now() - start)
if __name__ == "__main__":
main()