Revision efc9a01c mininet/node.py

View differences:

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

  

Also available in: Unified diff