Statistics
| Branch: | Tag: | Revision:

mininet / mininet / node.py @ c6e7eaf0

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