From 019bff8282f4783ec7799c354c508392f8cebdd4 Mon Sep 17 00:00:00 2001 From: Bob Lantz Date: Fri, 26 Feb 2010 17:16:07 -0800 Subject: [PATCH] Made it possible to create a Mininet() without using a Topo object. This increases flexibility by allowing a topology to be manually created in Python, or specified using another format, without having to create a Topo object first. However, Topos are useful, and are still the default topology object! --- bin/mn | 0 mininet/net.py | 232 ++++++++++++++++++++++++------------------------ mininet/node.py | 46 +++++----- 3 files changed, 136 insertions(+), 142 deletions(-) mode change 100644 => 100755 bin/mn diff --git a/bin/mn b/bin/mn old mode 100644 new mode 100755 diff --git a/mininet/net.py b/mininet/net.py index 79ba3a3..78bbb21 100755 --- a/mininet/net.py +++ b/mininet/net.py @@ -84,7 +84,7 @@ from mininet.cli import CLI from mininet.log import info, error from mininet.node import KernelSwitch, OVSKernelSwitch from mininet.util import quietRun, fixLimits -from mininet.util import makeIntfPair, moveIntf +from mininet.util import makeIntfPair, moveIntf, macColonHex from mininet.xterm import cleanUpScreens, makeXterms DATAPATHS = [ 'kernel' ] #[ 'user', 'kernel' ] @@ -110,92 +110,103 @@ class Mininet( object ): inNamespace=False, autoSetMacs=False, autoStaticArp=False ): """Create Mininet object. - topo: Topo object + topo: Topo (topology) object or None switch: Switch class host: Host class controller: Controller class cparams: ControllerParams object - now: build now? + now: build now from topo? xterms: if build now, spawn xterms? cleanup: if build now, cleanup before creating? inNamespace: spawn switches and controller in net namespaces? autoSetMacs: set MAC addrs to DPIDs? autoStaticArp: set all-pairs static MAC addrs?""" - self.topo = topo self.switch = switch self.host = host self.controller = controller self.cparams = cparams - self.nodes = {} # dpid to Node{ Host, Switch } objects - self.controllers = {} # controller name to Controller objects - self.dps = 0 # number of created kernel datapaths + self.topo = topo self.inNamespace = inNamespace self.xterms = xterms self.cleanup = cleanup self.autoSetMacs = autoSetMacs self.autoStaticArp = autoStaticArp + self.hosts = [] + self.switches = [] + self.controllers = [] + self.nameToNode = {} # name to Node (Host/Switch) objects + self.idToNode = {} # dpid to Node (Host/Switch) objects + self.dps = 0 # number of created kernel datapaths self.terms = [] # list of spawned xterm processes - if build: - self.build() + if topo and build: + self.buildFromTopo( self.topo ) - def _addHost( self, dpid ): + def addHost( self, name, defaultMac=None, defaultIp=None ): """Add host. - dpid: DPID of host to add""" - host = self.host( 'h' + self.topo.name( dpid ) ) + name: name of host to add + defaultMac: default MAC address for intf 0 + defaultIp: default IP address for intf 0 + returns: added host""" + host = self.host( name ) # for now, assume one interface per host. - host.intfs.append( 'h' + self.topo.name( dpid ) + '-eth0' ) - self.nodes[ dpid ] = host - #info( '%s ' % host.name ) + host.intfs.append( name + '-eth0' ) + self.hosts.append( host ) + self.nameToNode[ name ] = host + # May wish to add this to actual object + if defaultMac: + host.defaultMac = defaultMac + if defaultIp: + host.defaultIP = defaultIp + return host - def _addSwitch( self, dpid ): + def addSwitch( self, name, defaultMac=None ): """Add switch. - dpid: DPID of switch to add""" - sw = None - swDpid = None - if self.autoSetMacs: - swDpid = dpid + name: name of switch to add + defaultMac: default MAC address for kernel/OVS switch intf 0 + returns: added switch""" if self.switch is KernelSwitch or self.switch is OVSKernelSwitch: - sw = self.switch( 's_' + self.topo.name( dpid ), dp = self.dps, - dpid = swDpid ) + sw = self.switch( name, dp=self.dps, defaultMac=defaultMac ) self.dps += 1 else: - sw = self.switch( 's_' + self.topo.name( dpid ) ) - self.nodes[ dpid ] = sw + sw = self.switch( name ) + self.switches.append( sw ) + self.nameToNode[ name ] = sw + return sw - def _addLink( self, src, dst ): + def addLink( self, src, srcPort, dst, dstPort ): """Add link. - src: source DPID - dst: destination DPID""" - srcPort, dstPort = self.topo.port( src, dst ) - srcNode = self.nodes[ src ] - dstNode = self.nodes[ dst ] - srcIntf = srcNode.intfName( srcPort ) - dstIntf = dstNode.intfName( dstPort ) + src: source Node + srcPort: source port + dst: destination Node + dstPort: destination port""" + srcIntf = src.intfName( srcPort ) + dstIntf = dst.intfName( dstPort ) makeIntfPair( srcIntf, dstIntf ) - srcNode.intfs.append( srcIntf ) - dstNode.intfs.append( dstIntf ) - srcNode.ports[ srcPort ] = srcIntf - dstNode.ports[ dstPort ] = dstIntf + src.intfs.append( srcIntf ) + dst.intfs.append( dstIntf ) + src.ports[ srcPort ] = srcIntf + dst.ports[ dstPort ] = dstIntf #info( '\n' ) #info( 'added intf %s to src node %x\n' % ( srcIntf, src ) ) #info( 'added intf %s to dst node %x\n' % ( dstIntf, dst ) ) - if srcNode.inNamespace: + if src.inNamespace: #info( 'moving src w/inNamespace set\n' ) - moveIntf( srcIntf, srcNode ) - if dstNode.inNamespace: + moveIntf( srcIntf, src ) + if dst.inNamespace: #info( 'moving dst w/inNamespace set\n' ) - moveIntf( dstIntf, dstNode ) - srcNode.connection[ srcIntf ] = ( dstNode, dstIntf ) - dstNode.connection[ dstIntf ] = ( srcNode, srcIntf ) + moveIntf( dstIntf, dst ) + src.connection[ srcIntf ] = ( dst, dstIntf ) + dst.connection[ dstIntf ] = ( src, srcIntf ) - def _addController( self, controller ): + def addController( self, controller ): """Add controller. controller: Controller class""" controller = self.controller( 'c0', self.inNamespace ) if controller: # allow controller-less setups - self.controllers[ 'c0' ] = controller + self.controllers.append( controller ) + self.nameToNode[ 'c0' ] = controller # Control network support: # @@ -216,7 +227,7 @@ class Mininet( object ): # # 4. Even if we dispense with this in general, it could still be # useful for people who wish to simulate a separate control - # network ( since real networks may need one! ) + # network (since real networks may need one!) def _configureControlNetwork( self ): "Configure control network." @@ -229,12 +240,11 @@ class Mininet( object ): """ # params were: controller, switches, ips - controller = self.controllers[ 'c0' ] + controller = self.controllers[ 0 ] info( '%s <-> ' % controller.name ) - for switchDpid in self.topo.switches(): - switch = self.nodes[ switchDpid ] + for switch in self.switches: info( '%s ' % switch.name ) - sip = self.topo.ip( switchDpid )#ips.next() + sip = switch.defaultIP sintf = switch.intfs[ 0 ] node, cintf = switch.connection[ sintf ] if node != controller: @@ -253,8 +263,7 @@ class Mininet( object ): info( '*** Waiting for %s to come up\n', controller.intfs[ 0 ] ) sleep( 1 ) - for switchDpid in self.topo.switches(): - switch = self.nodes[ switchDpid ] + for switch in self.switches: while not switch.intfIsUp( switch.intfs[ 0 ] ): info( '*** Waiting for %s to come up\n' % switch.intfs[ 0 ] ) @@ -267,10 +276,9 @@ class Mininet( object ): def _configHosts( self ): "Configure a set of hosts." # params were: hosts, ips - for hostDpid in self.topo.hosts(): - host = self.nodes[ hostDpid ] + for host in self.hosts: hintf = host.intfs[ 0 ] - host.setIP( hintf, self.topo.ip( hostDpid ), + host.setIP( hintf, host.defaultIP, '/' + str( self.cparams.subnetSize ) ) host.setDefaultRoute( hintf ) # You're low priority, dude! @@ -278,28 +286,37 @@ class Mininet( object ): info( '%s ', host.name ) info( '\n' ) - def build( self ): - """Build mininet. + def buildFromTopo( self, topo ): + """Build mininet from a topology object At the end of this function, everything should be connected and up.""" if self.cleanup: pass # cleanup # validate topo? info( '*** Adding controller\n' ) - self._addController( self.controller ) + self.addController( self.controller ) info( '*** Creating network\n' ) info( '*** Adding hosts:\n' ) - for host in sorted( self.topo.hosts() ): - self._addHost( host ) - info( '0x%x ' % host ) + for hostId in sorted( topo.hosts() ): + name = 'h' + topo.name( hostId ) + mac = macColonHex( hostId ) if self.setMacs else None + ip = topo.ip( hostId ) + host = self.addHost( name, defaultIp=ip, defaultMac=mac ) + self.idToNode[ hostId ] = host + info( name ) info( '\n*** Adding switches:\n' ) - for switch in sorted( self.topo.switches() ): - self._addSwitch( switch ) - info( '0x%x ' % switch ) + for switchId in sorted( topo.switches() ): + name = 's' + topo.name( switchId ) + mac = macColonHex( switchId) if self.setMacs else None + switch = self.addSwitch( name, defaultMac=mac ) + self.idToNode[ switchId ] = switch + info( name ) info( '\n*** Adding edges:\n' ) - for src, dst in sorted( self.topo.edges() ): - self._addLink( src, dst ) - info( '(0x%x, 0x%x) ' % ( src, dst ) ) + for srcId, dstId in sorted( topo.edges() ): + src, dst = self.idToNode[ srcId ], self.idToNode[ dstId ] + srcPort, dstPort = topo.port( srcId, dstId ) + self.addLink( src, srcPort, dst, dstPort ) + info( '(%s, %s) ' % ( src.name, dst.name ) ) info( '\n' ) if self.inNamespace: @@ -316,21 +333,13 @@ class Mininet( object ): if self.autoStaticArp: self.staticArp() - def switchNodes( self ): - "Return switch nodes." - return [ self.nodes[ dpid ] for dpid in self.topo.switches() ] - - def hostNodes( self ): - "Return host nodes." - return [ self.nodes[ dpid ] for dpid in self.topo.hosts() ] - def startXterms( self ): - "Start an xterm for each node in the topo." + "Start an xterm for each node." info( "*** Running xterms on %s\n" % os.environ[ 'DISPLAY' ] ) cleanUpScreens() - self.terms += makeXterms( self.controllers.values(), 'controller' ) - self.terms += makeXterms( self.switchNodes(), 'switch' ) - self.terms += makeXterms( self.hostNodes(), 'host' ) + self.terms += makeXterms( self.controllers, 'controller' ) + self.terms += makeXterms( self.switches, 'switch' ) + self.terms += makeXterms( self.hosts, 'host' ) def stopXterms( self ): "Kill each xterm." @@ -342,28 +351,24 @@ class Mininet( object ): def setMacs( self ): """Set MAC addrs to correspond to datapath IDs on hosts. Assume that the host only has one interface.""" - for dpid in self.topo.hosts(): - hostNode = self.nodes[ dpid ] - hostNode.setMAC( hostNode.intfs[ 0 ], dpid ) + for host in self.hosts: + host.setMAC( host.intfs[ 0 ], host.defaultMac ) def staticArp( self ): "Add all-pairs ARP entries to remove the need to handle broadcast." - for src in self.topo.hosts(): - srcNode = self.nodes[ src ] - for dst in self.topo.hosts(): + for src in self.hosts: + for dst in self.hosts: if src != dst: - srcNode.setARP( dst, dst ) + src.setARP( ip=dst.IP(), mac=dst.defaultMac ) def start( self ): "Start controller and switches" info( '*** Starting controller\n' ) - for cnode in self.controllers.values(): - cnode.start() - info( '*** Starting %s switches\n' % len( self.topo.switches() ) ) - for switchDpid in self.topo.switches(): - switch = self.nodes[ switchDpid ] - #info( 'switch = %s' % switch ) - info( '0x%x ' % switchDpid ) + for controller in self.controllers: + controller.start() + info( '*** Starting %s switches\n' % len( self.switches ) ) + for switch in self.switches: + info( switch.name ) switch.start( self.controllers ) info( '\n' ) @@ -372,21 +377,19 @@ class Mininet( object ): if self.terms: info( '*** Stopping %i terms\n' % len( self.terms ) ) self.stopXterms() - info( '*** Stopping %i hosts\n' % len( self.topo.hosts() ) ) - for hostDpid in self.topo.hosts(): - host = self.nodes[ hostDpid ] + info( '*** Stopping %i hosts\n' % len( self.hosts ) ) + for host in self.hosts: info( '%s ' % host.name ) host.terminate() info( '\n' ) - info( '*** Stopping %i switches\n' % len( self.topo.switches() ) ) - for switchDpid in self.topo.switches(): - switch = self.nodes[ switchDpid ] - info( '%s' % switch.name ) + info( '*** Stopping %i switches\n' % len( self.switches ) ) + for switch in self.switches: + info( '%s ' % switch.name ) switch.stop() info( '\n' ) - info( '*** Stopping controller\n' ) - for cnode in self.controllers.values(): - cnode.stop() + info( '*** Stopping %i controllers\n' % len( self.controllers ) ) + for controller in self.controllers: + controller.stop() info( '*** Test complete\n' ) def run( self, test, **params ): @@ -411,7 +414,7 @@ class Mininet( object ): def ping( self, hosts=None ): """Ping between all specified hosts. - hosts: list of host DPIDs + hosts: list of hosts returns: ploss packet loss percentage""" #self.start() # check if running - only then, start? @@ -419,13 +422,11 @@ class Mininet( object ): lost = 0 ploss = None if not hosts: - hosts = self.topo.hosts() - info( '*** Ping: testing ping reachability\n' ) - for nodeDpid in hosts: - node = self.nodes[ nodeDpid ] + hosts = self.hosts + info( '*** Ping: testing ping reachability\n' ) + for node in hosts: info( '%s -> ' % node.name ) - for destDpid in hosts: - dest = self.nodes[ destDpid ] + for dest in hosts: if node != dest: result = node.cmd( 'ping -c1 ' + dest.IP() ) sent, received = self._parsePing( result ) @@ -451,8 +452,7 @@ class Mininet( object ): def pingPair( self ): """Ping between first two hosts, useful for testing. returns: ploss packet loss percentage""" - hostsSorted = sorted( self.topo.hosts() ) - hosts = [ hostsSorted[ 0 ], hostsSorted[ 1 ] ] + hosts = [ self.hosts[ 0 ], self.hosts[ 1 ] ] return self.ping( hosts=hosts ) @staticmethod @@ -470,17 +470,15 @@ class Mininet( object ): def iperf( self, hosts=None, l4Type='TCP', udpBw='10M', verbose=False ): """Run iperf between two hosts. - hosts: list of host DPIDs; if None, uses opposite hosts + hosts: list of hosts; if None, uses opposite hosts l4Type: string, one of [ TCP, UDP ] verbose: verbose printing returns: results two-element array of server and client speeds""" if not hosts: - hostsSorted = sorted( self.topo.hosts() ) - hosts = [ hostsSorted[ 0 ], hostsSorted[ -1 ] ] + hosts = [ self.hosts[ 0 ], self.hosts[ -1 ] ] else: assert len( hosts ) == 2 - host0 = self.nodes[ hosts[ 0 ] ] - host1 = self.nodes[ hosts[ 1 ] ] + host0, host1 = hosts info( '*** Iperf: testing ' + l4Type + ' bandwidth between ' ) info( "%s and %s\n" % ( host0.name, host1.name ) ) host0.cmd( 'killall -9 iperf' ) diff --git a/mininet/node.py b/mininet/node.py index 1a33c46..e867882 100644 --- a/mininet/node.py +++ b/mininet/node.py @@ -41,7 +41,7 @@ import signal import select from mininet.log import info, error -from mininet.util import quietRun, macColonHex, ipStr +from mininet.util import quietRun class Node( object ): @@ -191,19 +191,16 @@ class Node( object ): def setMAC( self, intf, mac ): """Set the MAC address for an interface. - mac: MAC address as unsigned int""" - macStr = macColonHex( mac ) + mac: MAC address as string""" result = self.cmd( [ 'ifconfig', intf, 'down' ] ) - result += self.cmd( [ 'ifconfig', intf, 'hw', 'ether', macStr ] ) + result += self.cmd( [ 'ifconfig', intf, 'hw', 'ether', mac ] ) result += self.cmd( [ 'ifconfig', intf, 'up' ] ) return result def setARP( self, ip, mac ): """Add an ARP entry. - ip: IP address as unsigned int - mac: MAC address as unsigned int""" - ip = ipStr( ip ) - mac = macColonHex( mac ) + ip: IP address as string + mac: MAC address as string""" result = self.cmd( [ 'arp', '-s', ip, mac ] ) return result @@ -286,9 +283,7 @@ class UserSwitch( Switch ): """Start OpenFlow reference user datapath. Log to /tmp/sN-{ofd,ofp}.log. controllers: dict of controller names to objects""" - if 'c0' not in controllers: - raise Exception( 'User datapath start() requires controller c0' ) - controller = controllers[ 'c0' ] + controller = controllers[ 0 ] ofdlog = '/tmp/' + self.name + '-ofd.log' ofplog = '/tmp/' + self.name + '-ofp.log' self.cmd( 'ifconfig lo up' ) @@ -311,14 +306,14 @@ class KernelSwitch( Switch ): """Kernel-space switch. Currently only works in the root namespace.""" - def __init__( self, name, dp=None, dpid=None ): + def __init__( self, name, dp=None, defaultMac=None ): """Init. name: dp: netlink id (0, 1, 2, ...) - dpid: datapath ID as unsigned int; random value if None""" + defaultMac: default MAC as string; random value if None""" Switch.__init__( self, name, inNamespace=False ) self.dp = dp - self.dpid = dpid + self.defaultMac = defaultMac def start( self, controllers ): "Start up reference kernel datapath." @@ -328,10 +323,9 @@ class KernelSwitch( Switch ): # then create a new one monitoring the given interfaces quietRun( 'dpctl deldp nl:%i' % self.dp ) self.cmdPrint( 'dpctl adddp nl:%i' % self.dp ) - if self.dpid: + if self.defaultMac: intf = 'of%i' % self.dp - macStr = macColonHex( self.dpid ) - self.cmd( [ 'ifconfig', intf, 'hw', 'ether', macStr ] ) + self.cmd( [ 'ifconfig', intf, 'hw', 'ether', self.defaultMac ] ) if len( self.ports ) != max( self.ports.keys() ) + 1: raise Exception( 'only contiguous, zero-indexed port ranges' @@ -340,9 +334,10 @@ class KernelSwitch( Switch ): self.cmdPrint( 'dpctl addif nl:' + str( self.dp ) + ' ' + ' '.join( intfs ) ) # Run protocol daemon + controller = controllers[ 0 ] self.cmdPrint( 'ofprotocol nl:' + str( self.dp ) + ' tcp:' + - controllers[ 'c0' ].IP() + ':' + - str( controllers[ 'c0' ].port ) + + controller.IP() + ':' + + str( controller.port ) + ' --fail=closed 1> ' + ofplog + ' 2>' + ofplog + ' &' ) self.execed = False @@ -363,14 +358,14 @@ class OVSKernelSwitch( Switch ): """Open VSwitch kernel-space switch. Currently only works in the root namespace.""" - def __init__( self, name, dp=None, dpid=None ): + def __init__( self, name, dp=None, defaultMac=None ): """Init. name: dp: netlink id (0, 1, 2, ...) dpid: datapath ID as unsigned int; random value if None""" Switch.__init__( self, name, inNamespace=False ) self.dp = dp - self.dpid = dpid + self.defaultMac = defaultMac def start( self, controllers ): "Start up kernel datapath." @@ -380,10 +375,10 @@ class OVSKernelSwitch( Switch ): # then create a new one monitoring the given interfaces quietRun( 'ovs-dpctl del-dp dp%i' % self.dp ) self.cmdPrint( 'ovs-dpctl add-dp dp%i' % self.dp ) - if self.dpid: + if self.defaultMac: intf = 'dp' % self.dp - macStr = macColonHex( self.dpid ) - self.cmd( [ 'ifconfig', intf, 'hw', 'ether', macStr ] ) + mac = self.defaultMac + self.cmd( [ 'ifconfig', intf, 'hw', 'ether', mac ] ) if len( self.ports ) != max( self.ports.keys() ) + 1: raise Exception( 'only contiguous, zero-indexed port ranges' @@ -392,8 +387,9 @@ class OVSKernelSwitch( Switch ): self.cmdPrint( 'ovs-dpctl add-if dp' + str( self.dp ) + ' ' + ' '.join( intfs ) ) # Run protocol daemon + controller = controllers[ 0 ] self.cmdPrint( 'ovs-openflowd dp' + str( self.dp ) + ' tcp:' + - controllers[ 'c0' ].IP() + ':' + + controller.IP() + ':' + ' --fail=closed 1> ' + ofplog + ' 2>' + ofplog + ' &' ) self.execed = False