core-extra/daemon/examples/netns/wlanemanetests.py

847 lines
31 KiB
Python
Executable file

#!/usr/bin/python
# Copyright (c)2011-2014 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 datetime
import math
import optparse
import os
import sys
import time
from core import emane
from core.emane.bypass import EmaneBypassModel
from core.emane.nodes import EmaneNode
from core.emane.rfpipe import EmaneRfPipeModel
from core.misc import ipaddress
from core.netns import nodes
from core.session import Session
try:
import emaneeventservice
import emaneeventpathloss
except Exception, e:
try:
from emanesh.events import EventService
from emanesh.events import PathlossEvent
except Exception, e2:
raise ImportError("failed to import EMANE Python bindings:\n%s\n%s" % (e, e2))
# global Experiment object (for interaction with "python -i")
exp = None
# 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.client.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()
# stop the server
self.node.cmd_output(["killall", self.args[0]])
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.client.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.cmd_output(["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:
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()
# non-zero mgen exit status OK
tmp = self.id.wait()
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 = ipaddress.Ipv4Prefix("10.0.0.0/16")
self.session = Session(1)
# emulated network
self.net = self.session.add_object(cls=nodes.WlanNode, name="wlan1")
prev = None
for i in xrange(1, numnodes + 1):
addr = "%s/%s" % (prefix.addr(i), 32)
tmp = self.session.add_object(cls=nodes.CoreNode, objid=i, name="n%d" % i)
tmp.newnetif(self.net, [addr])
self.nodes.append(tmp)
self.session.services.add_services(tmp, "router", "IPForward")
self.session.services.boot_services(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 = ipaddress.Ipv4Prefix("10.0.0.0/16")
self.session = Session(2)
self.session.node_count = str(numnodes + 1)
self.session.master = True
self.session.location.setrefgeo(47.57917, -122.13232, 2.00000)
self.session.location.refscale = 150.0
self.session.emane.loadmodels()
self.net = self.session.add_object(cls=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.add_object(cls=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.add_services(tmp, "router", "IPForward")
if values is None:
values = cls.getdefaultvalues()
self.session.emane.setconfig(self.net.objid, cls.name, values)
self.session.instantiate()
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.boot_services(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.cmd_output(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.cmd_output(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.cmd_output(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.
"""
if self.session.emane.version < self.session.emane.EMANE091:
service = emaneeventservice.EventService()
e = emaneeventpathloss.EventPathloss(1)
old = True
else:
if self.session.emane.version == self.session.emane.EMANE091:
dev = "lo"
else:
dev = self.session.obj("ctrlnet").brname
service = EventService(eventchannel=("224.1.2.8", 45703, dev),
otachannel=None)
old = False
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
if txnem > 0:
if old:
e.set(0, txnem, 10.0, 10.0)
service.publish(emaneeventpathloss.EVENT_ID,
emaneeventservice.PLATFORMID_ANY, rxnem,
emaneeventservice.COMPONENTID_ANY, e.export())
else:
e = PathlossEvent()
e.append(txnem, forward=10.0, reverse=10.0)
service.publish(rxnem, e)
# inform rxnem that it can hear node to the right with 10dB noise
txnem = rxnem + 1
if txnem > numnodes:
continue
if old:
e.set(0, txnem, 10.0, 10.0)
service.publish(emaneeventpathloss.EVENT_ID,
emaneeventservice.PLATFORMID_ANY, rxnem,
emaneeventservice.COMPONENTID_ANY, e.export())
else:
e = PathlossEvent()
e.append(txnem, forward=10.0, reverse=10.0)
service.publish(rxnem, e)
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
# iperf is very CPU intensive
time.sleep(1)
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.cmd_output(["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 = {}
starttime = datetime.datetime.now()
exp = Experiment(opt=opt, start=starttime)
exp.info("Starting wlanemanetests.py tests %s" % starttime.ctime())
# 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.0 netem"] = exp.runalltests("netem")
exp.info("shutting down bridged session")
# bridged with netem (1 Mbps,200ms)
exp.info("setting up bridged tests 3/2 with netem")
exp.setneteffects(bw=1000000, delay=20000)
exp.info("waiting %s sec (queue bring-up)" % opt.delay)
results["1.2 netem_1M"] = exp.runalltests("netem_1M")
exp.info("shutting down bridged session")
# bridged with netem (54 kbps,500ms)
exp.info("setting up bridged tests 3/2 with netem")
exp.setneteffects(bw=54000, delay=100000)
exp.info("waiting %s sec (queue bring-up)" % opt.delay)
results["1.4 netem_54K"] = exp.runalltests("netem_54K")
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.0 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()
# max value
rfpipevals[rfpnames.index("datarate")] = "4294967295"
if emanever < emane.EMANE091:
rfpipevals[rfpnames.index("pathlossmode")] = "2ray"
rfpipevals[rfpnames.index("defaultconnectivitymode")] = "1"
else:
rfpipevals[rfpnames.index("propagationmodel")] = "2ray"
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.0 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")] = "54000000"
# TX delay != propagation delay
# rfpipevals[ rfpnames.index("delay") ] = "5000"
if emanever < emane.EMANE091:
rfpipevals[rfpnames.index("pathlossmode")] = "2ray"
rfpipevals[rfpnames.index("defaultconnectivitymode")] = "1"
else:
rfpipevals[rfpnames.index("propagationmodel")] = "2ray"
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.0 rfpipe54m"] = exp.runalltests("rfpipe54m")
exp.info("shutting down RF-PIPE session")
exp.reset()
# EMANE RF-PIPE model: 54K datarate
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"
if emanever < emane.EMANE091:
rfpipevals[rfpnames.index("pathlossmode")] = "pathloss"
rfpipevals[rfpnames.index("defaultconnectivitymode")] = "0"
else:
rfpipevals[rfpnames.index("propagationmodel")] = "precomputed"
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.0 pathloss"] = exp.runalltests("pathloss")
exp.info("shutting down RF-PIPE session")
exp.reset()
# EMANE RF-PIPE model (512K, 200ms)
exp.info("setting up EMANE tests 4/4 with RF-PIPE model pathloss")
rfpipevals = list(EmaneRfPipeModel.getdefaultvalues())
rfpnames = EmaneRfPipeModel.getnames()
rfpipevals[rfpnames.index("datarate")] = "512000"
rfpipevals[rfpnames.index("delay")] = "200"
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.1 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__":
main()