Minor refactoring of BMv2 mininet scripts

With options to delay pushing the netcfg for each device and generating
the full netcfg JSON for bmv2-demo.

Change-Id: I046a93a8c639f4bb4cf76cbd61b826473760bfb1
This commit is contained in:
Carmelo Cascone 2017-11-22 14:27:06 -08:00
parent 6168994ad6
commit 6ec8f8ff93
2 changed files with 90 additions and 55 deletions

View File

@ -35,7 +35,8 @@ class ONOSHost(Host):
def config(self, **params): def config(self, **params):
r = super(Host, self).config(**params) r = super(Host, self).config(**params)
for off in ["rx", "tx", "sg"]: for off in ["rx", "tx", "sg"]:
cmd = "/sbin/ethtool --offload %s %s off" % (self.defaultIntf(), off) cmd = "/sbin/ethtool --offload %s %s off"\
% (self.defaultIntf(), off)
self.cmd(cmd) self.cmd(cmd)
# disable IPv6 # disable IPv6
self.cmd("sysctl -w net.ipv6.conf.all.disable_ipv6=1") self.cmd("sysctl -w net.ipv6.conf.all.disable_ipv6=1")
@ -52,7 +53,8 @@ class ONOSBmv2Switch(Switch):
def __init__(self, name, json=None, debugger=False, loglevel="warn", elogger=False, def __init__(self, name, json=None, debugger=False, loglevel="warn", elogger=False,
persistent=False, grpcPort=None, thriftPort=None, netcfg=True, dryrun=False, persistent=False, grpcPort=None, thriftPort=None, netcfg=True, dryrun=False,
pipeconfId="", pktdump=False, valgrind=False, **kwargs): pipeconfId="", pktdump=False, valgrind=False, netcfgDelay=0,
**kwargs):
Switch.__init__(self, name, **kwargs) Switch.__init__(self, name, **kwargs)
self.grpcPort = ONOSBmv2Switch.pickUnusedPort() if not grpcPort else grpcPort self.grpcPort = ONOSBmv2Switch.pickUnusedPort() if not grpcPort else grpcPort
self.thriftPort = ONOSBmv2Switch.pickUnusedPort() if not thriftPort else thriftPort self.thriftPort = ONOSBmv2Switch.pickUnusedPort() if not thriftPort else thriftPort
@ -71,6 +73,7 @@ class ONOSBmv2Switch(Switch):
self.netcfg = parseBoolean(netcfg) self.netcfg = parseBoolean(netcfg)
self.dryrun = parseBoolean(dryrun) self.dryrun = parseBoolean(dryrun)
self.valgrind = parseBoolean(valgrind) self.valgrind = parseBoolean(valgrind)
self.netcfgDelay = netcfgDelay
self.netcfgfile = '/tmp/bmv2-%d-netcfg.json' % self.deviceId self.netcfgfile = '/tmp/bmv2-%d-netcfg.json' % self.deviceId
self.pipeconfId = pipeconfId self.pipeconfId = pipeconfId
if persistent: if persistent:
@ -229,11 +232,13 @@ class ONOSBmv2Switch(Switch):
out = self.cmd(cmdStr) out = self.cmd(cmdStr)
if out: if out:
print out print out
if self.netcfg and self.valgrind: if self.netcfg:
# With valgrind, it takes some time before the gRPC server is available. if self.valgrind:
# Wait before pushing the netcfg. # With valgrind, it takes some time before the gRPC server is available.
info("\n*** Waiting %d seconds before pushing the config to ONOS...\n" % VALGRIND_SLEEP) # Wait before pushing the netcfg.
time.sleep(VALGRIND_SLEEP) info("\n*** Waiting %d seconds before pushing the config to ONOS...\n" % VALGRIND_SLEEP)
time.sleep(VALGRIND_SLEEP)
time.sleep(self.netcfgDelay)
try: # onos.py try: # onos.py
clist = controllers[0].nodes() clist = controllers[0].nodes()

View File

@ -42,8 +42,12 @@ from mininet.node import RemoteController, Host
from mininet.topo import Topo from mininet.topo import Topo
def getCmdBg(cmd, logfile="/dev/null"):
return "{} > {} 2>&1 &".format(cmd, logfile)
class ClosTopo(Topo): class ClosTopo(Topo):
"2 stage Clos topology" """2 stage Clos topology"""
def __init__(self, args, **opts): def __init__(self, args, **opts):
# Initialize topology and default options # Initialize topology and default options
@ -58,16 +62,17 @@ class ClosTopo(Topo):
for switchId in bmv2SwitchIds: for switchId in bmv2SwitchIds:
deviceId = int(switchId[1:]) deviceId = int(switchId[1:])
# Use first number in device id to calculate latitude (row number) # Use first number in device id to calculate latitude (row number),
# use second to calculate longitude (column number)
latitude = SWITCH_BASE_LATITUDE + (deviceId // 10) * BASE_SHIFT latitude = SWITCH_BASE_LATITUDE + (deviceId // 10) * BASE_SHIFT
# Use second number in device id to calculate longitude (column number)
longitude = BASE_LONGITUDE + (deviceId % 10) * BASE_SHIFT longitude = BASE_LONGITUDE + (deviceId % 10) * BASE_SHIFT
bmv2Switches[switchId] = self.addSwitch(switchId, bmv2Switches[switchId] = self.addSwitch(switchId,
cls=ONOSBmv2Switch, cls=ONOSBmv2Switch,
loglevel="warn", loglevel=args.log_level,
deviceId=deviceId, deviceId=deviceId,
netcfg=False, netcfg=True,
netcfgDelay=0.5,
longitude=longitude, longitude=longitude,
latitude=latitude, latitude=latitude,
pipeconfId=args.pipeconf_id) pipeconfId=args.pipeconf_id)
@ -75,14 +80,17 @@ class ClosTopo(Topo):
for i in range(1, args.size + 1): for i in range(1, args.size + 1):
for j in range(1, args.size + 1): for j in range(1, args.size + 1):
if i == j: if i == j:
# 2 links self.addLink(bmv2Switches["s1%d" % i],
self.addLink(bmv2Switches["s1%d" % i], bmv2Switches["s2%d" % j], bmv2Switches["s2%d" % j],
cls=TCLink, bw=DEFAULT_SW_BW) cls=TCLink, bw=DEFAULT_SW_BW)
if args.with_imbalanced_striping: if args.with_imbalanced_striping:
self.addLink(bmv2Switches["s1%d" % i], bmv2Switches["s2%d" % j], # 2 links
self.addLink(bmv2Switches["s1%d" % i],
bmv2Switches["s2%d" % j],
cls=TCLink, bw=DEFAULT_SW_BW) cls=TCLink, bw=DEFAULT_SW_BW)
else: else:
self.addLink(bmv2Switches["s1%d" % i], bmv2Switches["s2%d" % j], self.addLink(bmv2Switches["s1%d" % i],
bmv2Switches["s2%d" % j],
cls=TCLink, bw=DEFAULT_SW_BW) cls=TCLink, bw=DEFAULT_SW_BW)
for hostId in range(1, args.size + 1): for hostId in range(1, args.size + 1):
@ -90,11 +98,12 @@ class ClosTopo(Topo):
cls=DemoHost, cls=DemoHost,
ip="10.0.0.%d/24" % hostId, ip="10.0.0.%d/24" % hostId,
mac='00:00:00:00:00:%02x' % hostId) mac='00:00:00:00:00:%02x' % hostId)
self.addLink(host, bmv2Switches["s1%d" % hostId], cls=TCLink, bw=DEFAULT_HOST_BW) self.addLink(host, bmv2Switches["s1%d" % hostId],
cls=TCLink, bw=DEFAULT_HOST_BW)
class DemoHost(ONOSHost): class DemoHost(ONOSHost):
"Demo host" """Demo host"""
def __init__(self, name, **params): def __init__(self, name, **params):
ONOSHost.__init__(self, name, **params) ONOSHost.__init__(self, name, **params)
@ -109,10 +118,11 @@ class DemoHost(ONOSHost):
self.cmd(self.getInfiniteCmdBg("iperf -s -u")) self.cmd(self.getInfiniteCmdBg("iperf -s -u"))
def startIperfClient(self, h, flowBw="512k", numFlows=5, duration=5): def startIperfClient(self, h, flowBw="512k", numFlows=5, duration=5):
iperfCmd = "iperf -c{} -u -b{} -P{} -t{}".format(h.IP(), flowBw, numFlows, duration) iperfCmd = "iperf -c{} -u -b{} -P{} -t{}".format(
self.cmd(self.getInfiniteCmdBg(iperfCmd, sleep=0)) h.IP(), flowBw, numFlows, duration)
self.cmd(self.getInfiniteCmdBg(iperfCmd, delay=0))
def stop(self): def stop(self, **kwargs):
self.cmd("killall iperf") self.cmd("killall iperf")
self.cmd("killall ping") self.cmd("killall ping")
self.cmd("killall arping") self.cmd("killall arping")
@ -127,28 +137,27 @@ class DemoHost(ONOSHost):
) )
print "**********" print "**********"
def getInfiniteCmdBg(self, cmd, logfile="/dev/null", sleep=1): def getInfiniteCmdBg(self, cmd, logfile="/dev/null", delay=1):
return "(while [ -e {} ]; " \ return "(while [ -e {} ]; " \
"do {}; " \ "do {}; " \
"sleep {}; " \ "sleep {}; " \
"done;) > {} 2>&1 &".format(self.exectoken, cmd, sleep, logfile) "done;) > {} 2>&1 &".format(self.exectoken, cmd, delay, logfile)
def getCmdBg(self, cmd, logfile="/dev/null"):
return "{} > {} 2>&1 &".format(cmd, logfile)
def generateNetcfg(onosIp, net, args): def generateNetcfg(onosIp, net, args):
netcfg = OrderedDict() netcfg = OrderedDict()
netcfg['hosts'] = {}
netcfg['devices'] = {} netcfg['devices'] = {}
netcfg['links'] = {} netcfg['links'] = {}
netcfg['hosts'] = {}
# Device configs if args.full_netcfg:
for sw in net.switches: # Device configs
srcIp = sw.getSourceIp(onosIp) for sw in net.switches:
netcfg['devices'][sw.onosDeviceId] = sw.getDeviceConfig(srcIp) srcIp = sw.getSourceIp(onosIp)
netcfg['devices'][sw.onosDeviceId] = sw.getDeviceConfig(srcIp)
hostLocations = {} hostLocations = {}
# Link configs
for link in net.links: for link in net.links:
switchPort = link.intf1.name.split('-') switchPort = link.intf1.name.split('-')
sw1Name = switchPort[0] # s11 sw1Name = switchPort[0] # s11
@ -172,14 +181,18 @@ def generateNetcfg(onosIp, net, args):
hostLocations[sw2.name] = '%s/%s' % (sw1.onosDeviceId, port1) hostLocations[sw2.name] = '%s/%s' % (sw1.onosDeviceId, port1)
continue continue
for linkId in ('%s/%s-%s/%s' % (sw1.onosDeviceId, port1, sw2.onosDeviceId, port2), if args.full_netcfg:
'%s/%s-%s/%s' % (sw2.onosDeviceId, port2, sw1.onosDeviceId, port1)): # Link configs
netcfg['links'][linkId] = { for linkId in ('%s/%s-%s/%s' % (sw1.onosDeviceId, port1,
'basic': { sw2.onosDeviceId, port2),
'type': 'DIRECT', '%s/%s-%s/%s' % (sw2.onosDeviceId, port2,
'bandwidth': DEFAULT_SW_BW sw1.onosDeviceId, port1)):
netcfg['links'][linkId] = {
'basic': {
'type': 'DIRECT',
'bandwidth': DEFAULT_SW_BW
}
} }
}
# Host configs # Host configs
longitude = BASE_LONGITUDE longitude = BASE_LONGITUDE
@ -203,13 +216,14 @@ def generateNetcfg(onosIp, net, args):
} }
netcfg['hosts'][hostId] = hostConfig netcfg['hosts'][hostId] = hostConfig
netcfg["apps"] = { if args.full_netcfg:
"org.onosproject.core": { netcfg["apps"] = {
"core": { "org.onosproject.core": {
"linkDiscoveryMode": "STRICT" "core": {
"linkDiscoveryMode": "STRICT"
}
} }
} }
}
print "Writing network config to %s" % TEMP_NETCFG_FILE print "Writing network config to %s" % TEMP_NETCFG_FILE
with open(TEMP_NETCFG_FILE, 'w') as tempFile: with open(TEMP_NETCFG_FILE, 'w') as tempFile:
@ -233,7 +247,7 @@ def main(args):
print "Network started" print "Network started"
# Generate background traffic. # Always generate background pings.
sleep(3) sleep(3)
for (h1, h2) in combinations(net.hosts, 2): for (h1, h2) in combinations(net.hosts, 2):
h1.startPingBg(h2) h1.startPingBg(h2)
@ -246,14 +260,17 @@ def main(args):
print "Iperf servers started" print "Iperf servers started"
# sleep(4) if args.bg_traffic:
# print "Starting traffic from h1 to h3..." sleep(4)
# net.hosts[0].startIperfClient(net.hosts[-1], flowBw="200k", numFlows=100, duration=10) print "Starting iperf clients..."
net.hosts[0].startIperfClient(net.hosts[-1], flowBw="400k",
numFlows=50, duration=10)
generateNetcfg(onosIp, net, args) generateNetcfg(onosIp, net, args)
if args.netcfg_sleep > 0: if args.netcfg_sleep > 0:
print "Waiting %d seconds before pushing config to ONOS..." % args.netcfg_sleep print "Waiting %d seconds before pushing config to ONOS..." \
% args.netcfg_sleep
sleep(args.netcfg_sleep) sleep(args.netcfg_sleep)
print "Pushing config to ONOS..." print "Pushing config to ONOS..."
@ -275,12 +292,25 @@ if __name__ == '__main__':
type=str, action="store", required=False) type=str, action="store", required=False)
parser.add_argument('--size', help='Number of leaf/spine switches', parser.add_argument('--size', help='Number of leaf/spine switches',
type=int, action="store", required=False, default=2) type=int, action="store", required=False, default=2)
parser.add_argument('--with-imbalanced-striping', help='Topology with imbalanced striping', parser.add_argument('--with-imbalanced-striping',
type=bool, action="store", required=False, default=False) help='Topology with imbalanced striping',
type=bool, action="store", required=False,
default=False)
parser.add_argument('--pipeconf-id', help='Pipeconf ID for switches', parser.add_argument('--pipeconf-id', help='Pipeconf ID for switches',
type=str, action="store", required=False, default='') type=str, action="store", required=False, default='')
parser.add_argument('--netcfg-sleep', help='Seconds to wait before pushing config to ONOS', parser.add_argument('--netcfg-sleep',
help='Seconds to wait before pushing config to ONOS',
type=int, action="store", required=False, default=5) type=int, action="store", required=False, default=5)
args = parser.parse_args() parser.add_argument('--log-level', help='BMv2 log level',
type=str, action="store", required=False,
default='warn')
parser.add_argument('--full-netcfg',
help='Generate full netcfg JSON with links and devices',
type=bool, action="store", required=False,
default=False)
parser.add_argument('--bg-traffic',
help='Starts background traffic',
type=bool, action="store", required=False,
default=False)
setLogLevel('info') setLogLevel('info')
main(args) main(parser.parse_args())