Revision efc9a01c

View differences:

examples/scratchnet.py
6 6
but it exposes the configuration details and allows customization.
7 7
"""
8 8

  
9
from mininet.mininet import init, Node, createLink
9
import logging
10

  
11
from mininet.net import init
12
from mininet.node import Node
13
from mininet.util import createLink
14
from mininet.log import info
15

  
16
# print out info() messages, including cmdPrint
17
logging.LOGLEVELDEFAULT = logging.INFO
10 18

  
11 19
def scratchNet( cname='controller', cargs='ptcp:'):
12
   # Create Network
13
   controller = Node( 'c0', inNamespace=False )
14
   switch = Node( 's0', inNamespace=False )
15
   h0 = Node( 'h0' )
16
   h1 = Node( 'h1' )
17
   createLink( h0, switch )
18
   createLink( h1, switch )
19
   # Configure hosts
20
   h0.setIP( h0.intfs[ 0 ], '192.168.123.1', '/24' )
21
   h1.setIP( h1.intfs[ 0 ], '192.168.123.2', '/24' )
22
   # Start network using kernel datapath
23
   controller.cmdPrint( cname + ' ' + cargs + '&' )
24
   switch.cmdPrint( 'dpctl deldp nl:0' )
25
   switch.cmdPrint( 'dpctl adddp nl:0' )
26
   for intf in switch.intfs:
20
    # Create Network
21
    print "*** creating Nodes"
22
    controller = Node( 'c0', inNamespace=False )
23
    switch = Node( 's0', inNamespace=False )
24
    h0 = Node( 'h0' )
25
    h1 = Node( 'h1' )
26
    print "*** creating links"
27
    createLink( node1=h0, port1=0, node2=switch, port2=0 )
28
    createLink( node1=h1, port1=0, node2=switch, port2=1 )
29
    # Configure hosts
30
    print "*** configuring hosts"
31
    h0.setIP( h0.intfs[ 0 ], '192.168.123.1', '/24' )
32
    h1.setIP( h1.intfs[ 0 ], '192.168.123.2', '/24' )
33
    print h0
34
    print h1
35
    # Start network using kernel datapath
36
    controller.cmdPrint( cname + ' ' + cargs + '&' )
37
    switch.cmdPrint( 'dpctl deldp nl:0' )
38
    switch.cmdPrint( 'dpctl adddp nl:0' )
39
    for intf in switch.intfs.values():
27 40
      switch.cmdPrint( 'dpctl addif nl:0 ' + intf )
28
   switch.cmdPrint( 'ofprotocol nl:0 tcp:localhost &')
29
   # Run test
30
   h0.cmdPrint( 'ping -c1 ' + h1.IP() )
31
   # Stop network
32
   controller.cmdPrint( 'kill %' + cname)
33
   switch.cmdPrint( 'dpctl deldp nl:0' )
34
   switch.cmdPrint( 'kill %ofprotocol' )
35
   switch.stop()
36
   controller.stop()
41
    switch.cmdPrint( 'ofprotocol nl:0 tcp:localhost &')
42
    # Run test
43
    print h0.cmd( 'ping -c1 ' + h1.IP() )
44
    # Stop network
45
    controller.cmdPrint( 'kill %' + cname)
46
    switch.cmdPrint( 'dpctl deldp nl:0' )
47
    switch.cmdPrint( 'kill %ofprotocol' )
48
    switch.stop()
49
    controller.stop()
37 50
   
38 51
if __name__ == '__main__':
39
   init()   
40
   scratchNet()
52
    info( '*** Scratch network demo\n' )
53
    init()   
54
    scratchNet()
mininet/net.py
81 81
from time import sleep
82 82

  
83 83
from mininet.cli import CLI
84
from mininet.log import info, error
84
from mininet.log import info, error, debug
85 85
from mininet.node import KernelSwitch, OVSKernelSwitch
86 86
from mininet.util import quietRun, fixLimits
87
from mininet.util import makeIntfPair, moveIntf, macColonHex
87
from mininet.util import createLink, macColonHex
88 88
from mininet.xterm import cleanUpScreens, makeXterms
89 89

  
90 90
DATAPATHS = [ 'kernel' ] #[ 'user', 'kernel' ]
......
150 150
           defaultIp: default IP address for intf 0
151 151
           returns: added host"""
152 152
        host = self.host( name )
153
        # for now, assume one interface per host.
154
        host.intfs.append( name + '-eth0' )
155 153
        self.hosts.append( host )
156 154
        self.nameToNode[ name ] = host
157 155
        # May wish to add this to actual object
......
175 173
        self.nameToNode[ name ] = sw
176 174
        return sw
177 175

  
178
    def addLink( self, src, srcPort, dst, dstPort ):
179
        """Add link.
180
           src: source Node
181
           srcPort: source port
182
           dst: destination Node
183
           dstPort: destination port"""
184
        srcIntf = src.intfName( srcPort )
185
        dstIntf = dst.intfName( dstPort )
186
        makeIntfPair( srcIntf, dstIntf )
187
        src.intfs.append( srcIntf )
188
        dst.intfs.append( dstIntf )
189
        src.ports[ srcPort ] = srcIntf
190
        dst.ports[ dstPort ] = dstIntf
191
        #info( '\n' )
192
        #info( 'added intf %s to src node %x\n' % ( srcIntf, src ) )
193
        #info( 'added intf %s to dst node %x\n' % ( dstIntf, dst ) )
194
        if src.inNamespace:
195
            #info( 'moving src w/inNamespace set\n' )
196
            moveIntf( srcIntf, src )
197
        if dst.inNamespace:
198
            #info( 'moving dst w/inNamespace set\n' )
199
            moveIntf( dstIntf, dst )
200
        src.connection[ srcIntf ] = ( dst, dstIntf )
201
        dst.connection[ dstIntf ] = ( src, srcIntf )
202

  
203 176
    def addController( self, controller ):
204 177
        """Add controller.
205 178
           controller: Controller class"""
......
315 288
        for srcId, dstId in sorted( topo.edges() ):
316 289
            src, dst = self.idToNode[ srcId ], self.idToNode[ dstId ]
317 290
            srcPort, dstPort = topo.port( srcId, dstId )
318
            self.addLink( src, srcPort, dst, dstPort )
291
            createLink( src, srcPort, dst, dstPort )
319 292
            info( '(%s, %s) ' % ( src.name, dst.name ) )
320 293
        info( '\n' )
321 294

  
......
368 341
            controller.start()
369 342
        info( '*** Starting %s switches\n' % len( self.switches ) )
370 343
        for switch in self.switches:
371
            info( switch.name )
344
            info( switch.name + ' ')
372 345
            switch.start( self.controllers )
373 346
        info( '\n' )
374 347

  
......
474 447
           l4Type: string, one of [ TCP, UDP ]
475 448
           verbose: verbose printing
476 449
           returns: results two-element array of server and client speeds"""
450
        log = info if verbose else debug
477 451
        if not hosts:
478 452
            hosts = [ self.hosts[ 0 ], self.hosts[ -1 ] ]
479 453
        else:
480 454
            assert len( hosts ) == 2
481 455
        host0, host1 = hosts
482
        info( '*** Iperf: testing ' + l4Type + ' bandwidth between ' )
483
        info( "%s and %s\n" % ( host0.name, host1.name ) )
456
        log( '*** Iperf: testing ' + l4Type + ' bandwidth between ' )
457
        log( "%s and %s\n" % ( host0.name, host1.name ) )
484 458
        host0.cmd( 'killall -9 iperf' )
485 459
        iperfArgs = 'iperf '
486 460
        bwArgs = ''
......
490 464
        elif l4Type != 'TCP':
491 465
            raise Exception( 'Unexpected l4 type: %s' % l4Type )
492 466
        server = host0.cmd( iperfArgs + '-s &' )
493
        if verbose:
494
            info( '%s\n' % server )
467
        log( '%s\n' % server )
495 468
        client = host1.cmd( iperfArgs + '-t 5 -c ' + host0.IP() + ' ' +
496 469
                           bwArgs )
497
        if verbose:
498
            info( '%s\n' % client )
470
        log( '%s\n' % client )
499 471
        server = host0.cmd( 'killall -9 iperf' )
500
        if verbose:
501
            info( '%s\n' % server )
472
        log( '%s\n' % server )
502 473
        result = [ self._parseIperf( server ), self._parseIperf( client ) ]
503 474
        if l4Type == 'UDP':
504 475
            result.insert( 0, udpBw )
505
        info( '*** Results: %s\n' % result )
476
        log( '*** Results: %s\n' % result )
506 477
        return result
507 478

  
508 479
    def iperfUdp( self, udpBw='10M' ):
mininet/node.py
40 40
import signal
41 41
import select
42 42

  
43
from mininet.log import info, error
44
from mininet.util import quietRun
43
from mininet.log import info, error, debug
44
from mininet.util import quietRun, moveIntf
45 45

  
46 46

  
47 47
class Node( object ):
......
72 72
        self.inToNode[ self.stdin.fileno() ] = self
73 73
        self.pid = self.shell.pid
74 74
        self.intfCount = 0
75
        self.intfs = [] # list of interface names, as strings
75
        self.intfs = {} # dict of port numbers to interface names
76
        self.ports = {} # dict of interface names to port numbers
77
                        # replace with Port objects, eventually ?
76 78
        self.ips = {} # dict of interfaces to ip addresses as strings
77 79
        self.connection = {} # remote node connected to each interface
78 80
        self.waiting = False
79 81
        self.execed = False
80
        self.ports = {} # dict of ints to interface strings
81
                        # replace with Port object, eventually
82 82

  
83 83
    @classmethod
84 84
    def fdToNode( cls, fd ):
......
145 145
        "Send ^C, hopefully interrupting an interactive subprocess."
146 146
        self.write( chr( 3 ) )
147 147

  
148
    def waitOutput( self ):
148
    def waitOutput( self, verbose=False ):
149 149
        """Wait for a command to complete.
150 150
           Completion is signaled by a sentinel character, ASCII(127)
151 151
           appearing in the output stream.  Wait for the sentinel and return
152
           the output, including trailing newline."""
152
           the output, including trailing newline.
153
           verbose: print output interactively"""
154
        log = info if verbose else debug
153 155
        assert self.waiting
154 156
        output = ''
155 157
        while True:
......
157 159
            data = self.read( 1024 )
158 160
            if len( data ) > 0  and data[ -1 ] == chr( 0177 ):
159 161
                output += data[ :-1 ]
162
                log( output )
160 163
                break
161 164
            else:
162 165
                output += data
163 166
        self.waiting = False
164 167
        return output
165 168

  
166
    def cmd( self, cmd ):
169
    def cmd( self, cmd, verbose=False ):
167 170
        """Send a command, wait for output, and return it.
168 171
           cmd: string"""
172
        log = info if verbose else debug
173
        log( '*** %s : %s', self.name, cmd )
169 174
        self.sendCmd( cmd )
170
        return self.waitOutput()
175
        return self.waitOutput( verbose )
171 176

  
172 177
    def cmdPrint( self, cmd ):
173 178
        """Call cmd and printing its output
174 179
           cmd: string"""
175
        #info( '*** %s : %s', self.name, cmd )
176
        result = self.cmd( cmd )
177
        #info( '%s\n', result )
178
        return result
180
        return self.cmd( cmd, verbose=True )
179 181

  
180 182
    # Interface management, configuration, and routing
183

  
184
    # BL notes: This might be a bit redundant or over-complicated.
185
    # However, it does allow a bit of specialization, including
186
    # changing the canonical interface names. It's also tricky since
187
    # the real interfaces are created as veth pairs, so we can't
188
    # make a single interface at a time.
189

  
181 190
    def intfName( self, n ):
182
        "Construct a canonical interface name node-intf for interface N."
191
        "Construct a canonical interface name node-ethN for interface n."
183 192
        return self.name + '-eth' + repr( n )
184 193

  
185 194
    def newIntf( self ):
186 195
        "Reserve and return a new interface name."
187 196
        intfName = self.intfName( self.intfCount )
188 197
        self.intfCount += 1
189
        self.intfs += [ intfName ]
190 198
        return intfName
191 199

  
200
    def addIntf( self, intf, port ):
201
        """Add an interface.
202
           intf: interface name (nodeN-ethM)
203
           port: port number (typically OpenFlow port number)"""
204
        self.intfs[ port ] = intf
205
        self.ports[ intf ] = port
206
        #info( '\n' )
207
        #info( 'added intf %s to node %x\n' % ( srcIntf, src ) )
208
        if self.inNamespace:
209
            #info( 'moving w/inNamespace set\n' )
210
            moveIntf( intf, self )
211

  
212
    def connect( self, intf, dstNode, dstIntf ):
213
        "Register connection of intf to dstIntf on dstNode."
214
        self.connection[ intf ] = ( dstNode, dstIntf )
215

  
192 216
    def setMAC( self, intf, mac ):
193 217
        """Set the MAC address for an interface.
194 218
           mac: MAC address as string"""
......
206 230

  
207 231
    def setIP( self, intf, ip, bits ):
208 232
        """Set the IP address for an interface.
209
           intf: string, interface name
233
           intf: interface name
210 234
           ip: IP address as a string
211
           bits:"""
235
           bits: prefix length of form /24"""
212 236
        result = self.cmd( [ 'ifconfig', intf, ip + bits, 'up' ] )
213 237
        self.ips[ intf ] = ip
214 238
        return result
......
226 250
        return self.cmd( 'route add default ' + intf )
227 251

  
228 252
    def IP( self ):
229
        "Return IP address of first interface"
230
        if len( self.intfs ) > 0:
231
            return self.ips.get( self.intfs[ 0 ], None )
253
        "Return IP address of interface 0"
254
        return self.ips.get( self.intfs.get( 0 , None ), None )
232 255

  
233
    def intfIsUp( self ):
234
        "Check if one of our interfaces is up."
235
        return 'UP' in self.cmd( 'ifconfig ' + self.intfs[ 0 ] )
256
    def intfIsUp( self, port ):
257
        """Check if interface for a given port number is up.
258
           port: port number"""
259
        return 'UP' in self.cmd( 'ifconfig ' + self.intfs[ port ] )
236 260

  
237 261
    # Other methods
238 262
    def __str__( self ):
239 263
        result = self.name + ':'
240
        if self.IP():
241
            result += ' IP=' + self.IP()
242
        result += ' intfs=' + ','.join( self.intfs )
264
        result += ' IP=' + repr( self.IP() )
265
        result += ' intfs=' + ','.join( sorted( self.intfs.values() ) )
243 266
        result += ' waiting=' + repr( self.waiting )
244 267
        return result
245 268

  
......
282 305
    def start( self, controllers ):
283 306
        """Start OpenFlow reference user datapath.
284 307
           Log to /tmp/sN-{ofd,ofp}.log.
285
           controllers: dict of controller names to objects"""
308
           controllers: list of controller objects"""
286 309
        controller = controllers[ 0 ]
287 310
        ofdlog = '/tmp/' + self.name + '-ofd.log'
288 311
        ofplog = '/tmp/' + self.name + '-ofp.log'
289 312
        self.cmd( 'ifconfig lo up' )
290
        intfs = self.intfs
313
        intfs = sorted( self.intfs.values() )
291 314

  
292
        self.cmdPrint( 'ofdatapath -i ' + ','.join( intfs ) +
315
        self.cmd( 'ofdatapath -i ' + ','.join( intfs ) +
293 316
            ' punix:/tmp/' + self.name +
294 317
            ' 1> ' + ofdlog + ' 2> ' + ofdlog + ' &' )
295
        self.cmdPrint( 'ofprotocol unix:/tmp/' + self.name +
318
        self.cmd( 'ofprotocol unix:/tmp/' + self.name +
296 319
            ' tcp:' + controller.IP() + ' --fail=closed' +
297 320
            ' 1> ' + ofplog + ' 2>' + ofplog + ' &' )
298 321

  
......
322 345
        # Delete local datapath if it exists;
323 346
        # then create a new one monitoring the given interfaces
324 347
        quietRun( 'dpctl deldp nl:%i' % self.dp )
325
        self.cmdPrint( 'dpctl adddp nl:%i' % self.dp )
348
        self.cmd( 'dpctl adddp nl:%i' % self.dp )
326 349
        if self.defaultMac:
327 350
            intf = 'of%i' % self.dp
328 351
            self.cmd( [ 'ifconfig', intf, 'hw', 'ether', self.defaultMac ] )
329 352

  
330
        if len( self.ports ) != max( self.ports.keys() ) + 1:
353
        if len( self.intfs ) != max( self.intfs ) + 1:
331 354
            raise Exception( 'only contiguous, zero-indexed port ranges'
332
                            'supported: %s' % self.ports )
333
        intfs = [ self.ports[ port ] for port in self.ports.keys() ]
334
        self.cmdPrint( 'dpctl addif nl:' + str( self.dp ) + ' ' +
355
                            'supported: %s' % self.intfs )
356
        intfs = [ self.intfs[ port ] for port in sorted( self.intfs.keys() ) ]
357
        self.cmd( 'dpctl addif nl:' + str( self.dp ) + ' ' +
335 358
            ' '.join( intfs ) )
336 359
        # Run protocol daemon
337 360
        controller = controllers[ 0 ]
338
        self.cmdPrint( 'ofprotocol nl:' + str( self.dp ) + ' tcp:' +
361
        self.cmd( 'ofprotocol nl:' + str( self.dp ) + ' tcp:' +
339 362
                      controller.IP() + ':' +
340 363
                      str( controller.port ) +
341 364
                      ' --fail=closed 1> ' + ofplog + ' 2>' + ofplog + ' &' )
......
345 368
        "Terminate kernel datapath."
346 369
        quietRun( 'dpctl deldp nl:%i' % self.dp )
347 370
        # In theory the interfaces should go away after we shut down.
348
        # However, this takes time, so we're better off to remove them
371
        # However, this takes time, so we're better off removing them
349 372
        # explicitly so that we won't get errors if we run before they
350 373
        # have been removed by the kernel. Unfortunately this is very slow.
351 374
        self.cmd( 'kill %ofprotocol' )
352
        for intf in self.intfs:
375
        for intf in self.intfs.values():
353 376
            quietRun( 'ip link del ' + intf )
354 377
            info( '.' )
355 378

  
......
374 397
        # Delete local datapath if it exists;
375 398
        # then create a new one monitoring the given interfaces
376 399
        quietRun( 'ovs-dpctl del-dp dp%i' % self.dp )
377
        self.cmdPrint( 'ovs-dpctl add-dp dp%i' % self.dp )
400
        self.cmd( 'ovs-dpctl add-dp dp%i' % self.dp )
378 401
        if self.defaultMac:
379 402
            intf = 'dp' % self.dp
380 403
            mac = self.defaultMac
381 404
            self.cmd( [ 'ifconfig', intf, 'hw', 'ether', mac ] )
382 405

  
383
        if len( self.ports ) != max( self.ports.keys() ) + 1:
406
        if len( self.intfs ) != max( self.intfs ) + 1:
384 407
            raise Exception( 'only contiguous, zero-indexed port ranges'
385
                            'supported: %s' % self.ports )
386
        intfs = [ self.ports[ port ] for port in self.ports.keys() ]
387
        self.cmdPrint( 'ovs-dpctl add-if dp' + str( self.dp ) + ' ' +
408
                            'supported: %s' % self.intfs )
409
        intfs = [ self.intfs[ port ] for port in sorted( self.intfs.keys() ) ]
410
        self.cmd( 'ovs-dpctl add-if dp' + str( self.dp ) + ' ' +
388 411
                      ' '.join( intfs ) )
389 412
        # Run protocol daemon
390 413
        controller = controllers[ 0 ]
391
        self.cmdPrint( 'ovs-openflowd dp' + str( self.dp ) + ' tcp:' +
414
        self.cmd( 'ovs-openflowd dp' + str( self.dp ) + ' tcp:' +
392 415
                      controller.IP() + ':' +
393 416
                      ' --fail=closed 1> ' + ofplog + ' 2>' + ofplog + ' &' )
394 417
        self.execed = False
......
397 420
        "Terminate kernel datapath."
398 421
        quietRun( 'ovs-dpctl del-dp dp%i' % self.dp )
399 422
        # In theory the interfaces should go away after we shut down.
400
        # However, this takes time, so we're better off to remove them
423
        # However, this takes time, so we're better off removing them
401 424
        # explicitly so that we won't get errors if we run before they
402 425
        # have been removed by the kernel. Unfortunately this is very slow.
403 426
        self.cmd( 'kill %ovs-openflowd' )
404
        for intf in self.intfs:
427
        for intf in self.intfs.values():
405 428
            quietRun( 'ip link del ' + intf )
406 429
            info( '.' )
407 430

  
......
425 448
           Log to /tmp/cN.log"""
426 449
        cout = '/tmp/' + self.name + '.log'
427 450
        if self.cdir is not None:
428
            self.cmdPrint( 'cd ' + self.cdir )
429
        self.cmdPrint( self.controller + ' ' + self.cargs +
451
            self.cmd( 'cd ' + self.cdir )
452
        self.cmd( self.controller + ' ' + self.cargs +
430 453
            ' 1> ' + cout + ' 2> ' + cout + ' &' )
431 454
        self.execed = False
432 455

  
mininet/util.py
15 15
def checkRun( cmd ):
16 16
    """Simple interface to subprocess.check_call()
17 17
       cmd: list of command params"""
18
    check_call( cmd.split( ' ' ) )
18
    return check_call( cmd.split( ' ' ) )
19 19

  
20 20
def quietRun( cmd ):
21 21
    """Run a command, routing stderr to stdout, and return the output.
......
63 63
    quietRun( 'ip link del ' + intf2 )
64 64
    # Create new pair
65 65
    cmd = 'ip link add name ' + intf1 + ' type veth peer name ' + intf2
66
    return checkRun( cmd )
66
    return quietRun( cmd )
67 67

  
68 68
def retry( retries, delaySecs, fn, *args, **keywords ):
69 69
    """Try something several times before giving up.
......
101 101
       printError: if true, print error"""
102 102
    retry( retries, delaySecs, moveIntfNoRetry, intf, node, printError )
103 103

  
104
def createLink( node1, node2, retries=10, delaySecs=0.001 ):
104
def createLink( node1, port1, node2, port2 ):
105 105
    """Create a link between nodes, making an interface for each.
106 106
       node1: Node object
107
       node2: Node object"""
108
    intf1 = node1.newIntf()
109
    intf2 = node2.newIntf()
107
       port1: node1 port number
108
       node2: Node object
109
       port2: node2 port number
110
       returns: intf1 name, intf2 name"""
111
    intf1 = node1.intfName( port1 )
112
    intf2 = node2.intfName( port2 )
110 113
    makeIntfPair( intf1, intf2 )
111
    if node1.inNamespace:
112
        retry( retries, delaySecs, moveIntf, intf1, node1 )
113
    if node2.inNamespace:
114
        retry( retries, delaySecs, moveIntf, intf2, node2 )
115
    node1.connection[ intf1 ] = ( node2, intf2 )
116
    node2.connection[ intf2 ] = ( node1, intf1 )
114
    node1.addIntf( intf1, port1 )
115
    node2.addIntf( intf2, port2 )
116
    node1.connect( intf1, node2, intf2 )
117
    node2.connect( intf2, node1, intf1 )
117 118
    return intf1, intf2
118 119

  
119 120
def fixLimits():
......
141 142

  
142 143
def ipStr( ip ):
143 144
    """Generate IP address string
144
       returns: ip addr string"""
145
       ip: unsigned int of form x << 16 | y << 8 | z
146
       returns: ip address string 10.x.y.z """
145 147
    hi = ( ip & 0xff0000 ) >> 16
146 148
    mid = ( ip & 0xff00 ) >> 8
147 149
    lo = ip & 0xff

Also available in: Unified diff