initial import (Boeing r1752, NRL r878)
This commit is contained in:
commit
f8f46d28be
394 changed files with 99738 additions and 0 deletions
53
daemon/examples/controlnet_updown
Normal file
53
daemon/examples/controlnet_updown
Normal file
|
@ -0,0 +1,53 @@
|
|||
#!/bin/bash
|
||||
# Sample controlnet up/down script that will be executed when the control
|
||||
# network is brought up or down. This script either adds an interface to the
|
||||
# controlnet bridge or adds a permissive iptables firewall rule.
|
||||
|
||||
controlnet_intf=$1
|
||||
action=$2
|
||||
|
||||
config_type=iptables # iptables or brctl
|
||||
|
||||
iptables_address=10.205.15.132
|
||||
brctl_intf=eth2
|
||||
|
||||
BRCTL=/sbin/brctl
|
||||
IPTABLES=/usr/sbin/iptables
|
||||
|
||||
case "$action" in
|
||||
startup)
|
||||
case "$config_type" in
|
||||
iptables)
|
||||
$IPTABLES -I FORWARD -i $controlnet_intf -d $iptables_address -j ACCEPT
|
||||
$IPTABLES -I FORWARD -o $controlnet_intf -s $iptables_address -j ACCEPT
|
||||
;;
|
||||
brctl)
|
||||
$BRCTL addif $controlnet_intf $brctl_intf
|
||||
;;
|
||||
*)
|
||||
echo "Invalid config_type $config_type"
|
||||
;;
|
||||
esac
|
||||
;;
|
||||
|
||||
shutdown)
|
||||
case "$config_type" in
|
||||
iptables)
|
||||
$IPTABLES -D FORWARD -i $controlnet_intf -d $iptables_address -j ACCEPT
|
||||
$IPTABLES -D FORWARD -o $controlnet_intf -s $iptables_address -j ACCEPT
|
||||
;;
|
||||
brctl)
|
||||
$BRCTL delif $controlnet_intf $brctl_intf
|
||||
;;
|
||||
*)
|
||||
echo "Invalid config_type $config_type"
|
||||
;;
|
||||
esac
|
||||
;;
|
||||
|
||||
*)
|
||||
echo "Invalid action $action"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
exit 0
|
187
daemon/examples/emanemodel2core.py
Executable file
187
daemon/examples/emanemodel2core.py
Executable file
|
@ -0,0 +1,187 @@
|
|||
#!/usr/bin/env python
|
||||
#
|
||||
# CORE
|
||||
# Copyright (c) 2013 the Boeing Company.
|
||||
# See the LICENSE file included in this distribution.
|
||||
#
|
||||
# author: Jeff Ahrenholz <jeffrey.m.ahrenholz@boeing.com>
|
||||
#
|
||||
'''
|
||||
emanemodel2core.py: scans an EMANE model source file
|
||||
(e.g. emane/models/rfpipe/maclayer/rfpipemaclayer.cc) and outputs Python
|
||||
bindings that allow the model to be used in CORE.
|
||||
|
||||
When using this conversion utility, you should replace XYZ, Xyz, and xyz with
|
||||
the actual model name. Note the capitalization convention.
|
||||
'''
|
||||
|
||||
import os, sys, optparse
|
||||
|
||||
MODEL_TEMPLATE_PART1 = """
|
||||
#
|
||||
# CORE
|
||||
# Copyright (c)2013 Company.
|
||||
# See the LICENSE file included in this distribution.
|
||||
#
|
||||
# author: Name <email@company.com>
|
||||
#
|
||||
'''
|
||||
xyz.py: EMANE XYZ model bindings for CORE
|
||||
'''
|
||||
|
||||
from core.api import coreapi
|
||||
from emane import EmaneModel
|
||||
from universal import EmaneUniversalModel
|
||||
|
||||
class EmaneXyzModel(EmaneModel):
|
||||
def __init__(self, session, objid = None, verbose = False):
|
||||
EmaneModel.__init__(self, session, objid, verbose)
|
||||
|
||||
# model name
|
||||
_name = "emane_xyz"
|
||||
# MAC parameters
|
||||
_confmatrix_mac = [
|
||||
"""
|
||||
|
||||
MODEL_TEMPLATE_PART2 = """
|
||||
]
|
||||
|
||||
# PHY parameters from Universal PHY
|
||||
_confmatrix_phy = EmaneUniversalModel._confmatrix
|
||||
|
||||
_confmatrix = _confmatrix_mac + _confmatrix_phy
|
||||
|
||||
# value groupings
|
||||
_confgroups = "XYZ MAC Parameters:1-%d|Universal PHY Parameters:%d-%d" \
|
||||
% ( len(_confmatrix_mac), len(_confmatrix_mac) + 1, len(_confmatrix))
|
||||
|
||||
def buildnemxmlfiles(self, e, ifc):
|
||||
''' Build the necessary nem, mac, and phy XMLs in the given path.
|
||||
If an individual NEM has a nonstandard config, we need to build
|
||||
that file also. Otherwise the WLAN-wide nXXemane_xyznem.xml,
|
||||
nXXemane_xyzmac.xml, nXXemane_xyzphy.xml are used.
|
||||
'''
|
||||
values = e.getifcconfig(self.objid, self._name,
|
||||
self.getdefaultvalues(), ifc)
|
||||
if values is None:
|
||||
return
|
||||
nemdoc = e.xmldoc("nem")
|
||||
nem = nemdoc.getElementsByTagName("nem").pop()
|
||||
nem.setAttribute("name", "XYZ NEM")
|
||||
mactag = nemdoc.createElement("mac")
|
||||
mactag.setAttribute("definition", self.macxmlname(ifc))
|
||||
nem.appendChild(mactag)
|
||||
phytag = nemdoc.createElement("phy")
|
||||
phytag.setAttribute("definition", self.phyxmlname(ifc))
|
||||
nem.appendChild(phytag)
|
||||
e.xmlwrite(nemdoc, self.nemxmlname(ifc))
|
||||
|
||||
names = list(self.getnames())
|
||||
macnames = names[:len(self._confmatrix_mac)]
|
||||
phynames = names[len(self._confmatrix_mac):]
|
||||
# make any changes to the mac/phy names here to e.g. exclude them from
|
||||
# the XML output
|
||||
|
||||
macdoc = e.xmldoc("mac")
|
||||
mac = macdoc.getElementsByTagName("mac").pop()
|
||||
mac.setAttribute("name", "XYZ MAC")
|
||||
mac.setAttribute("library", "xyzmaclayer")
|
||||
# append MAC options to macdoc
|
||||
map( lambda n: mac.appendChild(e.xmlparam(macdoc, n, \
|
||||
self.valueof(n, values))), macnames)
|
||||
e.xmlwrite(macdoc, self.macxmlname(ifc))
|
||||
|
||||
phydoc = EmaneUniversalModel.getphydoc(e, self, values, phynames)
|
||||
e.xmlwrite(phydoc, self.phyxmlname(ifc))
|
||||
|
||||
"""
|
||||
|
||||
def emane_model_source_to_core(infile, outfile):
|
||||
do_parse_line = False
|
||||
output = MODEL_TEMPLATE_PART1
|
||||
|
||||
with open(infile, 'r') as f:
|
||||
for line in f:
|
||||
# begin marker
|
||||
if "EMANE::ConfigurationDefinition" in line:
|
||||
do_parse_line = True
|
||||
# end marker -- all done
|
||||
if "{0, 0, 0, 0, 0, 0" in line:
|
||||
break
|
||||
if do_parse_line:
|
||||
outstr = convert_line(line)
|
||||
if outstr is not None:
|
||||
output += outstr
|
||||
continue
|
||||
output += MODEL_TEMPLATE_PART2
|
||||
|
||||
if outfile == sys.stdout:
|
||||
sys.stdout.write(output)
|
||||
else:
|
||||
with open(outfile, 'w') as f:
|
||||
f.write(output)
|
||||
|
||||
def convert_line(line):
|
||||
line = line.strip()
|
||||
# skip comments
|
||||
if line.startswith(('/*', '//')):
|
||||
return None
|
||||
items = line.strip('{},').split(',')
|
||||
if len(items) != 7:
|
||||
#print "continuning on line=", len(items), items
|
||||
return None
|
||||
return convert_items_to_line(items)
|
||||
|
||||
def convert_items_to_line(items):
|
||||
fields = ('required', 'default', 'count', 'name', 'value', 'type',
|
||||
'description')
|
||||
getfield = lambda(x): items[fields.index(x)].strip()
|
||||
|
||||
output = " ("
|
||||
output += "%s, " % getfield('name')
|
||||
value = getfield('value')
|
||||
if value == '"off"':
|
||||
type = "coreapi.CONF_DATA_TYPE_BOOL"
|
||||
value = "0"
|
||||
defaults = '"On,Off"'
|
||||
elif value == '"on"':
|
||||
type = "coreapi.CONF_DATA_TYPE_BOOL"
|
||||
value = '"1"'
|
||||
defaults = '"On,Off"'
|
||||
else:
|
||||
type = "coreapi.CONF_DATA_TYPE_STRING"
|
||||
defaults = '""'
|
||||
output += "%s, %s, %s, " % (type, value, defaults)
|
||||
output += getfield('description')
|
||||
output += "),\n"
|
||||
return output
|
||||
|
||||
|
||||
def main():
|
||||
usagestr = "usage: %prog [-h] [options] -- <command> ..."
|
||||
parser = optparse.OptionParser(usage = usagestr)
|
||||
parser.set_defaults(infile = None, outfile = sys.stdout)
|
||||
|
||||
parser.add_option("-i", "--infile", dest = "infile",
|
||||
help = "file to read (usually '*mac.cc')")
|
||||
parser.add_option("-o", "--outfile", dest = "outfile",
|
||||
help = "file to write (stdout is default)")
|
||||
|
||||
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.infile is None:
|
||||
usage("please specify input file with the '-i' option", err=1)
|
||||
|
||||
emane_model_source_to_core(options.infile, options.outfile)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
78
daemon/examples/findcore.py
Executable file
78
daemon/examples/findcore.py
Executable file
|
@ -0,0 +1,78 @@
|
|||
#!/usr/bin/env python
|
||||
#
|
||||
# Search for installed CORE library files and Python bindings.
|
||||
#
|
||||
|
||||
import os, glob
|
||||
|
||||
pythondirs = [
|
||||
"/usr/lib/python2.7/site-packages",
|
||||
"/usr/lib/python2.7/dist-packages",
|
||||
"/usr/lib64/python2.7/site-packages",
|
||||
"/usr/lib64/python2.7/dist-packages",
|
||||
"/usr/local/lib/python2.7/site-packages",
|
||||
"/usr/local/lib/python2.7/dist-packages",
|
||||
"/usr/local/lib64/python2.7/site-packages",
|
||||
"/usr/local/lib64/python2.7/dist-packages",
|
||||
"/usr/lib/python2.6/site-packages",
|
||||
"/usr/lib/python2.6/dist-packages",
|
||||
"/usr/lib64/python2.6/site-packages",
|
||||
"/usr/lib64/python2.6/dist-packages",
|
||||
"/usr/local/lib/python2.6/site-packages",
|
||||
"/usr/local/lib/python2.6/dist-packages",
|
||||
"/usr/local/lib64/python2.6/site-packages",
|
||||
"/usr/local/lib64/python2.6/dist-packages",
|
||||
]
|
||||
|
||||
tcldirs = [
|
||||
"/usr/lib/core",
|
||||
"/usr/local/lib/core",
|
||||
]
|
||||
|
||||
def find_in_file(fn, search, column=None):
|
||||
''' Find a line starting with 'search' in the file given by the filename
|
||||
'fn'. Return True if found, False if not found, or the column text if
|
||||
column is specified.
|
||||
'''
|
||||
r = False
|
||||
if not os.path.exists(fn):
|
||||
return r
|
||||
f = open(fn, "r")
|
||||
for line in f:
|
||||
if line[:len(search)] != search:
|
||||
continue
|
||||
r = True
|
||||
if column is not None:
|
||||
r = line.split()[column]
|
||||
break
|
||||
f.close()
|
||||
return r
|
||||
|
||||
def main():
|
||||
versions = []
|
||||
for d in pythondirs:
|
||||
fn = "%s/core/constants.py" % d
|
||||
ver = find_in_file(fn, 'COREDPY_VERSION', 2)
|
||||
if ver:
|
||||
ver = ver.strip('"')
|
||||
versions.append((d, ver))
|
||||
for e in glob.iglob("%s/core_python*egg-info" % d):
|
||||
ver = find_in_file(e, 'Version:', 1)
|
||||
if ver:
|
||||
versions.append((e, ver))
|
||||
for e in glob.iglob("%s/netns*egg-info" % d):
|
||||
ver = find_in_file(e, 'Version:', 1)
|
||||
if ver:
|
||||
versions.append((e, ver))
|
||||
for d in tcldirs:
|
||||
fn = "%s/version.tcl" % d
|
||||
ver = find_in_file(fn, 'set CORE_VERSION', 2)
|
||||
if ver:
|
||||
versions.append((d, ver))
|
||||
|
||||
for (d, ver) in versions:
|
||||
print "%8s %s" % (ver, d)
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
26
daemon/examples/myservices/README.txt
Normal file
26
daemon/examples/myservices/README.txt
Normal file
|
@ -0,0 +1,26 @@
|
|||
This directory contains a sample custom service that you can use as a template
|
||||
for creating your own services.
|
||||
|
||||
Follow these steps to add your own services:
|
||||
|
||||
1. Modify the sample service MyService to do what you want. It could generate
|
||||
config/script files, mount per-node directories, start processes/scripts,
|
||||
etc. sample.py is a Python file that defines one or more classes to be
|
||||
imported. You can create multiple Python files that will be imported.
|
||||
Add any new filenames to the __init__.py file.
|
||||
|
||||
2. Put these files in a directory such as /home/username/.core/myservices
|
||||
Note that the last component of this directory name 'myservices' should not
|
||||
be named something like 'services' which conflicts with an existing Python
|
||||
name (the syntax 'from myservices import *' is used).
|
||||
|
||||
3. Add a 'custom_services_dir = /home/username/.core/myservices' entry to the
|
||||
/etc/core/core.conf file.
|
||||
|
||||
4. Restart the CORE daemon (core-daemon). Any import errors (Python syntax)
|
||||
should be displayed in the /var/log/core-daemon.log log file (or on screen).
|
||||
|
||||
5. Start using your custom service on your nodes. You can create a new node
|
||||
type that uses your service, or change the default services for an existing
|
||||
node type, or change individual nodes.
|
||||
|
7
daemon/examples/myservices/__init__.py
Normal file
7
daemon/examples/myservices/__init__.py
Normal file
|
@ -0,0 +1,7 @@
|
|||
"""myservices
|
||||
|
||||
Custom services that you define can be put in this directory. Everything
|
||||
listed in __all__ is automatically loaded when you add this directory to the
|
||||
custom_services_dir = '/full/path/to/here' core.conf file option.
|
||||
"""
|
||||
__all__ = ["sample"]
|
64
daemon/examples/myservices/sample.py
Normal file
64
daemon/examples/myservices/sample.py
Normal file
|
@ -0,0 +1,64 @@
|
|||
#
|
||||
# CORE
|
||||
# Copyright (c)2010-2012 the Boeing Company.
|
||||
# See the LICENSE file included in this distribution.
|
||||
#
|
||||
''' Sample user-defined service.
|
||||
'''
|
||||
|
||||
import os
|
||||
|
||||
from core.service import CoreService, addservice
|
||||
from core.misc.ipaddr import IPv4Prefix, IPv6Prefix
|
||||
|
||||
class MyService(CoreService):
|
||||
''' This is a sample user-defined service.
|
||||
'''
|
||||
# a unique name is required, without spaces
|
||||
_name = "MyService"
|
||||
# you can create your own group here
|
||||
_group = "Utility"
|
||||
# list of other services this service depends on
|
||||
_depends = ()
|
||||
# per-node directories
|
||||
_dirs = ()
|
||||
# generated files (without a full path this file goes in the node's dir,
|
||||
# e.g. /tmp/pycore.12345/n1.conf/)
|
||||
_configs = ('myservice.sh', )
|
||||
# this controls the starting order vs other enabled services
|
||||
_startindex = 50
|
||||
# list of startup commands, also may be generated during startup
|
||||
_startup = ('sh myservice.sh',)
|
||||
# list of shutdown commands
|
||||
_shutdown = ()
|
||||
|
||||
@classmethod
|
||||
def generateconfig(cls, node, filename, services):
|
||||
''' Return a string that will be written to filename, or sent to the
|
||||
GUI for user customization.
|
||||
'''
|
||||
cfg = "#!/bin/sh\n"
|
||||
cfg += "# auto-generated by MyService (sample.py)\n"
|
||||
|
||||
for ifc in node.netifs():
|
||||
cfg += 'echo "Node %s has interface %s"\n' % (node.name, ifc.name)
|
||||
# here we do something interesting
|
||||
cfg += "\n".join(map(cls.subnetentry, ifc.addrlist))
|
||||
break
|
||||
return cfg
|
||||
|
||||
@staticmethod
|
||||
def subnetentry(x):
|
||||
''' Generate a subnet declaration block given an IPv4 prefix string
|
||||
for inclusion in the config file.
|
||||
'''
|
||||
if x.find(":") >= 0:
|
||||
# this is an IPv6 address
|
||||
return ""
|
||||
else:
|
||||
net = IPv4Prefix(x)
|
||||
return 'echo " network %s"' % (net)
|
||||
|
||||
# this line is required to add the above class to the list of available services
|
||||
addservice(MyService)
|
||||
|
74
daemon/examples/netns/basicrange.py
Executable file
74
daemon/examples/netns/basicrange.py
Executable 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()
|
99
daemon/examples/netns/emane80211.py
Executable file
99
daemon/examples/netns/emane80211.py
Executable 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()
|
||||
|
209
daemon/examples/netns/howmanynodes.py
Executable file
209
daemon/examples/netns/howmanynodes.py
Executable 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()
|
109
daemon/examples/netns/iperf-performance-chain.py
Executable file
109
daemon/examples/netns/iperf-performance-chain.py
Executable 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()
|
||||
|
284
daemon/examples/netns/iperf-performance.sh
Executable file
284
daemon/examples/netns/iperf-performance.sh
Executable 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"
|
572
daemon/examples/netns/ospfmanetmdrtest.py
Executable file
572
daemon/examples/netns/ospfmanetmdrtest.py
Executable 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
78
daemon/examples/netns/switch.py
Executable 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()
|
||||
|
97
daemon/examples/netns/switchtest.py
Executable file
97
daemon/examples/netns/switchtest.py
Executable 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()
|
33
daemon/examples/netns/twonodes.sh
Executable file
33
daemon/examples/netns/twonodes.sh
Executable 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
|
772
daemon/examples/netns/wlanemanetests.py
Executable file
772
daemon/examples/netns/wlanemanetests.py
Executable 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()
|
98
daemon/examples/netns/wlantest.py
Executable file
98
daemon/examples/netns/wlantest.py
Executable 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()
|
30
daemon/examples/services/sampleFirewall
Normal file
30
daemon/examples/services/sampleFirewall
Normal file
|
@ -0,0 +1,30 @@
|
|||
# -------- CUSTOMIZATION REQUIRED --------
|
||||
#
|
||||
# Below are sample iptables firewall rules that you can uncomment and edit.
|
||||
# You can also use ip6tables rules for IPv6.
|
||||
#
|
||||
|
||||
# start by flushing all firewall rules (so this script may be re-run)
|
||||
#iptables -F
|
||||
|
||||
# allow traffic related to established connections
|
||||
#iptables -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT
|
||||
|
||||
# allow TCP packets from any source destined for 192.168.1.1
|
||||
#iptables -A INPUT -s 0/0 -i eth0 -d 192.168.1.1 -p TCP -j ACCEPT
|
||||
|
||||
# allow OpenVPN server traffic from eth0
|
||||
#iptables -A INPUT -p udp --dport 1194 -j ACCEPT
|
||||
#iptables -A INPUT -i eth0 -j DROP
|
||||
#iptables -A OUTPUT -p udp --sport 1194 -j ACCEPT
|
||||
#iptables -A OUTPUT -o eth0 -j DROP
|
||||
|
||||
# allow ICMP ping traffic
|
||||
#iptables -A OUTPUT -p icmp --icmp-type echo-request -j ACCEPT
|
||||
#iptables -A INPUT -p icmp --icmp-type echo-reply -j ACCEPT
|
||||
|
||||
# allow SSH traffic
|
||||
#iptables -A -p tcp -m state --state NEW -m tcp --dport 22 -j ACCEPT
|
||||
|
||||
# drop all other traffic coming in eth0
|
||||
#iptables -A INPUT -i eth0 -j DROP
|
119
daemon/examples/services/sampleIPsec
Normal file
119
daemon/examples/services/sampleIPsec
Normal file
|
@ -0,0 +1,119 @@
|
|||
# -------- CUSTOMIZATION REQUIRED --------
|
||||
#
|
||||
# The IPsec service builds ESP tunnels between the specified peers using the
|
||||
# racoon IKEv2 keying daemon. You need to provide keys and the addresses of
|
||||
# peers, along with subnets to tunnel.
|
||||
|
||||
# directory containing the certificate and key described below
|
||||
keydir=/etc/core/keys
|
||||
|
||||
# the name used for the "$certname.pem" x509 certificate and
|
||||
# "$certname.key" RSA private key, which can be generated using openssl
|
||||
certname=ipsec1
|
||||
|
||||
# list the public-facing IP addresses, starting with the localhost and followed
|
||||
# by each tunnel peer, separated with a single space
|
||||
tunnelhosts="172.16.0.1AND172.16.0.2 172.16.0.1AND172.16.2.1"
|
||||
|
||||
# Define T<i> where i is the index for each tunnel peer host from
|
||||
# the tunnel_hosts list above (0 is localhost).
|
||||
# T<i> is a list of IPsec tunnels with peer i, with a local subnet address
|
||||
# followed by the remote subnet address:
|
||||
# T<i>="<local>AND<remote> <local>AND<remote>"
|
||||
# For example, 172.16.0.0/24 is a local network (behind this node) to be
|
||||
# tunneled and 172.16.2.0/24 is a remote network (behind peer 1)
|
||||
T1="172.16.3.0/24AND172.16.5.0/24"
|
||||
T2="172.16.4.0/24AND172.16.5.0/24 172.16.4.0/24AND172.16.6.0/24"
|
||||
|
||||
# -------- END CUSTOMIZATION --------
|
||||
|
||||
echo "building config $PWD/ipsec.conf..."
|
||||
echo "building config $PWD/ipsec.conf..." > $PWD/ipsec.log
|
||||
|
||||
checkip=0
|
||||
if [ "$(dpkg -l | grep " sipcalc ")" = "" ]; then
|
||||
echo "WARNING: ip validation disabled because package sipcalc not installed
|
||||
" >> $PWD/ipsec.log
|
||||
checkip=1
|
||||
fi
|
||||
|
||||
echo "#!/usr/sbin/setkey -f
|
||||
# Flush the SAD and SPD
|
||||
flush;
|
||||
spdflush;
|
||||
|
||||
# Security policies \
|
||||
" > $PWD/ipsec.conf
|
||||
i=0
|
||||
for hostpair in $tunnelhosts; do
|
||||
i=`expr $i + 1`
|
||||
# parse tunnel host IP
|
||||
thishost=${hostpair%%AND*}
|
||||
peerhost=${hostpair##*AND}
|
||||
if [ $checkip = "0" ] &&
|
||||
[ "$(sipcalc "$thishost" "$peerhost" | grep ERR)" != "" ]; then
|
||||
echo "ERROR: invalid host address $thishost or $peerhost \
|
||||
" >> $PWD/ipsec.log
|
||||
fi
|
||||
# parse each tunnel addresses
|
||||
tunnel_list_var_name=T$i
|
||||
eval tunnels="$"$tunnel_list_var_name""
|
||||
for ttunnel in $tunnels; do
|
||||
lclnet=${ttunnel%%AND*}
|
||||
rmtnet=${ttunnel##*AND}
|
||||
if [ $checkip = "0" ] &&
|
||||
[ "$(sipcalc "$lclnet" "$rmtnet"| grep ERR)" != "" ]; then
|
||||
echo "ERROR: invalid tunnel address $lclnet and $rmtnet \
|
||||
" >> $PWD/ipsec.log
|
||||
fi
|
||||
# add tunnel policies
|
||||
echo "
|
||||
spdadd $lclnet $rmtnet any -P out ipsec
|
||||
esp/tunnel/$thishost-$peerhost/require;
|
||||
spdadd $rmtnet $lclnet any -P in ipsec
|
||||
esp/tunnel/$peerhost-$thishost/require; \
|
||||
" >> $PWD/ipsec.conf
|
||||
done
|
||||
done
|
||||
|
||||
echo "building config $PWD/racoon.conf..."
|
||||
if [ ! -e $keydir\/$certname.key ] || [ ! -e $keydir\/$certname.pem ]; then
|
||||
echo "ERROR: missing certification files under $keydir \
|
||||
$certname.key or $certname.pem " >> $PWD/ipsec.log
|
||||
fi
|
||||
echo "
|
||||
path certificate \"$keydir\";
|
||||
listen {
|
||||
adminsock disabled;
|
||||
}
|
||||
remote anonymous
|
||||
{
|
||||
exchange_mode main;
|
||||
certificate_type x509 \"$certname.pem\" \"$certname.key\";
|
||||
ca_type x509 \"ca-cert.pem\";
|
||||
my_identifier asn1dn;
|
||||
peers_identifier asn1dn;
|
||||
|
||||
proposal {
|
||||
encryption_algorithm 3des ;
|
||||
hash_algorithm sha1;
|
||||
authentication_method rsasig ;
|
||||
dh_group modp768;
|
||||
}
|
||||
}
|
||||
sainfo anonymous
|
||||
{
|
||||
pfs_group modp768;
|
||||
lifetime time 1 hour ;
|
||||
encryption_algorithm 3des, blowfish 448, rijndael ;
|
||||
authentication_algorithm hmac_sha1, hmac_md5 ;
|
||||
compression_algorithm deflate ;
|
||||
}
|
||||
" > $PWD/racoon.conf
|
||||
|
||||
# the setkey program is required from the ipsec-tools package
|
||||
echo "running setkey -f $PWD/ipsec.conf..."
|
||||
setkey -f $PWD/ipsec.conf
|
||||
|
||||
echo "running racoon -d -f $PWD/racoon.conf..."
|
||||
racoon -d -f $PWD/racoon.conf -l racoon.log
|
63
daemon/examples/services/sampleVPNClient
Normal file
63
daemon/examples/services/sampleVPNClient
Normal file
|
@ -0,0 +1,63 @@
|
|||
# -------- CUSTOMIZATION REQUIRED --------
|
||||
#
|
||||
# The VPNClient service builds a VPN tunnel to the specified VPN server using
|
||||
# OpenVPN software and a virtual TUN/TAP device.
|
||||
|
||||
# directory containing the certificate and key described below
|
||||
keydir=/etc/core/keys
|
||||
|
||||
# the name used for a "$keyname.crt" certificate and "$keyname.key" private key.
|
||||
keyname=client1
|
||||
|
||||
# the public IP address of the VPN server this client should connect with
|
||||
vpnserver="10.0.2.10"
|
||||
|
||||
# optional next hop for adding a static route to reach the VPN server
|
||||
nexthop="10.0.1.1"
|
||||
|
||||
# --------- END CUSTOMIZATION --------
|
||||
|
||||
# validate addresses
|
||||
if [ "$(dpkg -l | grep " sipcalc ")" = "" ]; then
|
||||
echo "WARNING: ip validation disabled because package sipcalc not installed
|
||||
" > $PWD/vpnclient.log
|
||||
else
|
||||
if [ "$(sipcalc "$vpnserver" "$nexthop" | grep ERR)" != "" ]; then
|
||||
echo "ERROR: invalide address $vpnserver or $nexthop \
|
||||
" > $PWD/vpnclient.log
|
||||
fi
|
||||
fi
|
||||
|
||||
# validate key and certification files
|
||||
if [ ! -e $keydir\/$keyname.key ] || [ ! -e $keydir\/$keyname.crt ] \
|
||||
|| [ ! -e $keydir\/ca.crt ] || [ ! -e $keydir\/dh1024.pem ]; then
|
||||
echo "ERROR: missing certification or key files under $keydir \
|
||||
$keyname.key or $keyname.crt or ca.crt or dh1024.pem" >> $PWD/vpnclient.log
|
||||
fi
|
||||
|
||||
# if necessary, add a static route for reaching the VPN server IP via the IF
|
||||
vpnservernet=${vpnserver%.*}.0/24
|
||||
if [ "$nexthop" != "" ]; then
|
||||
/sbin/ip route add $vpnservernet via $nexthop
|
||||
fi
|
||||
|
||||
# create openvpn client.conf
|
||||
(
|
||||
cat << EOF
|
||||
client
|
||||
dev tun
|
||||
proto udp
|
||||
remote $vpnserver 1194
|
||||
nobind
|
||||
ca $keydir/ca.crt
|
||||
cert $keydir/$keyname.crt
|
||||
key $keydir/$keyname.key
|
||||
dh $keydir/dh1024.pem
|
||||
cipher AES-256-CBC
|
||||
log $PWD/openvpn-client.log
|
||||
verb 4
|
||||
daemon
|
||||
EOF
|
||||
) > client.conf
|
||||
|
||||
openvpn --config client.conf
|
147
daemon/examples/services/sampleVPNServer
Normal file
147
daemon/examples/services/sampleVPNServer
Normal file
|
@ -0,0 +1,147 @@
|
|||
# -------- CUSTOMIZATION REQUIRED --------
|
||||
#
|
||||
# The VPNServer service sets up the OpenVPN server for building VPN tunnels
|
||||
# that allow access via TUN/TAP device to private networks.
|
||||
#
|
||||
# note that the IPForward and DefaultRoute services should be enabled
|
||||
|
||||
# directory containing the certificate and key described below, in addition to
|
||||
# a CA certificate and DH key
|
||||
keydir=/etc/core/keys
|
||||
|
||||
# the name used for a "$keyname.crt" certificate and "$keyname.key" private key.
|
||||
keyname=server2
|
||||
|
||||
# the VPN subnet address from which the client VPN IP (for the TUN/TAP)
|
||||
# will be allocated
|
||||
vpnsubnet=10.0.200.0
|
||||
|
||||
# public IP address of this vpn server (same as VPNClient vpnserver= setting)
|
||||
vpnserver=10.0.2.10
|
||||
|
||||
# optional list of private subnets reachable behind this VPN server
|
||||
# each subnet and next hop is separated by a space
|
||||
# "<subnet1>,<nexthop1> <subnet2>,<nexthop2> ..."
|
||||
privatenets="10.0.11.0,10.0.10.1 10.0.12.0,10.0.10.1"
|
||||
|
||||
# optional list of VPN clients, for statically assigning IP addresses to
|
||||
# clients; also, an optional client subnet can be specified for adding static
|
||||
# routes via the client
|
||||
# Note: VPN addresses x.x.x.0-3 are reserved
|
||||
# "<keyname>,<vpnIP>,<subnetIP> <keyname>,<vpnIP>,<subnetIP> ..."
|
||||
vpnclients="client1KeyFilename,10.0.200.5,10.0.0.0 client2KeyFilename,,"
|
||||
|
||||
# NOTE: you may need to enable the StaticRoutes service on nodes within the
|
||||
# private subnet, in order to have routes back to the client.
|
||||
# /sbin/ip ro add <vpnsubnet>/24 via <vpnServerRemoteInterface>
|
||||
# /sbin/ip ro add <vpnClientSubnet>/24 via <vpnServerRemoteInterface>
|
||||
|
||||
# -------- END CUSTOMIZATION --------
|
||||
|
||||
echo > $PWD/vpnserver.log
|
||||
rm -f -r $PWD/ccd
|
||||
|
||||
# validate key and certification files
|
||||
if [ ! -e $keydir\/$keyname.key ] || [ ! -e $keydir\/$keyname.crt ] \
|
||||
|| [ ! -e $keydir\/ca.crt ] || [ ! -e $keydir\/dh1024.pem ]; then
|
||||
echo "ERROR: missing certification or key files under $keydir \
|
||||
$keyname.key or $keyname.crt or ca.crt or dh1024.pem" >> $PWD/vpnserver.log
|
||||
fi
|
||||
|
||||
# validate configuration IP addresses
|
||||
checkip=0
|
||||
if [ "$(dpkg -l | grep " sipcalc ")" = "" ]; then
|
||||
echo "WARNING: ip validation disabled because package sipcalc not installed\
|
||||
" >> $PWD/vpnserver.log
|
||||
checkip=1
|
||||
else
|
||||
if [ "$(sipcalc "$vpnsubnet" "$vpnserver" | grep ERR)" != "" ]; then
|
||||
echo "ERROR: invalid vpn subnet or server address \
|
||||
$vpnsubnet or $vpnserver " >> $PWD/vpnserver.log
|
||||
fi
|
||||
fi
|
||||
|
||||
# create client vpn ip pool file
|
||||
(
|
||||
cat << EOF
|
||||
EOF
|
||||
)> $PWD/ippool.txt
|
||||
|
||||
# create server.conf file
|
||||
(
|
||||
cat << EOF
|
||||
# openvpn server config
|
||||
local $vpnserver
|
||||
server $vpnsubnet 255.255.255.0
|
||||
push redirect-gateway def1
|
||||
EOF
|
||||
)> $PWD/server.conf
|
||||
|
||||
# add routes to VPN server private subnets, and push these routes to clients
|
||||
for privatenet in $privatenets; do
|
||||
if [ $privatenet != "" ]; then
|
||||
net=${privatenet%%,*}
|
||||
nexthop=${privatenet##*,}
|
||||
if [ $checkip = "0" ] &&
|
||||
[ "$(sipcalc "$net" "$nexthop" | grep ERR)" != "" ]; then
|
||||
echo "ERROR: invalid vpn server private net address \
|
||||
$net or $nexthop " >> $PWD/vpnserver.log
|
||||
fi
|
||||
echo push route $net 255.255.255.0 >> $PWD/server.conf
|
||||
/sbin/ip ro add $net/24 via $nexthop
|
||||
/sbin/ip ro add $vpnsubnet/24 via $nexthop
|
||||
fi
|
||||
done
|
||||
|
||||
# allow subnet through this VPN, one route for each client subnet
|
||||
for client in $vpnclients; do
|
||||
if [ $client != "" ]; then
|
||||
cSubnetIP=${client##*,}
|
||||
cVpnIP=${client#*,}
|
||||
cVpnIP=${cVpnIP%%,*}
|
||||
cKeyFilename=${client%%,*}
|
||||
if [ "$cSubnetIP" != "" ]; then
|
||||
if [ $checkip = "0" ] &&
|
||||
[ "$(sipcalc "$cSubnetIP" "$cVpnIP" | grep ERR)" != "" ]; then
|
||||
echo "ERROR: invalid vpn client and subnet address \
|
||||
$cSubnetIP or $cVpnIP " >> $PWD/vpnserver.log
|
||||
fi
|
||||
echo route $cSubnetIP 255.255.255.0 >> $PWD/server.conf
|
||||
if ! test -d $PWD/ccd; then
|
||||
mkdir -p $PWD/ccd
|
||||
echo client-config-dir $PWD/ccd >> $PWD/server.conf
|
||||
fi
|
||||
if test -e $PWD/ccd/$cKeyFilename; then
|
||||
echo iroute $cSubnetIP 255.255.255.0 >> $PWD/ccd/$cKeyFilename
|
||||
else
|
||||
echo iroute $cSubnetIP 255.255.255.0 > $PWD/ccd/$cKeyFilename
|
||||
fi
|
||||
fi
|
||||
if [ "$cVpnIP" != "" ]; then
|
||||
echo $cKeyFilename,$cVpnIP >> $PWD/ippool.txt
|
||||
fi
|
||||
fi
|
||||
done
|
||||
|
||||
(
|
||||
cat << EOF
|
||||
keepalive 10 120
|
||||
ca $keydir/ca.crt
|
||||
cert $keydir/$keyname.crt
|
||||
key $keydir/$keyname.key
|
||||
dh $keydir/dh1024.pem
|
||||
cipher AES-256-CBC
|
||||
status /var/log/openvpn-status.log
|
||||
log /var/log/openvpn-server.log
|
||||
ifconfig-pool-linear
|
||||
ifconfig-pool-persist $PWD/ippool.txt
|
||||
port 1194
|
||||
proto udp
|
||||
dev tun
|
||||
verb 4
|
||||
daemon
|
||||
EOF
|
||||
)>> $PWD/server.conf
|
||||
|
||||
# start vpn server
|
||||
openvpn --config server.conf
|
45
daemon/examples/stopsession.py
Executable file
45
daemon/examples/stopsession.py
Executable file
|
@ -0,0 +1,45 @@
|
|||
#!/usr/bin/env python
|
||||
# (c)2010-2012 the Boeing Company
|
||||
# author: Jeff Ahrenholz <jeffrey.m.ahrenholz@boeing.com>
|
||||
#
|
||||
# List and stop CORE sessions from the command line.
|
||||
#
|
||||
|
||||
import socket, optparse
|
||||
from core.constants import *
|
||||
from core.api import coreapi
|
||||
|
||||
def main():
|
||||
parser = optparse.OptionParser(usage = "usage: %prog [-l] <sessionid>")
|
||||
parser.add_option("-l", "--list", dest = "list", action = "store_true",
|
||||
help = "list running sessions")
|
||||
(options, args) = parser.parse_args()
|
||||
|
||||
if options.list is True:
|
||||
num = '0'
|
||||
flags = coreapi.CORE_API_STR_FLAG
|
||||
else:
|
||||
num = args[0]
|
||||
flags = coreapi.CORE_API_DEL_FLAG
|
||||
tlvdata = coreapi.CoreSessionTlv.pack(coreapi.CORE_TLV_SESS_NUMBER, num)
|
||||
msg = coreapi.CoreSessionMessage.pack(flags, tlvdata)
|
||||
|
||||
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||
sock.connect(('localhost', coreapi.CORE_API_PORT))
|
||||
sock.send(msg)
|
||||
|
||||
# receive and print a session list
|
||||
if options.list is True:
|
||||
hdr = sock.recv(coreapi.CoreMessage.hdrsiz)
|
||||
msgtype, msgflags, msglen = coreapi.CoreMessage.unpackhdr(hdr)
|
||||
data = ""
|
||||
if msglen:
|
||||
data = sock.recv(msglen)
|
||||
msg = coreapi.CoreMessage(msgflags, hdr, data)
|
||||
sessions = msg.gettlv(coreapi.CORE_TLV_SESS_NUMBER)
|
||||
print "sessions:", sessions
|
||||
|
||||
sock.close()
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
Loading…
Add table
Add a link
Reference in a new issue