Statistics
| Branch: | Tag: | Revision:

mininet / mininet / node.py @ efc9a01c

History | View | Annotate | Download (18 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 7d4b7b7f Bob Lantz
"""
37 89bf3103 Brandon Heller
38
from subprocess import Popen, PIPE, STDOUT
39 723d068c Brandon Heller
import os
40
import signal
41
import select
42 60d9ead6 David Erickson
43 efc9a01c Bob Lantz
from mininet.log import info, error, debug
44
from mininet.util import quietRun, moveIntf
45 89bf3103 Brandon Heller
46 723d068c Brandon Heller
47 80a8fa62 Bob Lantz
class Node( object ):
48
    """A virtual network node is simply a shell in a network namespace.
49
       We communicate with it using pipes."""
50 6d72a3cb Bob Lantz
51
    inToNode = {} # mapping of input fds to nodes
52
    outToNode = {} # mapping of output fds to nodes
53 89bf3103 Brandon Heller
54 80a8fa62 Bob Lantz
    def __init__( self, name, inNamespace=True ):
55 89bf3103 Brandon Heller
        self.name = name
56
        closeFds = False # speed vs. memory use
57 80a8fa62 Bob Lantz
        # xpgEcho is needed so we can echo our sentinel in sendCmd
58
        cmd = [ '/bin/bash', '-O', 'xpg_echo' ]
59 89bf3103 Brandon Heller
        self.inNamespace = inNamespace
60
        if self.inNamespace:
61 80a8fa62 Bob Lantz
            cmd = [ 'netns' ] + cmd
62
        self.shell = Popen( cmd, stdin=PIPE, stdout=PIPE, stderr=STDOUT,
63 281f6e59 Bob Lantz
            close_fds=closeFds )
64 89bf3103 Brandon Heller
        self.stdin = self.shell.stdin
65
        self.stdout = self.shell.stdout
66
        self.pollOut = select.poll()
67 80a8fa62 Bob Lantz
        self.pollOut.register( self.stdout )
68 89bf3103 Brandon Heller
        # Maintain mapping between file descriptors and nodes
69
        # This could be useful for monitoring multiple nodes
70
        # using select.poll()
71 80a8fa62 Bob Lantz
        self.outToNode[ self.stdout.fileno() ] = self
72
        self.inToNode[ self.stdin.fileno() ] = self
73 89bf3103 Brandon Heller
        self.pid = self.shell.pid
74
        self.intfCount = 0
75 efc9a01c Bob Lantz
        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 ?
78 60d9ead6 David Erickson
        self.ips = {} # dict of interfaces to ip addresses as strings
79 6d72a3cb Bob Lantz
        self.connection = {} # remote node connected to each interface
80 89bf3103 Brandon Heller
        self.waiting = False
81
        self.execed = False
82
83 6d72a3cb Bob Lantz
    @classmethod
84
    def fdToNode( cls, fd ):
85
        """Return node corresponding to given file descriptor.
86
           fd: file descriptor
87
           returns: node"""
88
        node = Node.outToNode.get( fd )
89
        return node or Node.inToNode.get( fd )
90 89bf3103 Brandon Heller
91 80a8fa62 Bob Lantz
    def cleanup( self ):
92
        "Help python collect its garbage."
93 89bf3103 Brandon Heller
        self.shell = None
94
95
    # Subshell I/O, commands and control
96 6d72a3cb Bob Lantz
    def read( self, bytes ):
97
        """Read from a node.
98
           bytes: maximum number of bytes to read"""
99
        return os.read( self.stdout.fileno(), bytes )
100 80a8fa62 Bob Lantz
101
    def write( self, data ):
102
        """Write data to node.
103
           data: string"""
104
        os.write( self.stdin.fileno(), data )
105
106
    def terminate( self ):
107 6d72a3cb Bob Lantz
        "Send kill signal to Node and clean up after it."
108 80a8fa62 Bob Lantz
        os.kill( self.pid, signal.SIGKILL )
109 89bf3103 Brandon Heller
        self.cleanup()
110
111 80a8fa62 Bob Lantz
    def stop( self ):
112
        "Stop node."
113 89bf3103 Brandon Heller
        self.terminate()
114
115 80a8fa62 Bob Lantz
    def waitReadable( self ):
116 6d72a3cb Bob Lantz
        "Wait until node's output is readable."
117 89bf3103 Brandon Heller
        self.pollOut.poll()
118
119 80a8fa62 Bob Lantz
    def sendCmd( self, cmd ):
120
        """Send a command, followed by a command to echo a sentinel,
121
           and return without waiting for the command to complete."""
122 89bf3103 Brandon Heller
        assert not self.waiting
123 80a8fa62 Bob Lantz
        if cmd[ -1 ] == '&':
124 89bf3103 Brandon Heller
            separator = '&'
125 80a8fa62 Bob Lantz
            cmd = cmd[ :-1 ]
126 89bf3103 Brandon Heller
        else:
127
            separator = ';'
128 80a8fa62 Bob Lantz
        if isinstance( cmd, list ):
129
            cmd = ' '.join( cmd )
130
        self.write( cmd + separator + ' echo -n "\\0177" \n' )
131 89bf3103 Brandon Heller
        self.waiting = True
132
133 80a8fa62 Bob Lantz
    def monitor( self ):
134 6d72a3cb Bob Lantz
        "Monitor the output of a command, returning (done?, data)."
135 89bf3103 Brandon Heller
        assert self.waiting
136
        self.waitReadable()
137 80a8fa62 Bob Lantz
        data = self.read( 1024 )
138
        if len( data ) > 0 and data[ -1 ] == chr( 0177 ):
139 89bf3103 Brandon Heller
            self.waiting = False
140 80a8fa62 Bob Lantz
            return True, data[ :-1 ]
141 89bf3103 Brandon Heller
        else:
142
            return False, data
143
144 80a8fa62 Bob Lantz
    def sendInt( self ):
145 6d72a3cb Bob Lantz
        "Send ^C, hopefully interrupting an interactive subprocess."
146 80a8fa62 Bob Lantz
        self.write( chr( 3 ) )
147 723d068c Brandon Heller
148 efc9a01c Bob Lantz
    def waitOutput( self, verbose=False ):
149 80a8fa62 Bob Lantz
        """Wait for a command to complete.
150 7c371cf3 Bob Lantz
           Completion is signaled by a sentinel character, ASCII(127)
151 80a8fa62 Bob Lantz
           appearing in the output stream.  Wait for the sentinel and return
152 efc9a01c Bob Lantz
           the output, including trailing newline.
153
           verbose: print output interactively"""
154
        log = info if verbose else debug
155 89bf3103 Brandon Heller
        assert self.waiting
156
        output = ''
157
        while True:
158
            self.waitReadable()
159 80a8fa62 Bob Lantz
            data = self.read( 1024 )
160
            if len( data ) > 0  and data[ -1 ] == chr( 0177 ):
161
                output += data[ :-1 ]
162 efc9a01c Bob Lantz
                log( output )
163 89bf3103 Brandon Heller
                break
164 723d068c Brandon Heller
            else:
165
                output += data
166 89bf3103 Brandon Heller
        self.waiting = False
167
        return output
168
169 efc9a01c Bob Lantz
    def cmd( self, cmd, verbose=False ):
170 80a8fa62 Bob Lantz
        """Send a command, wait for output, and return it.
171
           cmd: string"""
172 efc9a01c Bob Lantz
        log = info if verbose else debug
173
        log( '*** %s : %s', self.name, cmd )
174 80a8fa62 Bob Lantz
        self.sendCmd( cmd )
175 efc9a01c Bob Lantz
        return self.waitOutput( verbose )
176 89bf3103 Brandon Heller
177 80a8fa62 Bob Lantz
    def cmdPrint( self, cmd ):
178
        """Call cmd and printing its output
179
           cmd: string"""
180 efc9a01c Bob Lantz
        return self.cmd( cmd, verbose=True )
181 89bf3103 Brandon Heller
182
    # Interface management, configuration, and routing
183 efc9a01c Bob Lantz
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
190 80a8fa62 Bob Lantz
    def intfName( self, n ):
191 efc9a01c Bob Lantz
        "Construct a canonical interface name node-ethN for interface n."
192 80a8fa62 Bob Lantz
        return self.name + '-eth' + repr( n )
193 89bf3103 Brandon Heller
194 80a8fa62 Bob Lantz
    def newIntf( self ):
195
        "Reserve and return a new interface name."
196
        intfName = self.intfName( self.intfCount )
197 89bf3103 Brandon Heller
        self.intfCount += 1
198
        return intfName
199
200 efc9a01c Bob Lantz
    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
216 80a8fa62 Bob Lantz
    def setMAC( self, intf, mac ):
217
        """Set the MAC address for an interface.
218 019bff82 Bob Lantz
           mac: MAC address as string"""
219 80a8fa62 Bob Lantz
        result = self.cmd( [ 'ifconfig', intf, 'down' ] )
220 019bff82 Bob Lantz
        result += self.cmd( [ 'ifconfig', intf, 'hw', 'ether', mac ] )
221 80a8fa62 Bob Lantz
        result += self.cmd( [ 'ifconfig', intf, 'up' ] )
222 376bcba4 Brandon Heller
        return result
223
224 80a8fa62 Bob Lantz
    def setARP( self, ip, mac ):
225
        """Add an ARP entry.
226 019bff82 Bob Lantz
           ip: IP address as string
227
           mac: MAC address as string"""
228 80a8fa62 Bob Lantz
        result = self.cmd( [ 'arp', '-s', ip, mac ] )
229 376bcba4 Brandon Heller
        return result
230
231 80a8fa62 Bob Lantz
    def setIP( self, intf, ip, bits ):
232
        """Set the IP address for an interface.
233 efc9a01c Bob Lantz
           intf: interface name
234 80a8fa62 Bob Lantz
           ip: IP address as a string
235 efc9a01c Bob Lantz
           bits: prefix length of form /24"""
236 80a8fa62 Bob Lantz
        result = self.cmd( [ 'ifconfig', intf, ip + bits, 'up' ] )
237
        self.ips[ intf ] = ip
238 89bf3103 Brandon Heller
        return result
239
240 80a8fa62 Bob Lantz
    def setHostRoute( self, ip, intf ):
241
        """Add route to host.
242
           ip: IP address as dotted decimal
243
           intf: string, interface name"""
244
        return self.cmd( 'route add -host ' + ip + ' dev ' + intf )
245 89bf3103 Brandon Heller
246 80a8fa62 Bob Lantz
    def setDefaultRoute( self, intf ):
247
        """Set the default route to go through intf.
248
           intf: string, interface name"""
249
        self.cmd( 'ip route flush' )
250
        return self.cmd( 'route add default ' + intf )
251 89bf3103 Brandon Heller
252 80a8fa62 Bob Lantz
    def IP( self ):
253 efc9a01c Bob Lantz
        "Return IP address of interface 0"
254
        return self.ips.get( self.intfs.get( 0 , None ), None )
255 89bf3103 Brandon Heller
256 efc9a01c Bob Lantz
    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 ] )
260 89bf3103 Brandon Heller
261
    # Other methods
262 80a8fa62 Bob Lantz
    def __str__( self ):
263 89bf3103 Brandon Heller
        result = self.name + ':'
264 efc9a01c Bob Lantz
        result += ' IP=' + repr( self.IP() )
265
        result += ' intfs=' + ','.join( sorted( self.intfs.values() ) )
266 80a8fa62 Bob Lantz
        result += ' waiting=' + repr( self.waiting )
267 89bf3103 Brandon Heller
        return result
268
269
270 80a8fa62 Bob Lantz
class Host( Node ):
271
    "A host is simply a Node."
272 89bf3103 Brandon Heller
    pass
273
274
275 80a8fa62 Bob Lantz
class Switch( Node ):
276 6d72a3cb Bob Lantz
    """A Switch is a Node that is running (or has execed?)
277 80a8fa62 Bob Lantz
       an OpenFlow switch."""
278 89bf3103 Brandon Heller
279 80a8fa62 Bob Lantz
    def sendCmd( self, cmd ):
280
        """Send command to Node.
281
           cmd: string"""
282 1bb4412f Brandon Heller
        if not self.execed:
283 80a8fa62 Bob Lantz
            return Node.sendCmd( self, cmd )
284 1bb4412f Brandon Heller
        else:
285 6d72a3cb Bob Lantz
            error( '*** Error: %s has execed and cannot accept commands' %
286 80a8fa62 Bob Lantz
                     self.name )
287 89bf3103 Brandon Heller
288 80a8fa62 Bob Lantz
    def monitor( self ):
289
        "Monitor node."
290 1bb4412f Brandon Heller
        if not self.execed:
291 80a8fa62 Bob Lantz
            return Node.monitor( self )
292 1bb4412f Brandon Heller
        else:
293
            return True, ''
294 89bf3103 Brandon Heller
295 723d068c Brandon Heller
296 80a8fa62 Bob Lantz
class UserSwitch( Switch ):
297
    """User-space switch.
298
       Currently only works in the root namespace."""
299 89bf3103 Brandon Heller
300 80a8fa62 Bob Lantz
    def __init__( self, name ):
301
        """Init.
302
           name: name for the switch"""
303
        Switch.__init__( self, name, inNamespace=False )
304 89bf3103 Brandon Heller
305 80a8fa62 Bob Lantz
    def start( self, controllers ):
306
        """Start OpenFlow reference user datapath.
307 6d72a3cb Bob Lantz
           Log to /tmp/sN-{ofd,ofp}.log.
308 efc9a01c Bob Lantz
           controllers: list of controller objects"""
309 019bff82 Bob Lantz
        controller = controllers[ 0 ]
310 89bf3103 Brandon Heller
        ofdlog = '/tmp/' + self.name + '-ofd.log'
311
        ofplog = '/tmp/' + self.name + '-ofp.log'
312 80a8fa62 Bob Lantz
        self.cmd( 'ifconfig lo up' )
313 efc9a01c Bob Lantz
        intfs = sorted( self.intfs.values() )
314 6d72a3cb Bob Lantz
315 efc9a01c Bob Lantz
        self.cmd( 'ofdatapath -i ' + ','.join( intfs ) +
316 6d72a3cb Bob Lantz
            ' punix:/tmp/' + self.name +
317
            ' 1> ' + ofdlog + ' 2> ' + ofdlog + ' &' )
318 efc9a01c Bob Lantz
        self.cmd( 'ofprotocol unix:/tmp/' + self.name +
319 6d72a3cb Bob Lantz
            ' tcp:' + controller.IP() + ' --fail=closed' +
320
            ' 1> ' + ofplog + ' 2>' + ofplog + ' &' )
321 89bf3103 Brandon Heller
322 80a8fa62 Bob Lantz
    def stop( self ):
323
        "Stop OpenFlow reference user datapath."
324
        self.cmd( 'kill %ofdatapath' )
325
        self.cmd( 'kill %ofprotocol' )
326 89bf3103 Brandon Heller
327 1bb4412f Brandon Heller
328 80a8fa62 Bob Lantz
class KernelSwitch( Switch ):
329
    """Kernel-space switch.
330
       Currently only works in the root namespace."""
331 723d068c Brandon Heller
332 019bff82 Bob Lantz
    def __init__( self, name, dp=None, defaultMac=None ):
333 80a8fa62 Bob Lantz
        """Init.
334
           name:
335 6d72a3cb Bob Lantz
           dp: netlink id (0, 1, 2, ...)
336 019bff82 Bob Lantz
           defaultMac: default MAC as string; random value if None"""
337 80a8fa62 Bob Lantz
        Switch.__init__( self, name, inNamespace=False )
338 54037995 Brandon Heller
        self.dp = dp
339 019bff82 Bob Lantz
        self.defaultMac = defaultMac
340 1bb4412f Brandon Heller
341 80a8fa62 Bob Lantz
    def start( self, controllers ):
342
        "Start up reference kernel datapath."
343 89bf3103 Brandon Heller
        ofplog = '/tmp/' + self.name + '-ofp.log'
344 80a8fa62 Bob Lantz
        quietRun( 'ifconfig lo up' )
345 89bf3103 Brandon Heller
        # Delete local datapath if it exists;
346
        # then create a new one monitoring the given interfaces
347 80a8fa62 Bob Lantz
        quietRun( 'dpctl deldp nl:%i' % self.dp )
348 efc9a01c Bob Lantz
        self.cmd( 'dpctl adddp nl:%i' % self.dp )
349 019bff82 Bob Lantz
        if self.defaultMac:
350 54037995 Brandon Heller
            intf = 'of%i' % self.dp
351 019bff82 Bob Lantz
            self.cmd( [ 'ifconfig', intf, 'hw', 'ether', self.defaultMac ] )
352 80a8fa62 Bob Lantz
353 efc9a01c Bob Lantz
        if len( self.intfs ) != max( self.intfs ) + 1:
354 80a8fa62 Bob Lantz
            raise Exception( 'only contiguous, zero-indexed port ranges'
355 efc9a01c Bob Lantz
                            '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 ) + ' ' +
358 80a8fa62 Bob Lantz
            ' '.join( intfs ) )
359 89bf3103 Brandon Heller
        # Run protocol daemon
360 019bff82 Bob Lantz
        controller = controllers[ 0 ]
361 efc9a01c Bob Lantz
        self.cmd( 'ofprotocol nl:' + str( self.dp ) + ' tcp:' +
362 019bff82 Bob Lantz
                      controller.IP() + ':' +
363
                      str( controller.port ) +
364 80a8fa62 Bob Lantz
                      ' --fail=closed 1> ' + ofplog + ' 2>' + ofplog + ' &' )
365 723d068c Brandon Heller
        self.execed = False
366 89bf3103 Brandon Heller
367 80a8fa62 Bob Lantz
    def stop( self ):
368
        "Terminate kernel datapath."
369
        quietRun( 'dpctl deldp nl:%i' % self.dp )
370 89bf3103 Brandon Heller
        # In theory the interfaces should go away after we shut down.
371 efc9a01c Bob Lantz
        # However, this takes time, so we're better off removing them
372 89bf3103 Brandon Heller
        # explicitly so that we won't get errors if we run before they
373
        # have been removed by the kernel. Unfortunately this is very slow.
374 80a8fa62 Bob Lantz
        self.cmd( 'kill %ofprotocol' )
375 efc9a01c Bob Lantz
        for intf in self.intfs.values():
376 80a8fa62 Bob Lantz
            quietRun( 'ip link del ' + intf )
377 6d72a3cb Bob Lantz
            info( '.' )
378 89bf3103 Brandon Heller
379 f7c2df25 Brandon Heller
380 80a8fa62 Bob Lantz
class OVSKernelSwitch( Switch ):
381
    """Open VSwitch kernel-space switch.
382
       Currently only works in the root namespace."""
383 f7c2df25 Brandon Heller
384 019bff82 Bob Lantz
    def __init__( self, name, dp=None, defaultMac=None ):
385 80a8fa62 Bob Lantz
        """Init.
386
           name:
387 6d72a3cb Bob Lantz
           dp: netlink id (0, 1, 2, ...)
388 80a8fa62 Bob Lantz
           dpid: datapath ID as unsigned int; random value if None"""
389
        Switch.__init__( self, name, inNamespace=False )
390 f7c2df25 Brandon Heller
        self.dp = dp
391 019bff82 Bob Lantz
        self.defaultMac = defaultMac
392 f7c2df25 Brandon Heller
393 80a8fa62 Bob Lantz
    def start( self, controllers ):
394
        "Start up kernel datapath."
395 f7c2df25 Brandon Heller
        ofplog = '/tmp/' + self.name + '-ofp.log'
396 80a8fa62 Bob Lantz
        quietRun( 'ifconfig lo up' )
397 f7c2df25 Brandon Heller
        # Delete local datapath if it exists;
398
        # then create a new one monitoring the given interfaces
399 80a8fa62 Bob Lantz
        quietRun( 'ovs-dpctl del-dp dp%i' % self.dp )
400 efc9a01c Bob Lantz
        self.cmd( 'ovs-dpctl add-dp dp%i' % self.dp )
401 019bff82 Bob Lantz
        if self.defaultMac:
402 f7c2df25 Brandon Heller
            intf = 'dp' % self.dp
403 019bff82 Bob Lantz
            mac = self.defaultMac
404
            self.cmd( [ 'ifconfig', intf, 'hw', 'ether', mac ] )
405 80a8fa62 Bob Lantz
406 efc9a01c Bob Lantz
        if len( self.intfs ) != max( self.intfs ) + 1:
407 80a8fa62 Bob Lantz
            raise Exception( 'only contiguous, zero-indexed port ranges'
408 efc9a01c Bob Lantz
                            '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 ) + ' ' +
411 80a8fa62 Bob Lantz
                      ' '.join( intfs ) )
412 f7c2df25 Brandon Heller
        # Run protocol daemon
413 019bff82 Bob Lantz
        controller = controllers[ 0 ]
414 efc9a01c Bob Lantz
        self.cmd( 'ovs-openflowd dp' + str( self.dp ) + ' tcp:' +
415 019bff82 Bob Lantz
                      controller.IP() + ':' +
416 80a8fa62 Bob Lantz
                      ' --fail=closed 1> ' + ofplog + ' 2>' + ofplog + ' &' )
417 f7c2df25 Brandon Heller
        self.execed = False
418
419 80a8fa62 Bob Lantz
    def stop( self ):
420
        "Terminate kernel datapath."
421
        quietRun( 'ovs-dpctl del-dp dp%i' % self.dp )
422 f7c2df25 Brandon Heller
        # In theory the interfaces should go away after we shut down.
423 efc9a01c Bob Lantz
        # However, this takes time, so we're better off removing them
424 f7c2df25 Brandon Heller
        # explicitly so that we won't get errors if we run before they
425
        # have been removed by the kernel. Unfortunately this is very slow.
426 80a8fa62 Bob Lantz
        self.cmd( 'kill %ovs-openflowd' )
427 efc9a01c Bob Lantz
        for intf in self.intfs.values():
428 80a8fa62 Bob Lantz
            quietRun( 'ip link del ' + intf )
429 6d72a3cb Bob Lantz
            info( '.' )
430 f7c2df25 Brandon Heller
431
432 80a8fa62 Bob Lantz
class Controller( Node ):
433 7c371cf3 Bob Lantz
    """A Controller is a Node that is running (or has execed?) an
434 80a8fa62 Bob Lantz
       OpenFlow controller."""
435 1bb4412f Brandon Heller
436 80a8fa62 Bob Lantz
    def __init__( self, name, inNamespace=False, controller='controller',
437
                 cargs='-v ptcp:', cdir=None, ipAddress="127.0.0.1",
438
                 port=6633 ):
439 1bb4412f Brandon Heller
        self.controller = controller
440
        self.cargs = cargs
441
        self.cdir = cdir
442 80a8fa62 Bob Lantz
        self.ipAddress = ipAddress
443 60d9ead6 David Erickson
        self.port = port
444 80a8fa62 Bob Lantz
        Node.__init__( self, name, inNamespace=inNamespace )
445 1bb4412f Brandon Heller
446 80a8fa62 Bob Lantz
    def start( self ):
447
        """Start <controller> <args> on controller.
448
           Log to /tmp/cN.log"""
449 1bb4412f Brandon Heller
        cout = '/tmp/' + self.name + '.log'
450
        if self.cdir is not None:
451 efc9a01c Bob Lantz
            self.cmd( 'cd ' + self.cdir )
452
        self.cmd( self.controller + ' ' + self.cargs +
453 80a8fa62 Bob Lantz
            ' 1> ' + cout + ' 2> ' + cout + ' &' )
454 723d068c Brandon Heller
        self.execed = False
455 89bf3103 Brandon Heller
456 80a8fa62 Bob Lantz
    def stop( self ):
457
        "Stop controller."
458
        self.cmd( 'kill %' + self.controller )
459 1bb4412f Brandon Heller
        self.terminate()
460 89bf3103 Brandon Heller
461 80a8fa62 Bob Lantz
    def IP( self ):
462
        "Return IP address of the Controller"
463
        return self.ipAddress
464 89bf3103 Brandon Heller
465 723d068c Brandon Heller
466 80a8fa62 Bob Lantz
class ControllerParams( object ):
467
    "Container for controller IP parameters."
468 89bf3103 Brandon Heller
469 80a8fa62 Bob Lantz
    def __init__( self, ip, subnetSize ):
470
        """Init.
471
           ip: integer, controller IP
472
            subnetSize: integer, ex 8 for slash-8, covering 17M"""
473 1bb4412f Brandon Heller
        self.ip = ip
474 80a8fa62 Bob Lantz
        self.subnetSize = subnetSize
475
476
477
class NOX( Controller ):
478
    "Controller to run a NOX application."
479
480
    def __init__( self, name, inNamespace=False, noxArgs=None, **kwargs ):
481
        """Init.
482
           name: name to give controller
483
           noxArgs: list of args, or single arg, to pass to NOX"""
484
        if type( noxArgs ) != list:
485
            noxArgs = [ noxArgs ]
486
        if not noxArgs:
487
            noxArgs = [ 'packetdump' ]
488 137ec305 Bob Lantz
489 4f4f1dd2 Brandon Heller
        if 'NOX_CORE_DIR' not in os.environ:
490 137ec305 Bob Lantz
            exit( 'exiting; please set missing NOX_CORE_DIR env var' )
491 80a8fa62 Bob Lantz
        noxCoreDir = os.environ[ 'NOX_CORE_DIR' ]
492 4f4f1dd2 Brandon Heller
493 80a8fa62 Bob Lantz
        Controller.__init__( self, name,
494
            controller=noxCoreDir + '/nox_core',
495 6d72a3cb Bob Lantz
            cargs='--libdir=/usr/local/lib -v -i ptcp: ' +
496 80a8fa62 Bob Lantz
                    ' '.join( noxArgs ),
497
            cdir = noxCoreDir, **kwargs )
498
499
500
class RemoteController( Controller ):
501
    "Controller running outside of Mininet's control."
502
503
    def __init__( self, name, inNamespace=False, ipAddress='127.0.0.1',
504
                 port=6633 ):
505
        """Init.
506
           name: name to give controller
507
           ipAddress: the IP address where the remote controller is
508
           listening
509
           port: the port where the remote controller is listening"""
510
        Controller.__init__( self, name, ipAddress=ipAddress, port=port )
511
512
    def start( self ):
513
        "Overridden to do nothing."
514 60d9ead6 David Erickson
        return
515
516 80a8fa62 Bob Lantz
    def stop( self ):
517
        "Overridden to do nothing."
518 60d9ead6 David Erickson
        return