Statistics
| Branch: | Tag: | Revision:

mininet / mininet / node.py @ e953444f

History | View | Annotate | Download (20.1 KB)

1 7d4b7b7f Bob Lantz
"""
2
Node objects for Mininet.
3

4 6d72a3cb Bob Lantz
Nodes provide a simple abstraction for interacting with hosts, switches
5
and controllers. Local nodes are simply one or more processes on the local
6
machine.
7 7d4b7b7f Bob Lantz

8 6d72a3cb Bob Lantz
Node: superclass for all (primarily local) network nodes.
9 7d4b7b7f Bob Lantz

10 31b43002 Bob Lantz
Host: a virtual host. By default, a host is simply a shell; commands
11
    may be sent using Cmd (which waits for output), or using sendCmd(),
12
    which returns immediately, allowing subsequent monitoring using
13
    monitor(). Examples of how to run experiments using this
14
    functionality are provided in the examples/ directory.
15 7d4b7b7f Bob Lantz

16
Switch: superclass for switch nodes.
17

18
UserSwitch: a switch using the user-space switch from the OpenFlow
19
    reference implementation.
20

21
KernelSwitch: a switch using the kernel switch from the OpenFlow reference
22
    implementation.
23

24
OVSSwitch: a switch using the OpenVSwitch OpenFlow-compatible switch
25
    implementation (openvswitch.org).
26

27
Controller: superclass for OpenFlow controllers. The default controller
28
    is controller(8) from the reference implementation.
29

30
NOXController: a controller node using NOX (noxrepo.org).
31

32
RemoteController: a remote controller node, which may use any
33 7c371cf3 Bob Lantz
    arbitrary OpenFlow-compatible controller, and which is not
34
    created or managed by mininet.
35 31b43002 Bob Lantz

36 80be5642 Bob Lantz
Future enhancements:
37

38
- Possibly make Node, Switch and Controller more abstract so that
39
  they can be used for both local and remote nodes
40

41
- Create proxy objects for remote nodes (Mininet: Cluster Edition)
42 7d4b7b7f Bob Lantz
"""
43 89bf3103 Brandon Heller
44 723d068c Brandon Heller
import os
45 75d72d96 Bob Lantz
import re
46 723d068c Brandon Heller
import signal
47
import select
48 75d72d96 Bob Lantz
from subprocess import Popen, PIPE, STDOUT
49 086ef80e Bob Lantz
from time import sleep
50 60d9ead6 David Erickson
51 efc9a01c Bob Lantz
from mininet.log import info, error, debug
52 d44a5843 Bob Lantz
from mininet.util import quietRun, makeIntfPair, moveIntf
53 723d068c Brandon Heller
54 80a8fa62 Bob Lantz
class Node( object ):
55
    """A virtual network node is simply a shell in a network namespace.
56
       We communicate with it using pipes."""
57 6d72a3cb Bob Lantz
58
    inToNode = {} # mapping of input fds to nodes
59
    outToNode = {} # mapping of output fds to nodes
60 89bf3103 Brandon Heller
61 086ef80e Bob Lantz
    def __init__( self, name, inNamespace=True,
62
        defaultMAC=None, defaultIP=None ):
63
        """name: name of node
64
           inNamespace: in network namespace?
65
           defaultMAC: default MAC address for intf 0
66
           defaultIP: default IP address for intf 0"""
67 89bf3103 Brandon Heller
        self.name = name
68
        closeFds = False # speed vs. memory use
69 4065511a Bob Lantz
        # setsid is necessary to detach from tty
70 dc630c54 Bob Lantz
        # xpg_echo is needed so we can echo our sentinel in sendCmd
71 4065511a Bob Lantz
        cmd = [ '/usr/bin/setsid', '/bin/bash', '-O', 'xpg_echo' ]
72 89bf3103 Brandon Heller
        self.inNamespace = inNamespace
73
        if self.inNamespace:
74 80a8fa62 Bob Lantz
            cmd = [ 'netns' ] + cmd
75
        self.shell = Popen( cmd, stdin=PIPE, stdout=PIPE, stderr=STDOUT,
76 281f6e59 Bob Lantz
            close_fds=closeFds )
77 89bf3103 Brandon Heller
        self.stdin = self.shell.stdin
78
        self.stdout = self.shell.stdout
79
        self.pollOut = select.poll()
80 80a8fa62 Bob Lantz
        self.pollOut.register( self.stdout )
81 89bf3103 Brandon Heller
        # Maintain mapping between file descriptors and nodes
82
        # This could be useful for monitoring multiple nodes
83
        # using select.poll()
84 80a8fa62 Bob Lantz
        self.outToNode[ self.stdout.fileno() ] = self
85
        self.inToNode[ self.stdin.fileno() ] = self
86 89bf3103 Brandon Heller
        self.pid = self.shell.pid
87 efc9a01c Bob Lantz
        self.intfs = {} # dict of port numbers to interface names
88
        self.ports = {} # dict of interface names to port numbers
89
                        # replace with Port objects, eventually ?
90 60d9ead6 David Erickson
        self.ips = {} # dict of interfaces to ip addresses as strings
91 6d72a3cb Bob Lantz
        self.connection = {} # remote node connected to each interface
92 89bf3103 Brandon Heller
        self.waiting = False
93
        self.execed = False
94 086ef80e Bob Lantz
        self.defaultIP = defaultIP
95
        self.defaultMAC = defaultMAC
96 89bf3103 Brandon Heller
97 6d72a3cb Bob Lantz
    @classmethod
98
    def fdToNode( cls, fd ):
99
        """Return node corresponding to given file descriptor.
100
           fd: file descriptor
101
           returns: node"""
102
        node = Node.outToNode.get( fd )
103
        return node or Node.inToNode.get( fd )
104 89bf3103 Brandon Heller
105 80a8fa62 Bob Lantz
    def cleanup( self ):
106
        "Help python collect its garbage."
107 89bf3103 Brandon Heller
        self.shell = None
108
109
    # Subshell I/O, commands and control
110 6d72a3cb Bob Lantz
    def read( self, bytes ):
111
        """Read from a node.
112
           bytes: maximum number of bytes to read"""
113
        return os.read( self.stdout.fileno(), bytes )
114 80a8fa62 Bob Lantz
115
    def write( self, data ):
116
        """Write data to node.
117
           data: string"""
118
        os.write( self.stdin.fileno(), data )
119
120
    def terminate( self ):
121 6d72a3cb Bob Lantz
        "Send kill signal to Node and clean up after it."
122 80a8fa62 Bob Lantz
        os.kill( self.pid, signal.SIGKILL )
123 89bf3103 Brandon Heller
        self.cleanup()
124
125 80a8fa62 Bob Lantz
    def stop( self ):
126
        "Stop node."
127 89bf3103 Brandon Heller
        self.terminate()
128
129 80a8fa62 Bob Lantz
    def waitReadable( self ):
130 6d72a3cb Bob Lantz
        "Wait until node's output is readable."
131 89bf3103 Brandon Heller
        self.pollOut.poll()
132
133 80a8fa62 Bob Lantz
    def sendCmd( self, cmd ):
134
        """Send a command, followed by a command to echo a sentinel,
135
           and return without waiting for the command to complete."""
136 89bf3103 Brandon Heller
        assert not self.waiting
137 e953444f Bob Lantz
        if isinstance( cmd, list ):
138
            cmd = ' '.join( cmd )
139 80a8fa62 Bob Lantz
        if cmd[ -1 ] == '&':
140 89bf3103 Brandon Heller
            separator = '&'
141 80a8fa62 Bob Lantz
            cmd = cmd[ :-1 ]
142 89bf3103 Brandon Heller
        else:
143
            separator = ';'
144 80a8fa62 Bob Lantz
        self.write( cmd + separator + ' echo -n "\\0177" \n' )
145 89bf3103 Brandon Heller
        self.waiting = True
146
147 4065511a Bob Lantz
    def sendInt( self ):
148
        """Placeholder for function to interrupt running subprocess.
149
           This is a tricky problem to solve."""
150
        self.write( chr( 3 ) )
151
152 80a8fa62 Bob Lantz
    def monitor( self ):
153 6d72a3cb Bob Lantz
        "Monitor the output of a command, returning (done?, data)."
154 89bf3103 Brandon Heller
        assert self.waiting
155
        self.waitReadable()
156 80a8fa62 Bob Lantz
        data = self.read( 1024 )
157 4065511a Bob Lantz
        if len( data ) > 0 and data[ -1 ] == chr( 127 ):
158 89bf3103 Brandon Heller
            self.waiting = False
159 80a8fa62 Bob Lantz
            return True, data[ :-1 ]
160 4065511a Bob Lantz
        elif chr( 127 ) in data:
161
            return True, data.replace( chr( 127 ), '' )
162
        return False, data
163 723d068c Brandon Heller
164 efc9a01c Bob Lantz
    def waitOutput( self, verbose=False ):
165 80a8fa62 Bob Lantz
        """Wait for a command to complete.
166 7c371cf3 Bob Lantz
           Completion is signaled by a sentinel character, ASCII(127)
167 80a8fa62 Bob Lantz
           appearing in the output stream.  Wait for the sentinel and return
168 efc9a01c Bob Lantz
           the output, including trailing newline.
169
           verbose: print output interactively"""
170
        log = info if verbose else debug
171 89bf3103 Brandon Heller
        output = ''
172 4065511a Bob Lantz
        done = False
173
        while not done:
174
            done, data = self.monitor()
175
            output += data
176
            log( data )
177 89bf3103 Brandon Heller
        return output
178
179 efc9a01c Bob Lantz
    def cmd( self, cmd, verbose=False ):
180 80a8fa62 Bob Lantz
        """Send a command, wait for output, and return it.
181
           cmd: string"""
182 efc9a01c Bob Lantz
        log = info if verbose else debug
183 dc630c54 Bob Lantz
        log( '*** %s : %s\n' % ( self.name, cmd ) )
184 80a8fa62 Bob Lantz
        self.sendCmd( cmd )
185 efc9a01c Bob Lantz
        return self.waitOutput( verbose )
186 89bf3103 Brandon Heller
187 80a8fa62 Bob Lantz
    def cmdPrint( self, cmd ):
188
        """Call cmd and printing its output
189
           cmd: string"""
190 efc9a01c Bob Lantz
        return self.cmd( cmd, verbose=True )
191 89bf3103 Brandon Heller
192
    # Interface management, configuration, and routing
193 efc9a01c Bob Lantz
194
    # BL notes: This might be a bit redundant or over-complicated.
195
    # However, it does allow a bit of specialization, including
196
    # changing the canonical interface names. It's also tricky since
197
    # the real interfaces are created as veth pairs, so we can't
198
    # make a single interface at a time.
199
200 80a8fa62 Bob Lantz
    def intfName( self, n ):
201 efc9a01c Bob Lantz
        "Construct a canonical interface name node-ethN for interface n."
202 80a8fa62 Bob Lantz
        return self.name + '-eth' + repr( n )
203 89bf3103 Brandon Heller
204 d44a5843 Bob Lantz
    def newPort( self ):
205
        "Return the next port number to allocate."
206
        if len( self.ports ) > 0:
207
            return max( self.ports.values() ) + 1
208
        return 0
209 89bf3103 Brandon Heller
210 efc9a01c Bob Lantz
    def addIntf( self, intf, port ):
211
        """Add an interface.
212
           intf: interface name (nodeN-ethM)
213
           port: port number (typically OpenFlow port number)"""
214
        self.intfs[ port ] = intf
215
        self.ports[ intf ] = port
216
        #info( '\n' )
217 d44a5843 Bob Lantz
        #info( 'added intf %s:%d to node %s\n' % ( intf,port, self.name ) )
218 efc9a01c Bob Lantz
        if self.inNamespace:
219
            #info( 'moving w/inNamespace set\n' )
220
            moveIntf( intf, self )
221
222 d44a5843 Bob Lantz
    def registerIntf( self, intf, dstNode, dstIntf ):
223 efc9a01c Bob Lantz
        "Register connection of intf to dstIntf on dstNode."
224
        self.connection[ intf ] = ( dstNode, dstIntf )
225
226 d44a5843 Bob Lantz
    # This is a symmetric operation, but it makes sense to put
227
    # the code here since it is tightly coupled to routines in
228
    # this class. For a more symmetric API, you can use
229
    # mininet.util.createLink()
230
231
    def linkTo( self, node2, port1=None, port2=None ):
232
        """Create link to another node, making two new interfaces.
233
           node2: Node to link us to
234
           port1: our port number (optional)
235
           port2: node2 port number (optional)
236
           returns: intf1 name, intf2 name"""
237
        node1 = self
238
        if port1 is None:
239
            port1 = node1.newPort()
240
        if port2 is None:
241
            port2 = node2.newPort()
242
        intf1 = node1.intfName( port1 )
243
        intf2 = node2.intfName( port2 )
244
        makeIntfPair( intf1, intf2 )
245
        node1.addIntf( intf1, port1 )
246
        node2.addIntf( intf2, port2 )
247
        node1.registerIntf( intf1, node2, intf2 )
248
        node2.registerIntf( intf2, node1, intf1 )
249
        return intf1, intf2
250
251 086ef80e Bob Lantz
    def deleteIntfs( self ):
252
        "Delete all of our interfaces."
253
        # In theory the interfaces should go away after we shut down.
254
        # However, this takes time, so we're better off removing them
255
        # explicitly so that we won't get errors if we run before they
256 d44a5843 Bob Lantz
        # have been removed by the kernel. Unfortunately this is very slow,
257
        # at least with Linux kernels before 2.6.33
258 086ef80e Bob Lantz
        for intf in self.intfs.values():
259
            quietRun( 'ip link del ' + intf )
260
            info( '.' )
261
            # Does it help to sleep to let things run?
262
            sleep( 0.001 )
263
264 80a8fa62 Bob Lantz
    def setMAC( self, intf, mac ):
265
        """Set the MAC address for an interface.
266 019bff82 Bob Lantz
           mac: MAC address as string"""
267 80a8fa62 Bob Lantz
        result = self.cmd( [ 'ifconfig', intf, 'down' ] )
268 019bff82 Bob Lantz
        result += self.cmd( [ 'ifconfig', intf, 'hw', 'ether', mac ] )
269 80a8fa62 Bob Lantz
        result += self.cmd( [ 'ifconfig', intf, 'up' ] )
270 376bcba4 Brandon Heller
        return result
271
272 80a8fa62 Bob Lantz
    def setARP( self, ip, mac ):
273
        """Add an ARP entry.
274 019bff82 Bob Lantz
           ip: IP address as string
275
           mac: MAC address as string"""
276 80a8fa62 Bob Lantz
        result = self.cmd( [ 'arp', '-s', ip, mac ] )
277 376bcba4 Brandon Heller
        return result
278
279 d44a5843 Bob Lantz
    def setIP( self, intf, ip, prefixLen=8 ):
280 80a8fa62 Bob Lantz
        """Set the IP address for an interface.
281 efc9a01c Bob Lantz
           intf: interface name
282 80a8fa62 Bob Lantz
           ip: IP address as a string
283 086ef80e Bob Lantz
           prefixLen: prefix length, e.g. 8 for /8 or 16M addrs"""
284
        ipSub = '%s/%d' % ( ip, prefixLen )
285
        result = self.cmd( [ 'ifconfig', intf, ipSub, 'up' ] )
286 80a8fa62 Bob Lantz
        self.ips[ intf ] = ip
287 89bf3103 Brandon Heller
        return result
288
289 80a8fa62 Bob Lantz
    def setHostRoute( self, ip, intf ):
290
        """Add route to host.
291
           ip: IP address as dotted decimal
292
           intf: string, interface name"""
293
        return self.cmd( 'route add -host ' + ip + ' dev ' + intf )
294 89bf3103 Brandon Heller
295 80a8fa62 Bob Lantz
    def setDefaultRoute( self, intf ):
296
        """Set the default route to go through intf.
297
           intf: string, interface name"""
298
        self.cmd( 'ip route flush' )
299
        return self.cmd( 'route add default ' + intf )
300 89bf3103 Brandon Heller
301 d44a5843 Bob Lantz
    def IP( self, intf=None ):
302
        "Return IP address of a node or specific interface."
303
        if len( self.ips ) == 1:
304
            return self.ips.values()[ 0 ]
305
        if intf:
306
            return self.ips.get( intf, None )
307
308
    def MAC( self, intf=None ):
309
        "Return MAC address of a node or specific interface."
310
        if intf is None and len( self.intfs ) == 1:
311
            intf = self.intfs.values()[ 0 ]
312
        ifconfig = self.cmd( 'ifconfig ' + intf )
313 75d72d96 Bob Lantz
        macs = re.findall( '..:..:..:..:..:..', ifconfig )
314
        if len( macs ) > 0:
315
            return macs[ 0 ]
316 6f45478f Bob Lantz
317 d44a5843 Bob Lantz
    def intfIsUp( self, intf ):
318
        "Check if an interface is up."
319
        return 'UP' in self.cmd( 'ifconfig ' + intf )
320 89bf3103 Brandon Heller
321
    # Other methods
322 80a8fa62 Bob Lantz
    def __str__( self ):
323 89bf3103 Brandon Heller
        result = self.name + ':'
324 dc630c54 Bob Lantz
        result += ' IP=' + str( self.IP() )
325 efc9a01c Bob Lantz
        result += ' intfs=' + ','.join( sorted( self.intfs.values() ) )
326 dc630c54 Bob Lantz
        result += ' waiting=' + str( self.waiting )
327 89bf3103 Brandon Heller
        return result
328
329
330 80a8fa62 Bob Lantz
class Host( Node ):
331
    "A host is simply a Node."
332 89bf3103 Brandon Heller
    pass
333
334
335 80a8fa62 Bob Lantz
class Switch( Node ):
336 6d72a3cb Bob Lantz
    """A Switch is a Node that is running (or has execed?)
337 80a8fa62 Bob Lantz
       an OpenFlow switch."""
338 89bf3103 Brandon Heller
339 80a8fa62 Bob Lantz
    def sendCmd( self, cmd ):
340
        """Send command to Node.
341
           cmd: string"""
342 1bb4412f Brandon Heller
        if not self.execed:
343 80a8fa62 Bob Lantz
            return Node.sendCmd( self, cmd )
344 1bb4412f Brandon Heller
        else:
345 6d72a3cb Bob Lantz
            error( '*** Error: %s has execed and cannot accept commands' %
346 80a8fa62 Bob Lantz
                     self.name )
347 89bf3103 Brandon Heller
348 80a8fa62 Bob Lantz
    def monitor( self ):
349
        "Monitor node."
350 1bb4412f Brandon Heller
        if not self.execed:
351 80a8fa62 Bob Lantz
            return Node.monitor( self )
352 1bb4412f Brandon Heller
        else:
353
            return True, ''
354 89bf3103 Brandon Heller
355 723d068c Brandon Heller
356 80a8fa62 Bob Lantz
class UserSwitch( Switch ):
357 d44a5843 Bob Lantz
    "User-space switch."
358 89bf3103 Brandon Heller
359 086ef80e Bob Lantz
    def __init__( self, name, *args, **kwargs ):
360 80a8fa62 Bob Lantz
        """Init.
361
           name: name for the switch"""
362 d44a5843 Bob Lantz
        Switch.__init__( self, name, **kwargs )
363 89bf3103 Brandon Heller
364 80a8fa62 Bob Lantz
    def start( self, controllers ):
365
        """Start OpenFlow reference user datapath.
366 6d72a3cb Bob Lantz
           Log to /tmp/sN-{ofd,ofp}.log.
367 efc9a01c Bob Lantz
           controllers: list of controller objects"""
368 019bff82 Bob Lantz
        controller = controllers[ 0 ]
369 89bf3103 Brandon Heller
        ofdlog = '/tmp/' + self.name + '-ofd.log'
370
        ofplog = '/tmp/' + self.name + '-ofp.log'
371 80a8fa62 Bob Lantz
        self.cmd( 'ifconfig lo up' )
372 efc9a01c Bob Lantz
        intfs = sorted( self.intfs.values() )
373 d44a5843 Bob Lantz
        if self.inNamespace:
374
            intfs = intfs[ :-1 ]
375 efc9a01c Bob Lantz
        self.cmd( 'ofdatapath -i ' + ','.join( intfs ) +
376 6d72a3cb Bob Lantz
            ' punix:/tmp/' + self.name +
377
            ' 1> ' + ofdlog + ' 2> ' + ofdlog + ' &' )
378 efc9a01c Bob Lantz
        self.cmd( 'ofprotocol unix:/tmp/' + self.name +
379 6d72a3cb Bob Lantz
            ' tcp:' + controller.IP() + ' --fail=closed' +
380
            ' 1> ' + ofplog + ' 2>' + ofplog + ' &' )
381 89bf3103 Brandon Heller
382 80a8fa62 Bob Lantz
    def stop( self ):
383
        "Stop OpenFlow reference user datapath."
384
        self.cmd( 'kill %ofdatapath' )
385
        self.cmd( 'kill %ofprotocol' )
386 086ef80e Bob Lantz
        self.deleteIntfs()
387 1bb4412f Brandon Heller
388 80a8fa62 Bob Lantz
class KernelSwitch( Switch ):
389
    """Kernel-space switch.
390 d44a5843 Bob Lantz
       Currently only works in root namespace."""
391 723d068c Brandon Heller
392 086ef80e Bob Lantz
    def __init__( self, name, dp=None, **kwargs ):
393 80a8fa62 Bob Lantz
        """Init.
394 e3a2ef01 Bob Lantz
           name: name for switch
395 6d72a3cb Bob Lantz
           dp: netlink id (0, 1, 2, ...)
396 086ef80e Bob Lantz
           defaultMAC: default MAC as string; random value if None"""
397 d44a5843 Bob Lantz
        Switch.__init__( self, name, **kwargs )
398 54037995 Brandon Heller
        self.dp = dp
399 d44a5843 Bob Lantz
        if self.inNamespace:
400
            error( "KernelSwitch currently only works"
401
                " in the root namespace." )
402
            exit( 1 )
403 1bb4412f Brandon Heller
404 80a8fa62 Bob Lantz
    def start( self, controllers ):
405
        "Start up reference kernel datapath."
406 89bf3103 Brandon Heller
        ofplog = '/tmp/' + self.name + '-ofp.log'
407 80a8fa62 Bob Lantz
        quietRun( 'ifconfig lo up' )
408 89bf3103 Brandon Heller
        # Delete local datapath if it exists;
409
        # then create a new one monitoring the given interfaces
410 80a8fa62 Bob Lantz
        quietRun( 'dpctl deldp nl:%i' % self.dp )
411 efc9a01c Bob Lantz
        self.cmd( 'dpctl adddp nl:%i' % self.dp )
412 086ef80e Bob Lantz
        if self.defaultMAC:
413 54037995 Brandon Heller
            intf = 'of%i' % self.dp
414 086ef80e Bob Lantz
            self.cmd( [ 'ifconfig', intf, 'hw', 'ether', self.defaultMAC ] )
415 efc9a01c Bob Lantz
        if len( self.intfs ) != max( self.intfs ) + 1:
416 80a8fa62 Bob Lantz
            raise Exception( 'only contiguous, zero-indexed port ranges'
417 efc9a01c Bob Lantz
                            'supported: %s' % self.intfs )
418
        intfs = [ self.intfs[ port ] for port in sorted( self.intfs.keys() ) ]
419
        self.cmd( 'dpctl addif nl:' + str( self.dp ) + ' ' +
420 80a8fa62 Bob Lantz
            ' '.join( intfs ) )
421 89bf3103 Brandon Heller
        # Run protocol daemon
422 019bff82 Bob Lantz
        controller = controllers[ 0 ]
423 efc9a01c Bob Lantz
        self.cmd( 'ofprotocol nl:' + str( self.dp ) + ' tcp:' +
424 019bff82 Bob Lantz
                      controller.IP() + ':' +
425
                      str( controller.port ) +
426 80a8fa62 Bob Lantz
                      ' --fail=closed 1> ' + ofplog + ' 2>' + ofplog + ' &' )
427 723d068c Brandon Heller
        self.execed = False
428 89bf3103 Brandon Heller
429 80a8fa62 Bob Lantz
    def stop( self ):
430
        "Terminate kernel datapath."
431
        quietRun( 'dpctl deldp nl:%i' % self.dp )
432 75d72d96 Bob Lantz
        self.cmd( 'kill %ofprotocol' )
433 086ef80e Bob Lantz
        self.deleteIntfs()
434 f7c2df25 Brandon Heller
435 80a8fa62 Bob Lantz
class OVSKernelSwitch( Switch ):
436
    """Open VSwitch kernel-space switch.
437
       Currently only works in the root namespace."""
438 f7c2df25 Brandon Heller
439 086ef80e Bob Lantz
    def __init__( self, name, dp=None, **kwargs ):
440 80a8fa62 Bob Lantz
        """Init.
441 e3a2ef01 Bob Lantz
           name: name for switch
442 6d72a3cb Bob Lantz
           dp: netlink id (0, 1, 2, ...)
443 d44a5843 Bob Lantz
           defaultMAC: default MAC as unsigned int; random value if None"""
444
        Switch.__init__( self, name, **kwargs )
445 f7c2df25 Brandon Heller
        self.dp = dp
446 d44a5843 Bob Lantz
        if self.inNamespace:
447
            error( "OVSKernelSwitch currently only works"
448
                " in the root namespace." )
449
            exit( 1 )
450 f7c2df25 Brandon Heller
451 80a8fa62 Bob Lantz
    def start( self, controllers ):
452
        "Start up kernel datapath."
453 f7c2df25 Brandon Heller
        ofplog = '/tmp/' + self.name + '-ofp.log'
454 80a8fa62 Bob Lantz
        quietRun( 'ifconfig lo up' )
455 f7c2df25 Brandon Heller
        # Delete local datapath if it exists;
456
        # then create a new one monitoring the given interfaces
457 80a8fa62 Bob Lantz
        quietRun( 'ovs-dpctl del-dp dp%i' % self.dp )
458 efc9a01c Bob Lantz
        self.cmd( 'ovs-dpctl add-dp dp%i' % self.dp )
459 086ef80e Bob Lantz
        if self.defaultMAC:
460 44c63c2a Brandon Heller
            intf = 'dp%i' % self.dp
461 086ef80e Bob Lantz
            mac = self.defaultMAC
462 019bff82 Bob Lantz
            self.cmd( [ 'ifconfig', intf, 'hw', 'ether', mac ] )
463 80a8fa62 Bob Lantz
464 efc9a01c Bob Lantz
        if len( self.intfs ) != max( self.intfs ) + 1:
465 80a8fa62 Bob Lantz
            raise Exception( 'only contiguous, zero-indexed port ranges'
466 efc9a01c Bob Lantz
                            'supported: %s' % self.intfs )
467
        intfs = [ self.intfs[ port ] for port in sorted( self.intfs.keys() ) ]
468
        self.cmd( 'ovs-dpctl add-if dp' + str( self.dp ) + ' ' +
469 80a8fa62 Bob Lantz
                      ' '.join( intfs ) )
470 f7c2df25 Brandon Heller
        # Run protocol daemon
471 019bff82 Bob Lantz
        controller = controllers[ 0 ]
472 efc9a01c Bob Lantz
        self.cmd( 'ovs-openflowd dp' + str( self.dp ) + ' tcp:' +
473 019bff82 Bob Lantz
                      controller.IP() + ':' +
474 80a8fa62 Bob Lantz
                      ' --fail=closed 1> ' + ofplog + ' 2>' + ofplog + ' &' )
475 f7c2df25 Brandon Heller
        self.execed = False
476
477 80a8fa62 Bob Lantz
    def stop( self ):
478
        "Terminate kernel datapath."
479
        quietRun( 'ovs-dpctl del-dp dp%i' % self.dp )
480 75d72d96 Bob Lantz
        self.cmd( 'kill %ovs-openflowd' )
481 086ef80e Bob Lantz
        self.deleteIntfs()
482 f7c2df25 Brandon Heller
483
484 80a8fa62 Bob Lantz
class Controller( Node ):
485 7c371cf3 Bob Lantz
    """A Controller is a Node that is running (or has execed?) an
486 80a8fa62 Bob Lantz
       OpenFlow controller."""
487 1bb4412f Brandon Heller
488 80a8fa62 Bob Lantz
    def __init__( self, name, inNamespace=False, controller='controller',
489 086ef80e Bob Lantz
                 cargs='-v ptcp:', cdir=None, defaultIP="127.0.0.1",
490 80a8fa62 Bob Lantz
                 port=6633 ):
491 1bb4412f Brandon Heller
        self.controller = controller
492
        self.cargs = cargs
493
        self.cdir = cdir
494 60d9ead6 David Erickson
        self.port = port
495 086ef80e Bob Lantz
        Node.__init__( self, name, inNamespace=inNamespace,
496
            defaultIP=defaultIP )
497 1bb4412f Brandon Heller
498 80a8fa62 Bob Lantz
    def start( self ):
499
        """Start <controller> <args> on controller.
500
           Log to /tmp/cN.log"""
501 1bb4412f Brandon Heller
        cout = '/tmp/' + self.name + '.log'
502
        if self.cdir is not None:
503 efc9a01c Bob Lantz
            self.cmd( 'cd ' + self.cdir )
504
        self.cmd( self.controller + ' ' + self.cargs +
505 80a8fa62 Bob Lantz
            ' 1> ' + cout + ' 2> ' + cout + ' &' )
506 723d068c Brandon Heller
        self.execed = False
507 89bf3103 Brandon Heller
508 80a8fa62 Bob Lantz
    def stop( self ):
509
        "Stop controller."
510
        self.cmd( 'kill %' + self.controller )
511 1bb4412f Brandon Heller
        self.terminate()
512 89bf3103 Brandon Heller
513 d44a5843 Bob Lantz
    def IP( self, intf=None ):
514 80a8fa62 Bob Lantz
        "Return IP address of the Controller"
515 d44a5843 Bob Lantz
        ip = Node.IP( self, intf=intf )
516
        if ip is None:
517
            ip = self.defaultIP
518
        return ip
519 723d068c Brandon Heller
520 80a8fa62 Bob Lantz
class ControllerParams( object ):
521
    "Container for controller IP parameters."
522 89bf3103 Brandon Heller
523 086ef80e Bob Lantz
    def __init__( self, ip, prefixLen ):
524 80a8fa62 Bob Lantz
        """Init.
525 086ef80e Bob Lantz
           ip: string, controller IP address
526
           prefixLen: prefix length, e.g. 8 for /8, covering 16M"""
527 1bb4412f Brandon Heller
        self.ip = ip
528 086ef80e Bob Lantz
        self.prefixLen = prefixLen
529 80a8fa62 Bob Lantz
530
531
class NOX( Controller ):
532
    "Controller to run a NOX application."
533
534
    def __init__( self, name, inNamespace=False, noxArgs=None, **kwargs ):
535
        """Init.
536
           name: name to give controller
537
           noxArgs: list of args, or single arg, to pass to NOX"""
538
        if type( noxArgs ) != list:
539
            noxArgs = [ noxArgs ]
540
        if not noxArgs:
541
            noxArgs = [ 'packetdump' ]
542 137ec305 Bob Lantz
543 4f4f1dd2 Brandon Heller
        if 'NOX_CORE_DIR' not in os.environ:
544 137ec305 Bob Lantz
            exit( 'exiting; please set missing NOX_CORE_DIR env var' )
545 80a8fa62 Bob Lantz
        noxCoreDir = os.environ[ 'NOX_CORE_DIR' ]
546 4f4f1dd2 Brandon Heller
547 80a8fa62 Bob Lantz
        Controller.__init__( self, name,
548
            controller=noxCoreDir + '/nox_core',
549 6d72a3cb Bob Lantz
            cargs='--libdir=/usr/local/lib -v -i ptcp: ' +
550 80a8fa62 Bob Lantz
                    ' '.join( noxArgs ),
551
            cdir = noxCoreDir, **kwargs )
552
553
554
class RemoteController( Controller ):
555
    "Controller running outside of Mininet's control."
556
557 086ef80e Bob Lantz
    def __init__( self, name, inNamespace=False, defaultIP='127.0.0.1',
558 80a8fa62 Bob Lantz
                 port=6633 ):
559
        """Init.
560
           name: name to give controller
561 c8641d7d Brandon Heller
           defaultIP: the IP address where the remote controller is
562 80a8fa62 Bob Lantz
           listening
563
           port: the port where the remote controller is listening"""
564 086ef80e Bob Lantz
        Controller.__init__( self, name, defaultIP=defaultIP, port=port )
565 80a8fa62 Bob Lantz
566
    def start( self ):
567
        "Overridden to do nothing."
568 60d9ead6 David Erickson
        return
569
570 80a8fa62 Bob Lantz
    def stop( self ):
571
        "Overridden to do nothing."
572 60d9ead6 David Erickson
        return