mirror of
https://github.com/opennetworkinglab/onos.git
synced 2025-10-15 17:31:31 +02:00
265 lines
8.4 KiB
Python
265 lines
8.4 KiB
Python
#!/usr/bin/python
|
|
import itertools
|
|
import os
|
|
import signal
|
|
import sys
|
|
from argparse import ArgumentParser
|
|
from subprocess import call
|
|
from threading import Thread
|
|
from time import sleep
|
|
|
|
import gratuitousArp
|
|
from mininet.cli import CLI
|
|
from mininet.examples.controlnet import MininetFacade
|
|
from mininet.link import TCLink
|
|
from mininet.log import info, output, error
|
|
from mininet.log import setLogLevel
|
|
from mininet.net import Mininet
|
|
from mininet.node import RemoteController, Node
|
|
|
|
ARP_PATH = gratuitousArp.__file__.replace('.pyc', '.py')
|
|
|
|
class ONOSMininet( Mininet ):
|
|
|
|
def __init__( self, controllers=[], gratuitousArp=True, build=True, *args, **kwargs ):
|
|
"""Create Mininet object for ONOS.
|
|
controllers: List of controller IP addresses
|
|
gratuitousArp: Send an ARP from each host to aid controller's host discovery"""
|
|
|
|
# delay building for a second
|
|
kwargs[ 'build' ] = False
|
|
|
|
Mininet.__init__(self, *args, **kwargs )
|
|
|
|
self.gratArp = gratuitousArp
|
|
|
|
# If a controller is not provided, use list of remote controller IPs instead.
|
|
if 'controller' not in kwargs or not kwargs['controller']:
|
|
info ( '*** Adding controllers\n' )
|
|
ctrl_count = 0
|
|
for controllerIP in controllers:
|
|
self.addController( 'c%d' % ctrl_count, RemoteController, ip=controllerIP )
|
|
info( ' c%d (%s)\n' % ( ctrl_count, controllerIP ) )
|
|
ctrl_count = ctrl_count + 1
|
|
|
|
if self.topo and build:
|
|
self.build()
|
|
|
|
def start( self ):
|
|
Mininet.start( self )
|
|
if self.gratArp:
|
|
self.waitConnected( timeout=5 )
|
|
sleep(2)
|
|
info ( '*** Sending a gratuitious ARP from each host\n' )
|
|
self.gratuitousArp()
|
|
|
|
def verifyHosts( self, hosts ):
|
|
for i in range( len( hosts ) ):
|
|
if isinstance( hosts[i], str):
|
|
if hosts[i] in self:
|
|
hosts[i] = self[ hosts[i] ]
|
|
else:
|
|
info( '*** ERROR: %s is not a host\n' % hosts[i] )
|
|
del hosts[i]
|
|
elif not isinstance( hosts[i], Node):
|
|
del hosts[i]
|
|
|
|
def gratuitousArp( self, hosts=[] ):
|
|
"Send an ARP from each host to aid controller's host discovery; fallback to ping if necessary"
|
|
if not hosts:
|
|
hosts = self.hosts
|
|
self.verifyHosts( hosts )
|
|
|
|
for host in hosts:
|
|
info( '%s ' % host.name )
|
|
info( host.cmd( ARP_PATH ) )
|
|
info ( '\n' )
|
|
|
|
def pingloop( self ):
|
|
"Loop forever pinging the full mesh of hosts"
|
|
setLogLevel( 'error' )
|
|
try:
|
|
while True:
|
|
self.ping()
|
|
finally:
|
|
setLogLevel( 'info' )
|
|
|
|
def bgIperf( self, hosts=[], seconds=10 ):
|
|
self.verifyHosts( hosts )
|
|
servers = [ host.popen("iperf -s") for host in hosts ]
|
|
|
|
clients = []
|
|
for s, d in itertools.combinations(hosts, 2):
|
|
info ( '%s <--> %s\n' % ( s.name, d.name ))
|
|
cmd = 'iperf -c %s -t %s -y csv' % (d.IP(), seconds)
|
|
p = s.popen(cmd)
|
|
p.s = s.name
|
|
p.d = d.name
|
|
clients.append(p)
|
|
|
|
def handler (_signum, _frame):
|
|
raise BackgroundException()
|
|
oldSignal = signal.getsignal(signal.SIGTSTP)
|
|
signal.signal(signal.SIGTSTP, handler)
|
|
|
|
def finish( verbose=True ):
|
|
for c in clients:
|
|
out, err = c.communicate()
|
|
if verbose:
|
|
if err:
|
|
info( err )
|
|
else:
|
|
bw = out.split( ',' )[8]
|
|
info( '%s <--> %s: %s\n' % ( c.s, c.d, formatBw(bw) ) )
|
|
for s in servers:
|
|
s.terminate()
|
|
|
|
try:
|
|
info ( 'Press ^Z to continue in background or ^C to abort\n')
|
|
progress( seconds )
|
|
finish()
|
|
except KeyboardInterrupt:
|
|
for c in clients:
|
|
c.terminate()
|
|
for s in servers:
|
|
s.terminate()
|
|
except BackgroundException:
|
|
info( '\n*** Continuing in background...\n' )
|
|
t = Thread( target=finish, args=[ False ] )
|
|
t.start()
|
|
finally:
|
|
#Disable custom background signal
|
|
signal.signal(signal.SIGTSTP, oldSignal)
|
|
|
|
def progress(t):
|
|
while t > 0:
|
|
sys.stdout.write( '.' )
|
|
t -= 1
|
|
sys.stdout.flush()
|
|
sleep(1)
|
|
print
|
|
|
|
def formatBw( bw ):
|
|
bw = float(bw)
|
|
if bw > 1000:
|
|
bw /= 1000
|
|
if bw > 1000:
|
|
bw /= 1000
|
|
if bw > 1000:
|
|
bw /= 1000
|
|
return '%.2f Gbps' % bw
|
|
return '%.2f Mbps' % bw
|
|
return '%.2f Kbps' % bw
|
|
return '%.2f bps' % bw
|
|
|
|
class BackgroundException( Exception ):
|
|
pass
|
|
|
|
|
|
def get_mn(mn):
|
|
if isinstance(mn, ONOSMininet):
|
|
return mn
|
|
elif isinstance(mn, MininetFacade):
|
|
# There's more Mininet objects instantiated (e.g. one for the control network in onos.py).
|
|
for net in mn.nets:
|
|
if isinstance(net, ONOSMininet):
|
|
return net
|
|
return None
|
|
|
|
|
|
def do_bgIperf( self, line ):
|
|
args = line.split()
|
|
if not args:
|
|
output( 'Provide a list of hosts.\n' )
|
|
|
|
#Try to parse the '-t' argument as the number of seconds
|
|
seconds = 10
|
|
for i, arg in enumerate(args):
|
|
if arg == '-t':
|
|
if i + 1 < len(args):
|
|
try:
|
|
seconds = int(args[i + 1])
|
|
except ValueError:
|
|
error( 'Could not parse number of seconds: %s', args[i+1] )
|
|
del(args[i+1])
|
|
del args[i]
|
|
|
|
hosts = []
|
|
err = False
|
|
for arg in args:
|
|
if arg not in self.mn:
|
|
err = True
|
|
error( "node '%s' not in network\n" % arg )
|
|
else:
|
|
hosts.append( self.mn[ arg ] )
|
|
mn = get_mn( self.mn )
|
|
if "bgIperf" in dir( mn ) and not err:
|
|
mn.bgIperf( hosts, seconds=seconds )
|
|
else:
|
|
output('Background Iperf is not supported.\n')
|
|
|
|
def do_gratuitousArp( self, line ):
|
|
args = line.split()
|
|
mn = get_mn(self.mn)
|
|
if "gratuitousArp" in dir( mn ):
|
|
mn.gratuitousArp( args )
|
|
else:
|
|
output( 'Gratuitous ARP is not supported.\n' )
|
|
|
|
CLI.do_bgIperf = do_bgIperf
|
|
CLI.do_gratuitousArp = do_gratuitousArp
|
|
|
|
def parse_args():
|
|
parser = ArgumentParser(description='ONOS Mininet')
|
|
parser.add_argument('--cluster-size', help='Starts an ONOS cluster with the given number of instances',
|
|
type=int, action='store', dest='clusterSize', required=False, default=0)
|
|
parser.add_argument('--netcfg', help='Relative path of the JSON file to be used with netcfg',
|
|
type=str, action='store', dest='netcfgJson', required=False, default='')
|
|
parser.add_argument('ipAddrs', metavar='IP', type=str, nargs='*',
|
|
help='List of controller IP addresses', default=[])
|
|
return parser.parse_args()
|
|
|
|
def run( topo, controllers=None, link=TCLink, autoSetMacs=True):
|
|
if not topo:
|
|
print 'Need to provide a topology'
|
|
exit(1)
|
|
|
|
args = parse_args()
|
|
|
|
if not controllers and len(args.ipAddrs) > 0:
|
|
controllers = args.ipAddrs
|
|
|
|
if not controllers and args.clusterSize < 1:
|
|
print 'Need to provide a list of controller IPs, or define a cluster size.'
|
|
exit( 1 )
|
|
|
|
setLogLevel( 'info' )
|
|
|
|
if args.clusterSize > 0:
|
|
if 'ONOS_ROOT' not in os.environ:
|
|
print "Environment var $ONOS_ROOT not set (needed to import onos.py)"
|
|
exit( 1 )
|
|
sys.path.append(os.environ["ONOS_ROOT"] + "/tools/dev/mininet")
|
|
from onos import ONOSCluster, ONOSOVSSwitch, ONOSCLI
|
|
controller = ONOSCluster('c0', args.clusterSize)
|
|
onosAddr = controller.nodes()[0].IP()
|
|
net = ONOSMininet( topo=topo, controller=controller, switch=ONOSOVSSwitch, link=link,
|
|
autoSetMacs=autoSetMacs )
|
|
cli = ONOSCLI
|
|
else:
|
|
onosAddr = controllers[0]
|
|
net = ONOSMininet(topo=topo, controllers=controllers, link=link, autoSetMacs=autoSetMacs)
|
|
cli = CLI
|
|
|
|
net.start()
|
|
|
|
if len(args.netcfgJson) > 0:
|
|
if not os.path.isfile(args.netcfgJson):
|
|
error('*** WARNING no such netcfg file: %s\n' % args.netcfgJson)
|
|
else:
|
|
info('*** Setting netcfg: %s\n' % args.netcfgJson)
|
|
call(("onos-netcfg", onosAddr, args.netcfgJson))
|
|
|
|
cli( net )
|
|
net.stop()
|