diff --git a/bin/mn b/bin/mn index 75166b76ab244436e85e7e825f4d5359cdbe519a..fbe1f05bbfb08888e5069eeb65ba754a9867f504 100755 --- a/bin/mn +++ b/bin/mn @@ -25,11 +25,13 @@ from mininet.cli import CLI from mininet.log import lg, LEVELS, info, debug, error from mininet.net import Mininet, MininetWithControlNet, VERSION from mininet.node import ( Host, CPULimitedHost, Controller, OVSController, - NOX, RemoteController, UserSwitch, OVSKernelSwitch, + NOX, RemoteController, DefaultController, + UserSwitch, OVSSwitch, OVSLegacyKernelSwitch, IVSSwitch ) +from mininet.nodelib import LinuxBridge from mininet.link import Link, TCLink from mininet.topo import SingleSwitchTopo, LinearTopo, SingleSwitchReversedTopo -from mininet.topolib import TreeTopo +from mininet.topolib import TreeTopo, TorusTopo from mininet.util import custom, customConstructor from mininet.util import buildTopo @@ -40,24 +42,29 @@ TOPOS = { 'minimal': lambda: SingleSwitchTopo( k=2 ), 'linear': LinearTopo, 'reversed': SingleSwitchReversedTopo, 'single': SingleSwitchTopo, - 'tree': TreeTopo } + 'tree': TreeTopo, + 'torus': TorusTopo } SWITCHDEF = 'ovsk' SWITCHES = { 'user': UserSwitch, - 'ovsk': OVSKernelSwitch, + 'ovs': OVSSwitch, + # Keep ovsk for compatibility with 2.0 + 'ovsk': OVSSwitch, 'ovsl': OVSLegacyKernelSwitch, - 'ivs': IVSSwitch } + 'ivs': IVSSwitch, + 'lxbr': LinuxBridge } HOSTDEF = 'proc' HOSTS = { 'proc': Host, 'rt': custom( CPULimitedHost, sched='rt' ), 'cfs': custom( CPULimitedHost, sched='cfs' ) } -CONTROLLERDEF = 'ovsc' +CONTROLLERDEF = 'default' CONTROLLERS = { 'ref': Controller, 'ovsc': OVSController, 'nox': NOX, 'remote': RemoteController, + 'default': DefaultController, 'none': lambda name: None } LINKDEF = 'default' @@ -261,12 +268,14 @@ class MininetRunner( object ): if test == 'none': pass elif test == 'all': + mn.waitConnected() mn.start() mn.ping() mn.iperf() elif test == 'cli': CLI( mn ) elif test != 'build': + mn.waitConnected() getattr( mn, test )() if self.options.post: diff --git a/examples/linearbandwidth.py b/examples/linearbandwidth.py index 3fd06c757db8730d10ccee8345ebd9de458f0ce9..dee5490cf28a34376a7cc93bb8a92be181badc36 100755 --- a/examples/linearbandwidth.py +++ b/examples/linearbandwidth.py @@ -24,7 +24,7 @@ """ from mininet.net import Mininet -from mininet.node import UserSwitch, OVSKernelSwitch +from mininet.node import UserSwitch, OVSKernelSwitch, Controller from mininet.topo import Topo from mininet.log import lg from mininet.util import irange @@ -76,7 +76,7 @@ def linearBandwidthTest( lengths ): print "*** testing", datapath, "datapath" Switch = switches[ datapath ] results[ datapath ] = [] - net = Mininet( topo=topo, switch=Switch ) + net = Mininet( topo=topo, switch=Switch, controller=Controller, waitConnected=True ) net.start() print "*** testing basic connectivity" for n in lengths: diff --git a/mininet/net.py b/mininet/net.py index 135807bd2258a7be5618caa105c223456eaca41a..4224b974dacef4b24a20cb77f99a5cc09f1e9685 100755 --- a/mininet/net.py +++ b/mininet/net.py @@ -90,12 +90,13 @@ import re import select import signal +import copy from time import sleep from itertools import chain, groupby from mininet.cli import CLI -from mininet.log import info, error, debug, output -from mininet.node import Host, OVSKernelSwitch, Controller +from mininet.log import info, error, debug, output, warn +from mininet.node import Host, OVSKernelSwitch, DefaultController, Controller from mininet.link import Link, Intf from mininet.util import quietRun, fixLimits, numCores, ensureRoot from mininet.util import macColonHex, ipStr, ipParse, netParse, ipAdd @@ -109,11 +110,11 @@ class Mininet( object ): "Network emulation with hosts spawned in network namespaces." def __init__( self, topo=None, switch=OVSKernelSwitch, host=Host, - controller=Controller, link=Link, intf=Intf, + controller=DefaultController, link=Link, intf=Intf, build=True, xterms=False, cleanup=False, ipBase='10.0.0.0/8', inNamespace=False, autoSetMacs=False, autoStaticArp=False, autoPinCpus=False, - listenPort=None ): + listenPort=None, waitConnected=False ): """Create Mininet object. topo: Topo (topology) object or None switch: default Switch class @@ -149,6 +150,7 @@ def __init__( self, topo=None, switch=OVSKernelSwitch, host=Host, self.numCores = numCores() self.nextCore = 0 # next core for pinning hosts to CPUs self.listenPort = listenPort + self.waitConn = waitConnected self.hosts = [] self.switches = [] @@ -166,6 +168,37 @@ def __init__( self, topo=None, switch=OVSKernelSwitch, host=Host, if topo and build: self.build() + + def waitConnected( self, timeout=None, delay=.5 ): + """wait for each switch to connect to a controller, + up to 5 seconds + timeout: time to wait, or None to wait indefinitely + delay: seconds to sleep per iteration + returns: True if all switches are connected""" + info( '*** Waiting for switches to connect\n' ) + time = 0 + remaining = list( self.switches ) + while True: + for switch in tuple( remaining ): + if switch.connected(): + info( '%s ' % switch ) + remaining.remove( switch ) + if not remaining: + info( '\n' ) + return True + if time > timeout and timeout is not None: + break + sleep( delay ) + time += delay + warn( 'Timed out after %d seconds\n' % time ) + for switch in remaining: + if not switch.connected(): + warn( 'Warning: %s is not connected to a controller\n' + % switch.name ) + else: + remaining.remove( switch ) + return not remaining + def addHost( self, name, cls=None, **params ): """Add host. name: name of host to add @@ -216,7 +249,7 @@ def addController( self, name='c0', controller=None, **params ): if not controller: controller = self.controller # Construct new controller if one is not given - if isinstance(name, Controller): + if isinstance( name, Controller ): controller_new = name # Pylint thinks controller is a str() # pylint: disable=E1103 @@ -225,7 +258,7 @@ def addController( self, name='c0', controller=None, **params ): else: controller_new = controller( name, **params ) # Add new controller to net - if controller_new: # allow controller-less setups + if controller_new: # allow controller-less setups self.controllers.append( controller_new ) self.nameToNode[ name ] = controller_new return controller_new @@ -327,7 +360,11 @@ def buildFromTopo( self, topo=None ): if type( classes ) is not list: classes = [ classes ] for i, cls in enumerate( classes ): - self.addController( 'c%d' % i, cls ) + # Allow Controller objects because nobody understands currying + if isinstance( cls, Controller ): + self.addController( cls ) + else: + self.addController( 'c%d' % i, cls ) info( '*** Adding hosts:\n' ) for hostName in topo.hosts(): @@ -408,9 +445,16 @@ def start( self ): info( switch.name + ' ') switch.start( self.controllers ) info( '\n' ) + if self.waitConn: + self.waitConnected() def stop( self ): "Stop the controller(s), switches and hosts" + info( '*** Stopping %i controllers\n' % len( self.controllers ) ) + for controller in self.controllers: + info( controller.name + ' ' ) + controller.stop() + info( '\n' ) if self.terms: info( '*** Stopping %i terms\n' % len( self.terms ) ) self.stopXterms() @@ -426,11 +470,6 @@ def stop( self ): for host in self.hosts: info( host.name + ' ' ) host.terminate() - info( '\n' ) - info( '*** Stopping %i controllers\n' % len( self.controllers ) ) - for controller in self.controllers: - info( controller.name + ' ' ) - controller.stop() info( '\n*** Done\n' ) def run( self, test, *args, **kwargs ): @@ -623,7 +662,7 @@ def _parseIperf( iperfOutput ): # XXX This should be cleaned up - def iperf( self, hosts=None, l4Type='TCP', udpBw='10M' ): + def iperf( self, hosts=None, l4Type='TCP', udpBw='10M', format=None ): """Run iperf between two hosts. hosts: list of hosts; if None, uses opposite hosts l4Type: string, one of [ TCP, UDP ] @@ -646,6 +685,8 @@ def iperf( self, hosts=None, l4Type='TCP', udpBw='10M' ): bwArgs = '-b ' + udpBw + ' ' elif l4Type != 'TCP': raise Exception( 'Unexpected l4 type: %s' % l4Type ) + if format: + iperfArgs += '-f %s ' %format server.sendCmd( iperfArgs + '-s', printPid=True ) servout = '' while server.lastPid is None: @@ -653,7 +694,7 @@ def iperf( self, hosts=None, l4Type='TCP', udpBw='10M' ): if l4Type == 'TCP': while 'Connected' not in client.cmd( 'sh -c "echo A | telnet -e A %s 5001"' % server.IP()): - output('waiting for iperf to start up...') + info( 'Waiting for iperf to start up...' ) sleep(.5) cliout = client.cmd( iperfArgs + '-t 5 -c ' + server.IP() + ' ' + bwArgs ) diff --git a/mininet/node.py b/mininet/node.py index 568d9863216904fcf5a2e8a45fc8cae52ecfdeb4..d9052fab7e6f1a8a5134044edc6f869e08e2a05a 100644 --- a/mininet/node.py +++ b/mininet/node.py @@ -1277,13 +1277,19 @@ def __repr__( self ): return '<%s %s: %s:%s pid=%s> ' % ( self.__class__.__name__, self.name, self.IP(), self.port, self.pid ) - + @classmethod + def isAvailable( self ): + return quietRun( 'which controller' ) class OVSController( Controller ): "Open vSwitch controller" def __init__( self, name, command='ovs-controller', **kwargs ): + if quietRun( 'which test-controller' ): + command = 'test-controller' Controller.__init__( self, name, command=command, **kwargs ) - + @classmethod + def isAvailable( self ): + return quietRun( 'which ovs-controller' ) or quietRun( 'which test-controller' ) class NOX( Controller ): "Controller to run a NOX application." @@ -1338,3 +1344,10 @@ def checkListening( self ): if 'Connected' not in listening: warn( "Unable to contact the remote controller" " at %s:%d\n" % ( self.ip, self.port ) ) + + +def DefaultController( name, order=[ Controller, OVSController ], **kwargs ): + "find any controller that is available and run it" + for controller in order: + if controller.isAvailable(): + return controller( name, **kwargs ) diff --git a/mininet/nodelib.py b/mininet/nodelib.py new file mode 100644 index 0000000000000000000000000000000000000000..2eb80465cd005c28c9a2aaa86aa920b2cc04c02a --- /dev/null +++ b/mininet/nodelib.py @@ -0,0 +1,51 @@ +""" +Node Library for Mininet + +This contains additional Node types which you may find to be useful +""" + +from mininet.net import Mininet +from mininet.topo import Topo +from mininet.node import Switch +from mininet.log import setLogLevel, info + + +class LinuxBridge( Switch ): + "Linux Bridge (with optional spanning tree)" + + nextPrio = 100 # next bridge priority for spanning tree + + def __init__( self, name, stp=False, prio=None, **kwargs ): + """stp: use spanning tree protocol? (default False) + prio: optional explicit bridge priority for STP""" + self.stp = stp + if prio: + self.prio = prio + else: + self.prio = LinuxBridge.nextPrio + LinuxBridge.nextPrio += 1 + Switch.__init__( self, name, **kwargs ) + + def connected( self ): + "Are we forwarding yet?" + if self.stp: + return 'forwarding' in self.cmd( 'brctl showstp', self ) + else: + return True + + def start( self, controllers ): + self.cmd( 'ifconfig', self, 'down' ) + self.cmd( 'brctl delbr', self ) + self.cmd( 'brctl addbr', self ) + if self.stp: + self.cmd( 'brctl setbridgeprio', self.prio ) + self.cmd( 'brctl stp', self, 'on' ) + for i in self.intfList(): + if self.name in i.name: + self.cmd( 'brctl addif', self, i ) + self.cmd( 'ifconfig', self, 'up' ) + + def stop( self ): + self.cmd( 'ifconfig', self, 'down' ) + self.cmd( 'brctl delbr', self ) + diff --git a/mininet/test/test_hifi.py b/mininet/test/test_hifi.py index 20ee03167b648f59f7ea94b8f37400ee0d375670..c888e29620a4edfd4aea6468feff43be11c44ba8 100755 --- a/mininet/test/test_hifi.py +++ b/mininet/test/test_hifi.py @@ -55,6 +55,9 @@ def assertWithinTolerance(self, measured, expected, tolerance_frac): """ self.assertGreaterEqual( float(measured), float(expected) * tolerance_frac ) + self.assertLessEqual( float( measured ), + float(expected) + (1-tolerance_frac) + * float( expected ) ) def testCPULimits( self ): "Verify topology creation with CPU limits set for both schedulers." @@ -68,19 +71,20 @@ def testCPULimits( self ): mn.start() results = mn.runCpuLimitTest( cpu=CPU_FRACTION ) mn.stop() - for cpu in results: - self.assertWithinTolerance( cpu, CPU_FRACTION, CPU_TOLERANCE ) + for pct in results: + #divide cpu by 100 to convert from percentage to fraction + self.assertWithinTolerance( pct/100, CPU_FRACTION, CPU_TOLERANCE ) def testLinkBandwidth( self ): "Verify that link bandwidths are accurate within a bound." - BW = 5 # Mbps + BW = .5 # Mbps BW_TOLERANCE = 0.8 # BW fraction below which test should fail # Verify ability to create limited-link topo first; lopts = { 'bw': BW, 'use_htb': True } # Also verify correctness of limit limitng within a bound. mn = Mininet( SingleSwitchOptionsTopo( n=N, lopts=lopts ), link=TCLink, switch=self.switchClass ) - bw_strs = mn.run( mn.iperf ) + bw_strs = mn.run( mn.iperf, format='m' ) for bw_str in bw_strs: bw = float( bw_str.split(' ')[0] ) self.assertWithinTolerance( bw, BW, BW_TOLERANCE ) @@ -91,7 +95,7 @@ def testLinkDelay( self ): DELAY_TOLERANCE = 0.8 # Delay fraction below which test should fail lopts = { 'delay': '%sms' % DELAY_MS, 'use_htb': True } mn = Mininet( SingleSwitchOptionsTopo( n=N, lopts=lopts ), - link=TCLink, switch=self.switchClass ) + link=TCLink, switch=self.switchClass, autoStaticArp=True ) ping_delays = mn.run( mn.pingFull ) test_outputs = ping_delays[0] # Ignore unused variables below @@ -102,9 +106,10 @@ def testLinkDelay( self ): # pylint: enable-msg=W0612 for rttval in [rttmin, rttavg, rttmax]: # Multiply delay by 4 to cover there & back on two links - self.assertWithinTolerance( rttval, DELAY_MS * 4.0, + self.assertWithinTolerance( rttval, DELAY_MS * 4.0, DELAY_TOLERANCE) + def testLinkLoss( self ): "Verify that we see packet drops with a high configured loss rate." LOSS_PERCENT = 99 diff --git a/mininet/test/test_nets.py b/mininet/test/test_nets.py index 159ba348c46831fe4e48f020f07bbd79d40d4744..330e9a88a6c69e336f95467bf99e2b9bb9b31e69 100755 --- a/mininet/test/test_nets.py +++ b/mininet/test/test_nets.py @@ -66,7 +66,7 @@ class testLinearCommon( object ): def testLinear5( self ): "Ping test on a 5-switch topology" - mn = Mininet( LinearTopo( k=5 ), self.switchClass, Host, Controller ) + mn = Mininet( LinearTopo( k=5 ), self.switchClass, Host, Controller, waitConnected=True ) dropped = mn.run( mn.ping ) self.assertEqual( dropped, 0 ) diff --git a/mininet/topo.py b/mininet/topo.py index f9c421fbc8f57dc72e0850ae72ebd9cab99ee41c..de5ba8a7a37d63cfd5795aec907f32cfb33420bf 100644 --- a/mininet/topo.py +++ b/mininet/topo.py @@ -48,18 +48,25 @@ def __getitem__( self, node ): class Topo(object): "Data center network representation for structured multi-trees." - def __init__(self, hopts=None, sopts=None, lopts=None): - """Topo object: + def __init__(self, *args, **params): + """Topo object. + Optional named parameters: hinfo: default host options sopts: default switch options - lopts: default link options""" + lopts: default link options + calls build()""" self.g = MultiGraph() self.node_info = {} self.link_info = {} # (src, dst) tuples hash to EdgeInfo objects - self.hopts = {} if hopts is None else hopts - self.sopts = {} if sopts is None else sopts - self.lopts = {} if lopts is None else lopts + self.hopts = params.pop( 'hopts', {} ) + self.sopts = params.pop( 'sopts', {} ) + self.lopts = params.pop( 'lopts', {} ) self.ports = {} # ports[src][dst] is port on src that connects to dst + self.build( *args, **params ) + + def build( self, *args, **params ): + "Override this method to build your topology." + pass def addNode(self, name, **opts): """Add Node to graph. diff --git a/mininet/topolib.py b/mininet/topolib.py index 63ba36deb3d51e5a246545e7bfe8782ed4a3848e..8e3b3a4b6a33caabebaba7ddcfde5e581ea9197e 100644 --- a/mininet/topolib.py +++ b/mininet/topolib.py @@ -34,3 +34,37 @@ def TreeNet( depth=1, fanout=2, **kwargs ): "Convenience function for creating tree networks." topo = TreeTopo( depth, fanout ) return Mininet( topo, **kwargs ) + + +class TorusTopo( Topo ): + """2-D Torus topology + WARNING: this topology has LOOPS and WILL NOT WORK + with the default controller or any Ethernet bridge + without STP turned on! It can be used with STP, e.g.: + # mn --topo torus,3,3 --switch lxbr,stp=1 --test pingall""" + def __init__( self, x, y, *args, **kwargs ): + Topo.__init__( self, *args, **kwargs ) + if x < 3 or y < 3: + raise Exception( 'Please use 3x3 or greater for compatibility ' + 'with 2.1' ) + hosts, switches, dpid = {}, {}, 0 + # Create and wire interior + for i in range( 0, x ): + for j in range( 0, y ): + loc = '%dx%d' % ( i + 1, j + 1 ) + # dpid cannot be zero for OVS + dpid = ( i + 1 ) * 256 + ( j + 1 ) + switch = switches[ i, j ] = self.addSwitch( 's' + loc, dpid='%016x' % dpid ) + host = hosts[ i, j ] = self.addHost( 'h' + loc ) + self.addLink( host, switch ) + # Connect switches + for i in range( 0, x ): + for j in range( 0, y ): + sw1 = switches[ i, j ] + sw2 = switches[ i, ( j + 1 ) % y ] + sw3 = switches[ ( i + 1 ) % x, j ] + self.addLink( sw1, sw2 ) + self.addLink( sw1, sw3 ) + + + diff --git a/util/vm/build.py b/util/vm/build.py index be809eced8ef4c2982e048ef8526a3cef16ba462..ad454f0ddfbd3df163202cd352ebad1f480a3abc 100755 --- a/util/vm/build.py +++ b/util/vm/build.py @@ -89,6 +89,12 @@ 'trusty64server': 'http://mirrors.kernel.org/ubuntu-releases/14.04/' 'ubuntu-14.04-server-amd64.iso', + 'utopic32server': + 'http://mirrors.kernel.org/ubuntu-releases/14.10/' + 'ubuntu-14.10-server-i386.iso', + 'utopic64server': + 'http://mirrors.kernel.org/ubuntu-releases/14.10/' + 'ubuntu-14.10-server-amd64.iso', }