mirror of
https://github.com/opennetworkinglab/onos.git
synced 2025-10-15 01:11:30 +02:00
With options to delay pushing the netcfg for each device and generating the full netcfg JSON for bmv2-demo. Change-Id: I046a93a8c639f4bb4cf76cbd61b826473760bfb1
317 lines
11 KiB
Python
Executable File
317 lines
11 KiB
Python
Executable File
#!/usr/bin/python
|
|
|
|
import os
|
|
import sys
|
|
import json
|
|
import argparse
|
|
from collections import OrderedDict
|
|
|
|
TEMP_NETCFG_FILE = '/tmp/bmv2-demo-cfg.json'
|
|
BASE_LONGITUDE = -115
|
|
SWITCH_BASE_LATITUDE = 25
|
|
HOST_BASE_LATITUDE = 28
|
|
BASE_SHIFT = 8
|
|
VLAN_NONE = -1
|
|
DEFAULT_SW_BW = 50
|
|
DEFAULT_HOST_BW = 25
|
|
|
|
if 'ONOS_ROOT' not in os.environ:
|
|
print "Environment var $ONOS_ROOT not set"
|
|
exit()
|
|
else:
|
|
ONOS_ROOT = os.environ["ONOS_ROOT"]
|
|
sys.path.append(ONOS_ROOT + "/tools/dev/mininet")
|
|
if 'RUN_PACK_PATH' not in os.environ:
|
|
print "Environment var $RUN_PACK_PATH not set"
|
|
exit()
|
|
else:
|
|
RUN_PACK_PATH = os.environ["RUN_PACK_PATH"]
|
|
|
|
from onos import ONOSCluster, ONOSCLI
|
|
from bmv2 import ONOSBmv2Switch, ONOSHost
|
|
|
|
from itertools import combinations
|
|
from time import sleep
|
|
from subprocess import call
|
|
|
|
from mininet.cli import CLI
|
|
from mininet.link import TCLink
|
|
from mininet.log import setLogLevel
|
|
from mininet.net import Mininet
|
|
from mininet.node import RemoteController, Host
|
|
from mininet.topo import Topo
|
|
|
|
|
|
def getCmdBg(cmd, logfile="/dev/null"):
|
|
return "{} > {} 2>&1 &".format(cmd, logfile)
|
|
|
|
|
|
class ClosTopo(Topo):
|
|
"""2 stage Clos topology"""
|
|
|
|
def __init__(self, args, **opts):
|
|
# Initialize topology and default options
|
|
Topo.__init__(self, **opts)
|
|
|
|
bmv2SwitchIds = []
|
|
for row in (1, 2):
|
|
for col in range(1, args.size + 1):
|
|
bmv2SwitchIds.append("s%d%d" % (row, col))
|
|
|
|
bmv2Switches = {}
|
|
|
|
for switchId in bmv2SwitchIds:
|
|
deviceId = int(switchId[1:])
|
|
# 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
|
|
longitude = BASE_LONGITUDE + (deviceId % 10) * BASE_SHIFT
|
|
|
|
bmv2Switches[switchId] = self.addSwitch(switchId,
|
|
cls=ONOSBmv2Switch,
|
|
loglevel=args.log_level,
|
|
deviceId=deviceId,
|
|
netcfg=True,
|
|
netcfgDelay=0.5,
|
|
longitude=longitude,
|
|
latitude=latitude,
|
|
pipeconfId=args.pipeconf_id)
|
|
|
|
for i in range(1, args.size + 1):
|
|
for j in range(1, args.size + 1):
|
|
if i == j:
|
|
self.addLink(bmv2Switches["s1%d" % i],
|
|
bmv2Switches["s2%d" % j],
|
|
cls=TCLink, bw=DEFAULT_SW_BW)
|
|
if args.with_imbalanced_striping:
|
|
# 2 links
|
|
self.addLink(bmv2Switches["s1%d" % i],
|
|
bmv2Switches["s2%d" % j],
|
|
cls=TCLink, bw=DEFAULT_SW_BW)
|
|
else:
|
|
self.addLink(bmv2Switches["s1%d" % i],
|
|
bmv2Switches["s2%d" % j],
|
|
cls=TCLink, bw=DEFAULT_SW_BW)
|
|
|
|
for hostId in range(1, args.size + 1):
|
|
host = self.addHost("h%d" % hostId,
|
|
cls=DemoHost,
|
|
ip="10.0.0.%d/24" % hostId,
|
|
mac='00:00:00:00:00:%02x' % hostId)
|
|
self.addLink(host, bmv2Switches["s1%d" % hostId],
|
|
cls=TCLink, bw=DEFAULT_HOST_BW)
|
|
|
|
|
|
class DemoHost(ONOSHost):
|
|
"""Demo host"""
|
|
|
|
def __init__(self, name, **params):
|
|
ONOSHost.__init__(self, name, **params)
|
|
self.exectoken = "/tmp/mn-exec-token-host-%s" % name
|
|
self.cmd("touch %s" % self.exectoken)
|
|
|
|
def startPingBg(self, h):
|
|
self.cmd(self.getInfiniteCmdBg("ping -i0.5 %s" % h.IP()))
|
|
self.cmd(self.getInfiniteCmdBg("arping -w5000000 %s" % h.IP()))
|
|
|
|
def startIperfServer(self):
|
|
self.cmd(self.getInfiniteCmdBg("iperf -s -u"))
|
|
|
|
def startIperfClient(self, h, flowBw="512k", numFlows=5, duration=5):
|
|
iperfCmd = "iperf -c{} -u -b{} -P{} -t{}".format(
|
|
h.IP(), flowBw, numFlows, duration)
|
|
self.cmd(self.getInfiniteCmdBg(iperfCmd, delay=0))
|
|
|
|
def stop(self, **kwargs):
|
|
self.cmd("killall iperf")
|
|
self.cmd("killall ping")
|
|
self.cmd("killall arping")
|
|
|
|
def describe(self):
|
|
print "**********"
|
|
print self.name
|
|
print "default interface: %s\t%s\t%s" % (
|
|
self.defaultIntf().name,
|
|
self.defaultIntf().IP(),
|
|
self.defaultIntf().MAC()
|
|
)
|
|
print "**********"
|
|
|
|
def getInfiniteCmdBg(self, cmd, logfile="/dev/null", delay=1):
|
|
return "(while [ -e {} ]; " \
|
|
"do {}; " \
|
|
"sleep {}; " \
|
|
"done;) > {} 2>&1 &".format(self.exectoken, cmd, delay, logfile)
|
|
|
|
|
|
def generateNetcfg(onosIp, net, args):
|
|
netcfg = OrderedDict()
|
|
|
|
netcfg['hosts'] = {}
|
|
netcfg['devices'] = {}
|
|
netcfg['links'] = {}
|
|
|
|
if args.full_netcfg:
|
|
# Device configs
|
|
for sw in net.switches:
|
|
srcIp = sw.getSourceIp(onosIp)
|
|
netcfg['devices'][sw.onosDeviceId] = sw.getDeviceConfig(srcIp)
|
|
|
|
hostLocations = {}
|
|
for link in net.links:
|
|
switchPort = link.intf1.name.split('-')
|
|
sw1Name = switchPort[0] # s11
|
|
port1Name = switchPort[1] # eth0
|
|
port1 = port1Name[3:]
|
|
switchPort = link.intf2.name.split('-')
|
|
sw2Name = switchPort[0]
|
|
port2Name = switchPort[1]
|
|
port2 = port2Name[3:]
|
|
sw1 = net[sw1Name]
|
|
sw2 = net[sw2Name]
|
|
if isinstance(sw1, Host):
|
|
# record host location and ignore it
|
|
# e.g. {'h1': 'device:bmv2:11'}
|
|
hostLocations[sw1.name] = '%s/%s' % (sw2.onosDeviceId, port2)
|
|
continue
|
|
|
|
if isinstance(sw2, Host):
|
|
# record host location and ignore it
|
|
# e.g. {'h1': 'device:bmv2:11'}
|
|
hostLocations[sw2.name] = '%s/%s' % (sw1.onosDeviceId, port1)
|
|
continue
|
|
|
|
if args.full_netcfg:
|
|
# Link configs
|
|
for linkId in ('%s/%s-%s/%s' % (sw1.onosDeviceId, port1,
|
|
sw2.onosDeviceId, port2),
|
|
'%s/%s-%s/%s' % (sw2.onosDeviceId, port2,
|
|
sw1.onosDeviceId, port1)):
|
|
netcfg['links'][linkId] = {
|
|
'basic': {
|
|
'type': 'DIRECT',
|
|
'bandwidth': DEFAULT_SW_BW
|
|
}
|
|
}
|
|
|
|
# Host configs
|
|
longitude = BASE_LONGITUDE
|
|
for host in net.hosts:
|
|
longitude = longitude + BASE_SHIFT
|
|
hostDefaultIntf = host.defaultIntf()
|
|
hostMac = host.MAC(hostDefaultIntf)
|
|
hostIp = host.IP(hostDefaultIntf)
|
|
hostId = '%s/%d' % (hostMac, VLAN_NONE)
|
|
location = hostLocations[host.name]
|
|
|
|
# use host Id to generate host location
|
|
hostConfig = {
|
|
'basic': {
|
|
'locations': [location],
|
|
'ips': [hostIp],
|
|
'name': host.name,
|
|
'latitude': HOST_BASE_LATITUDE,
|
|
'longitude': longitude
|
|
}
|
|
}
|
|
netcfg['hosts'][hostId] = hostConfig
|
|
|
|
if args.full_netcfg:
|
|
netcfg["apps"] = {
|
|
"org.onosproject.core": {
|
|
"core": {
|
|
"linkDiscoveryMode": "STRICT"
|
|
}
|
|
}
|
|
}
|
|
|
|
print "Writing network config to %s" % TEMP_NETCFG_FILE
|
|
with open(TEMP_NETCFG_FILE, 'w') as tempFile:
|
|
json.dump(netcfg, tempFile, indent=4)
|
|
|
|
|
|
def main(args):
|
|
if not args.onos_ip:
|
|
controller = ONOSCluster('c0', 3)
|
|
onosIp = controller.nodes()[0].IP()
|
|
else:
|
|
controller = RemoteController('c0', ip=args.onos_ip)
|
|
onosIp = args.onos_ip
|
|
|
|
topo = ClosTopo(args)
|
|
|
|
net = Mininet(topo=topo, build=False, controller=[controller])
|
|
|
|
net.build()
|
|
net.start()
|
|
|
|
print "Network started"
|
|
|
|
# Always generate background pings.
|
|
sleep(3)
|
|
for (h1, h2) in combinations(net.hosts, 2):
|
|
h1.startPingBg(h2)
|
|
h2.startPingBg(h1)
|
|
|
|
print "Background ping started"
|
|
|
|
for h in net.hosts:
|
|
h.startIperfServer()
|
|
|
|
print "Iperf servers started"
|
|
|
|
if args.bg_traffic:
|
|
sleep(4)
|
|
print "Starting iperf clients..."
|
|
net.hosts[0].startIperfClient(net.hosts[-1], flowBw="400k",
|
|
numFlows=50, duration=10)
|
|
|
|
generateNetcfg(onosIp, net, args)
|
|
|
|
if args.netcfg_sleep > 0:
|
|
print "Waiting %d seconds before pushing config to ONOS..." \
|
|
% args.netcfg_sleep
|
|
sleep(args.netcfg_sleep)
|
|
|
|
print "Pushing config to ONOS..."
|
|
call(("%s/onos-netcfg" % RUN_PACK_PATH, onosIp, TEMP_NETCFG_FILE))
|
|
|
|
if not args.onos_ip:
|
|
ONOSCLI(net)
|
|
else:
|
|
CLI(net)
|
|
|
|
net.stop()
|
|
call(("rm", "-f", TEMP_NETCFG_FILE))
|
|
|
|
|
|
if __name__ == '__main__':
|
|
parser = argparse.ArgumentParser(
|
|
description='BMv2 mininet demo script (2-stage Clos topology)')
|
|
parser.add_argument('--onos-ip', help='ONOS-BMv2 controller IP address',
|
|
type=str, action="store", required=False)
|
|
parser.add_argument('--size', help='Number of leaf/spine switches',
|
|
type=int, action="store", required=False, default=2)
|
|
parser.add_argument('--with-imbalanced-striping',
|
|
help='Topology with imbalanced striping',
|
|
type=bool, action="store", required=False,
|
|
default=False)
|
|
parser.add_argument('--pipeconf-id', help='Pipeconf ID for switches',
|
|
type=str, action="store", required=False, default='')
|
|
parser.add_argument('--netcfg-sleep',
|
|
help='Seconds to wait before pushing config to ONOS',
|
|
type=int, action="store", required=False, default=5)
|
|
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')
|
|
main(parser.parse_args())
|