Revision a6bcad8f mininet/node.py

View differences:

mininet/node.py
46 46
import signal
47 47
import select
48 48
from subprocess import Popen, PIPE, STDOUT
49
from time import sleep
50 49

  
51 50
from mininet.log import info, error, debug
52
from mininet.util import quietRun, errRun, makeIntfPair, moveIntf, isShellBuiltin
51
from mininet.util import quietRun, errRun, moveIntf, isShellBuiltin
53 52
from mininet.moduledeps import moduleDeps, pathCheck, OVS_KMOD, OF_KMOD, TUN
53
from mininet.link import Link
54 54

  
55 55
SWITCH_PORT_BASE = 1  # For OF > 0.9, switch ports start at 1 rather than zero
56 56

  
......
78 78
            opts += 'n'
79 79
        cmd = [ 'mnexec', opts, 'bash', '-m' ]
80 80
        self.shell = Popen( cmd, stdin=PIPE, stdout=PIPE, stderr=STDOUT,
81
            close_fds=False )
81
            close_fds=True )
82 82
        self.stdin = self.shell.stdin
83 83
        self.stdout = self.shell.stdout
84 84
        self.pid = self.shell.pid
......
89 89
        # using select.poll()
90 90
        self.outToNode[ self.stdout.fileno() ] = self
91 91
        self.inToNode[ self.stdin.fileno() ] = self
92
        self.intfs = {}  # dict of port numbers to interface names
93
        self.ports = {}  # dict of interface names to port numbers
92
        self.intfs = {}  # dict of port numbers to interfaces
93
        self.ports = {}  # dict of interfaces to port numbers
94 94
                         # replace with Port objects, eventually ?
95
        self.ips = {}  # dict of interfaces to ip addresses as strings
96
        self.macs = {}  # dict of interfacesto mac addresses as strings
97
        self.connection = {}  # remote node connected to each interface
95
        self.nameToIntf = {}  # dict of interface names to Intfs
98 96
        self.execed = False
99 97
        self.lastCmd = None
100 98
        self.lastPid = None
......
172 170
        if len( args ) > 0:
173 171
            cmd = args
174 172
        if not isinstance( cmd, str ):
175
            cmd = ' '.join( cmd )
173
            cmd = ' '.join( [ str( c ) for c in cmd ] )
176 174
        if not re.search( r'\w', cmd ):
177 175
            # Replace empty commands with something harmless
178 176
            cmd = 'echo -n'
......
254 252
    # the real interfaces are created as veth pairs, so we can't
255 253
    # make a single interface at a time.
256 254

  
257
    def intfName( self, n ):
258
        "Construct a canonical interface name node-ethN for interface n."
259
        return self.name + '-eth' + repr( n )
260

  
261 255
    def newPort( self ):
262 256
        "Return the next port number to allocate."
263 257
        if len( self.ports ) > 0:
......
266 260

  
267 261
    def addIntf( self, intf, port=None ):
268 262
        """Add an interface.
269
           intf: interface name (e.g. nodeN-ethM)
263
           intf: interface
270 264
           port: port number (optional, typically OpenFlow port number)"""
271 265
        if port is None:
272 266
            port = self.newPort()
273 267
        self.intfs[ port ] = intf
274 268
        self.ports[ intf ] = port
275
        #info( '\n' )
276
        #info( 'added intf %s:%d to node %s\n' % ( intf,port, self.name ) )
269
        self.nameToIntf[ intf.name ] = intf
270
        info( '\n' )
271
        info( 'added intf %s:%d to node %s\n' % ( intf,port, self.name ) )
277 272
        if self.inNamespace:
278
            #info( 'moving w/inNamespace set\n' )
279
            moveIntf( intf, self )
273
            info( 'moving', intf, 'into namespace for', self.name, '\n' )
274
            moveIntf( intf.name, self )
280 275

  
281
    def registerIntf( self, intf, dstNode, dstIntf ):
282
        "Register connection of intf to dstIntf on dstNode."
283
        self.connection[ intf ] = ( dstNode, dstIntf )
276
    def defaultIntf( self ):
277
        "Return interface for lowest port"
278
        ports = self.intfs.keys()
279
        if ports:
280
            return self.intfs[ min( ports ) ]
284 281

  
285
    def connectionsTo( self, node):
286
        "Return [(srcIntf, dstIntf)..] for connections to dstNode."
282
    def intf( self, intf='' ):
283
        """Return our interface object with given name,x
284
           or default intf if name is empty"""
285
        if not intf:
286
            return self.defaultIntf()
287
        elif type( intf) is str:
288
            return self.nameToIntf[ intf ]
289
        else:
290
            return intf
291

  
292
    def linksTo( self, node):
293
        "Return [ link1, link2...] for all links from self to node."
287 294
        # We could optimize this if it is important
288
        connections = []
289
        for intf in self.connection.keys():
290
            dstNode, dstIntf = self.connection[ intf ]
291
            if dstNode == node:
292
                connections.append( ( intf, dstIntf ) )
293
        return connections
294

  
295
    # This is a symmetric operation, but it makes sense to put
296
    # the code here since it is tightly coupled to routines in
297
    # this class. For a more symmetric API, you can use
298
    # mininet.util.createLink()
299

  
300
    def linkTo( self, node2, port1=None, port2=None ):
301
        """Create link to another node, making two new interfaces.
302
           node2: Node to link us to
303
           port1: our port number (optional)
304
           port2: node2 port number (optional)
305
           returns: intf1 name, intf2 name"""
306
        node1 = self
307
        if port1 is None:
308
            port1 = node1.newPort()
309
        if port2 is None:
310
            port2 = node2.newPort()
311
        intf1 = node1.intfName( port1 )
312
        intf2 = node2.intfName( port2 )
313
        makeIntfPair( intf1, intf2 )
314
        node1.addIntf( intf1, port1 )
315
        node2.addIntf( intf2, port2 )
316
        node1.registerIntf( intf1, node2, intf2 )
317
        node2.registerIntf( intf2, node1, intf1 )
318
        return intf1, intf2
295
        links = []
296
        for intf in self.intfs:
297
            link = intf.link
298
            nodes = ( link.intf1.node, link.intf2.node )
299
            if self in nodes and node in nodes:
300
                links.append( link )
301
        return links
319 302

  
320 303
    def deleteIntfs( self ):
321 304
        "Delete all of our interfaces."
......
325 308
        # have been removed by the kernel. Unfortunately this is very slow,
326 309
        # at least with Linux kernels before 2.6.33
327 310
        for intf in self.intfs.values():
328
            quietRun( 'ip link del ' + intf )
311
            intf.delete()
329 312
            info( '.' )
330
            # Does it help to sleep to let things run?
331
            sleep( 0.001 )
332 313

  
333
    def setMAC( self, intf, mac ):
334
        """Set the MAC address for an interface.
335
           mac: MAC address as string"""
336
        result = self.cmd( 'ifconfig', intf, 'down' )
337
        result += self.cmd( 'ifconfig', intf, 'hw', 'ether', mac )
338
        result += self.cmd( 'ifconfig', intf, 'up' )
339
        return result
314
    # Routing support
340 315

  
341 316
    def setARP( self, ip, mac ):
342 317
        """Add an ARP entry.
......
345 320
        result = self.cmd( 'arp', '-s', ip, mac )
346 321
        return result
347 322

  
348
    def setIP( self, intf, ip, prefixLen=8 ):
349
        """Set the IP address for an interface.
350
           intf: interface name
351
           ip: IP address as a string
352
           prefixLen: prefix length, e.g. 8 for /8 or 16M addrs"""
353
        ipSub = '%s/%d' % ( ip, prefixLen )
354
        result = self.cmd( 'ifconfig', intf, ipSub, 'up' )
355
        self.ips[ intf ] = ip
356
        return result
357

  
358 323
    def setHostRoute( self, ip, intf ):
359 324
        """Add route to host.
360 325
           ip: IP address as dotted decimal
......
365 330
        """Set the default route to go through intf.
366 331
           intf: string, interface name"""
367 332
        self.cmd( 'ip route flush root 0/0' )
368
        return self.cmd( 'route add default ' + intf )
333
        return self.cmd( 'route add default %s' % intf )
369 334

  
370
    def defaultIntf( self ):
371
        "Return interface for lowest port"
372
        ports = self.intfs.keys()
373
        if ports:
374
            return self.intfs[ min( ports ) ]
335
    # Convenience methods
375 336

  
376
    _ipMatchRegex = re.compile( r'\d+\.\d+\.\d+\.\d+' )
377
    _macMatchRegex = re.compile( r'..:..:..:..:..:..' )
337
    def setMAC( self, mac, intf=''):
338
        """Set the MAC address for an interface.
339
           intf: intf or intf name
340
           mac: MAC address as string"""
341
        return self.intf( intf ).setMAC( mac )
342

  
343
    def setIP( self, ip, prefixLen=8, intf='' ):
344
        """Set the IP address for an interface.
345
           intf: interface name
346
           ip: IP address as a string
347
           prefixLen: prefix length, e.g. 8 for /8 or 16M addrs"""
348
        # This should probably be rethought:
349
        ipSub = '%s/%s' % ( ip, prefixLen )
350
        return self.intf( intf ).setIP( ipSub )
378 351

  
379 352
    def IP( self, intf=None ):
380 353
        "Return IP address of a node or specific interface."
381
        if intf is None:
382
            intf = self.defaultIntf()
383
        if intf and not self.waiting:
384
            self.updateIP( intf )
385
        return self.ips.get( intf, None )
354
        return self.intf( intf ).IP()
386 355

  
387 356
    def MAC( self, intf=None ):
388 357
        "Return MAC address of a node or specific interface."
389
        if intf is None:
390
            intf = self.defaultIntf()
391
        if intf and not self.waiting:
392
            self.updateMAC( intf )
393
        return self.macs.get( intf, None )
394

  
395
    def updateIP( self, intf ):
396
        "Update IP address for an interface"
397
        assert not self.waiting
398
        ifconfig = self.cmd( 'ifconfig ' + intf )
399
        ips = self._ipMatchRegex.findall( ifconfig )
400
        if ips:
401
            self.ips[ intf ] = ips[ 0 ]
402
        else:
403
            self.ips[ intf ] = None
358
        return self.intf( intf ).MAC()
404 359

  
405
    def updateMAC( self, intf ):
406
        "Update MAC address for an interface"
407
        assert not self.waiting
408
        ifconfig = self.cmd( 'ifconfig ' + intf )
409
        macs = self._macMatchRegex.findall( ifconfig )
410
        if macs:
411
            self.macs[ intf ] = macs[ 0 ]
412
        else:
413
            self.macs[ intf ] = None
414

  
415
    def intfIsUp( self, intf ):
360
    def intfIsUp( self, intf=None ):
416 361
        "Check if an interface is up."
417
        return 'UP' in self.cmd( 'ifconfig ' + intf )
362
        return self.intf( intf ).isUp()
363

  
364
    # This is here for backward compatibility
365
    def linkTo( self, node, link=Link ):
366
        """(Deprecated) Link to another node
367
           replace with Link( node1, node2)"""
368
        return link( self, node )
418 369

  
419 370
    # Other methods
371

  
372
    def intfNames( self ):
373
        "The names of our interfaces"
374
        return [ str( i ) for i in sorted( self.ports.values() ) ]
375

  
420 376
    def __str__( self ):
421
        intfs = sorted( self.intfs.values() )
422 377
        return '%s: IP=%s intfs=%s pid=%s' % (
423
            self.name, self.IP(), ','.join( intfs ), self.pid )
378
            self.name, self.IP(), ','.join( self.intfNames() ), self.pid )
424 379

  
425 380

  
426 381
class Host( Node ):
......
623 578
    def __init__( self, name, dp=None, **kwargs ):
624 579
        """Init.
625 580
           name: name for switch
626
           dp: netlink id (0, 1, 2, ...)
627 581
           defaultMAC: default MAC as unsigned int; random value if None"""
628 582
        Switch.__init__( self, name, **kwargs )
629
        self.dp = 'dp%i' % dp
583
        self.dp = name
630 584

  
631 585
    @staticmethod
632 586
    def setup():
......
671 625

  
672 626
OVSKernelSwitch = OVSSwitch
673 627

  
674

  
675 628
class Controller( Node ):
676 629
    """A Controller is a Node that is running (or has execed?) an
677 630
       OpenFlow controller."""
......
704 657

  
705 658
    def IP( self, intf=None ):
706 659
        "Return IP address of the Controller"
707
        ip = Node.IP( self, intf=intf )
708
        if ip is None:
660
        if self.intfs:
661
            ip = Node.IP( self, intf )
662
        else:
709 663
            ip = self.defaultIP
710 664
        return ip
711 665

  

Also available in: Unified diff