Statistics
| Branch: | Tag: | Revision:

mininet / mininet / node.py @ 149a1f56

History | View | Annotate | Download (35.7 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 0dbfd3a6 Bob Lantz
CPULimitedHost: a virtual host whose CPU bandwidth is limited by
17
    RT or CFS bandwidth limiting.
18

19 7d4b7b7f Bob Lantz
Switch: superclass for switch nodes.
20

21
UserSwitch: a switch using the user-space switch from the OpenFlow
22
    reference implementation.
23

24
KernelSwitch: a switch using the kernel switch from the OpenFlow reference
25
    implementation.
26

27
OVSSwitch: a switch using the OpenVSwitch OpenFlow-compatible switch
28
    implementation (openvswitch.org).
29

30
Controller: superclass for OpenFlow controllers. The default controller
31
    is controller(8) from the reference implementation.
32

33
NOXController: a controller node using NOX (noxrepo.org).
34

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

39 80be5642 Bob Lantz
Future enhancements:
40

41
- Possibly make Node, Switch and Controller more abstract so that
42
  they can be used for both local and remote nodes
43

44
- Create proxy objects for remote nodes (Mininet: Cluster Edition)
45 7d4b7b7f Bob Lantz
"""
46 89bf3103 Brandon Heller
47 723d068c Brandon Heller
import os
48 75d72d96 Bob Lantz
import re
49 723d068c Brandon Heller
import signal
50
import select
51 75d72d96 Bob Lantz
from subprocess import Popen, PIPE, STDOUT
52 60d9ead6 David Erickson
53 2db4268b Bob Lantz
from mininet.log import info, error, warn, debug
54 197b083f Bob Lantz
from mininet.util import ( quietRun, errRun, errFail, moveIntf, isShellBuiltin,
55
                          numCores, retry, mountCgroups )
56 f0010171 Bob Lantz
from mininet.moduledeps import moduleDeps, pathCheck, OVS_KMOD, OF_KMOD, TUN
57 1aec55d9 Bob Lantz
from mininet.link import Link, Intf, TCIntf
58 723d068c Brandon Heller
59 80a8fa62 Bob Lantz
class Node( object ):
60
    """A virtual network node is simply a shell in a network namespace.
61
       We communicate with it using pipes."""
62 6d72a3cb Bob Lantz
63 dd159b4a Bob Lantz
    portBase = 0  # Nodes always start with eth0/port0, even in OF 1.0
64 54d026f6 Bob Lantz
65 84a91a14 Bob Lantz
    def __init__( self, name, inNamespace=True, **params ):
66 086ef80e Bob Lantz
        """name: name of node
67
           inNamespace: in network namespace?
68 84a91a14 Bob Lantz
           params: Node parameters (see config() for details)"""
69
70
        # Make sure class actually works
71
        self.checkSetup()
72
73 89bf3103 Brandon Heller
        self.name = name
74
        self.inNamespace = inNamespace
75 84a91a14 Bob Lantz
76
        # Stash configuration parameters for future reference
77
        self.params = params
78
79
        self.intfs = {}  # dict of port numbers to interfaces
80
        self.ports = {}  # dict of interfaces to port numbers
81
                         # replace with Port objects, eventually ?
82
        self.nameToIntf = {}  # dict of interface names to Intfs
83
84 14ff3ad3 Bob Lantz
        # Make pylint happy
85
        ( self.shell, self.execed, self.pid, self.stdin, self.stdout,
86
          self.lastPid, self.lastCmd, self.pollOut ) = (
87
            None, None, None, None, None, None, None, None )
88
        self.waiting = False
89
        self.readbuf = ''
90
91 84a91a14 Bob Lantz
        # Start command interpreter shell
92
        self.startShell()
93
94
    # File descriptor to node mapping support
95
    # Class variables and methods
96
97
    inToNode = {}  # mapping of input fds to nodes
98
    outToNode = {}  # mapping of output fds to nodes
99
100
    @classmethod
101
    def fdToNode( cls, fd ):
102
        """Return node corresponding to given file descriptor.
103
           fd: file descriptor
104
           returns: node"""
105
        node = cls.outToNode.get( fd )
106
        return node or cls.inToNode.get( fd )
107
108
    # Command support via shell process in namespace
109
110
    def startShell( self ):
111
        "Start a shell process for running commands"
112
        if self.shell:
113
            error( "%s: shell is already running" )
114
            return
115 216a4b7c Bob Lantz
        # mnexec: (c)lose descriptors, (d)etach from tty,
116 14ff3ad3 Bob Lantz
        # (p)rint pid, and run in (n)amespace
117 121eb449 Bob Lantz
        opts = '-cdp'
118 89bf3103 Brandon Heller
        if self.inNamespace:
119 b14b1ee4 Bob Lantz
            opts += 'n'
120 216a4b7c Bob Lantz
        # bash -m: enable job control
121 47acf539 Bob Lantz
        cmd = [ 'mnexec', opts, 'bash', '-m' ]
122 80a8fa62 Bob Lantz
        self.shell = Popen( cmd, stdin=PIPE, stdout=PIPE, stderr=STDOUT,
123 a6bcad8f Bob Lantz
            close_fds=True )
124 89bf3103 Brandon Heller
        self.stdin = self.shell.stdin
125
        self.stdout = self.shell.stdout
126 121eb449 Bob Lantz
        self.pid = self.shell.pid
127 89bf3103 Brandon Heller
        self.pollOut = select.poll()
128 80a8fa62 Bob Lantz
        self.pollOut.register( self.stdout )
129 89bf3103 Brandon Heller
        # Maintain mapping between file descriptors and nodes
130 bcacfc05 Bob Lantz
        # This is useful for monitoring multiple nodes
131 89bf3103 Brandon Heller
        # using select.poll()
132 80a8fa62 Bob Lantz
        self.outToNode[ self.stdout.fileno() ] = self
133
        self.inToNode[ self.stdin.fileno() ] = self
134 89bf3103 Brandon Heller
        self.execed = False
135 bcacfc05 Bob Lantz
        self.lastCmd = None
136
        self.lastPid = None
137 ec7b211c Bob Lantz
        self.readbuf = ''
138 bcacfc05 Bob Lantz
        self.waiting = False
139 89bf3103 Brandon Heller
140 14ff3ad3 Bob Lantz
    def cleanup( self ):
141
        "Help python collect its garbage."
142 28833d86 Bob Lantz
        if not self.inNamespace:
143
            for intfName in self.intfNames():
144
                if self.name in intfName:
145
                    quietRun( 'ip link del ' + intfName )
146 14ff3ad3 Bob Lantz
        self.shell = None
147
148
    def read( self, maxbytes=1024 ):
149 ec7b211c Bob Lantz
        """Buffered read from node, non-blocking.
150 14ff3ad3 Bob Lantz
           maxbytes: maximum number of bytes to return"""
151 ec7b211c Bob Lantz
        count = len( self.readbuf )
152 14ff3ad3 Bob Lantz
        if count < maxbytes:
153
            data = os.read( self.stdout.fileno(), maxbytes - count )
154 ec7b211c Bob Lantz
            self.readbuf += data
155 14ff3ad3 Bob Lantz
        if maxbytes >= len( self.readbuf ):
156 ec7b211c Bob Lantz
            result = self.readbuf
157
            self.readbuf = ''
158
        else:
159 14ff3ad3 Bob Lantz
            result = self.readbuf[ :maxbytes ]
160
            self.readbuf = self.readbuf[ maxbytes: ]
161 ec7b211c Bob Lantz
        return result
162
163
    def readline( self ):
164
        """Buffered readline from node, non-blocking.
165
           returns: line (minus newline) or None"""
166
        self.readbuf += self.read( 1024 )
167
        if '\n' not in self.readbuf:
168
            return None
169
        pos = self.readbuf.find( '\n' )
170
        line = self.readbuf[ 0 : pos ]
171
        self.readbuf = self.readbuf[ pos + 1: ]
172
        return line
173 80a8fa62 Bob Lantz
174
    def write( self, data ):
175
        """Write data to node.
176
           data: string"""
177
        os.write( self.stdin.fileno(), data )
178
179
    def terminate( self ):
180 6d72a3cb Bob Lantz
        "Send kill signal to Node and clean up after it."
181 80a8fa62 Bob Lantz
        os.kill( self.pid, signal.SIGKILL )
182 89bf3103 Brandon Heller
        self.cleanup()
183
184 80a8fa62 Bob Lantz
    def stop( self ):
185
        "Stop node."
186 89bf3103 Brandon Heller
        self.terminate()
187
188 f24e70a4 Bob Lantz
    def waitReadable( self, timeoutms=None ):
189
        """Wait until node's output is readable.
190
           timeoutms: timeout in ms or None to wait indefinitely."""
191 ec7b211c Bob Lantz
        if len( self.readbuf ) == 0:
192 f24e70a4 Bob Lantz
            self.pollOut.poll( timeoutms )
193 89bf3103 Brandon Heller
194 121eb449 Bob Lantz
    def sendCmd( self, *args, **kwargs ):
195 80a8fa62 Bob Lantz
        """Send a command, followed by a command to echo a sentinel,
196 121eb449 Bob Lantz
           and return without waiting for the command to complete.
197
           args: command and arguments, or string
198
           printPid: print command's PID?"""
199 89bf3103 Brandon Heller
        assert not self.waiting
200 121eb449 Bob Lantz
        printPid = kwargs.get( 'printPid', True )
201 318ae55e Bob Lantz
        # Allow sendCmd( [ list ] )
202
        if len( args ) == 1 and type( args[ 0 ] ) is list:
203
            cmd = args[ 0 ]
204
        # Allow sendCmd( cmd, arg1, arg2... )
205
        elif len( args ) > 0:
206 121eb449 Bob Lantz
            cmd = args
207 318ae55e Bob Lantz
        # Convert to string
208 121eb449 Bob Lantz
        if not isinstance( cmd, str ):
209 a6bcad8f Bob Lantz
            cmd = ' '.join( [ str( c ) for c in cmd ] )
210 f24e70a4 Bob Lantz
        if not re.search( r'\w', cmd ):
211
            # Replace empty commands with something harmless
212
            cmd = 'echo -n'
213 d1b29d58 Bob Lantz
        self.lastCmd = cmd
214
        printPid = printPid and not isShellBuiltin( cmd )
215 c6e7eaf0 Bob Lantz
        if len( cmd ) > 0 and cmd[ -1 ] == '&':
216 d1b29d58 Bob Lantz
            # print ^A{pid}\n{sentinel}
217
            cmd += ' printf "\\001%d\n\\177" $! \n'
218 89bf3103 Brandon Heller
        else:
219 d1b29d58 Bob Lantz
            # print sentinel
220
            cmd += '; printf "\\177"'
221 bcacfc05 Bob Lantz
            if printPid and not isShellBuiltin( cmd ):
222 612b21cb Bob Lantz
                cmd = 'mnexec -p ' + cmd
223
        self.write( cmd + '\n' )
224 bcacfc05 Bob Lantz
        self.lastPid = None
225 89bf3103 Brandon Heller
        self.waiting = True
226
227 ec7b211c Bob Lantz
    def sendInt( self, sig=signal.SIGINT ):
228 bcacfc05 Bob Lantz
        "Interrupt running command."
229
        if self.lastPid:
230 b5672f15 Bob Lantz
            try:
231
                os.kill( self.lastPid, sig )
232 82b72072 Bob Lantz
            except OSError:
233 b5672f15 Bob Lantz
                pass
234 4065511a Bob Lantz
235 f24e70a4 Bob Lantz
    def monitor( self, timeoutms=None ):
236 e4c82e52 Bob Lantz
        """Monitor and return the output of a command.
237 f24e70a4 Bob Lantz
           Set self.waiting to False if command has completed.
238
           timeoutms: timeout in ms or None to wait indefinitely."""
239
        self.waitReadable( timeoutms )
240 80a8fa62 Bob Lantz
        data = self.read( 1024 )
241 bcacfc05 Bob Lantz
        # Look for PID
242
        marker = chr( 1 ) + r'\d+\n'
243
        if chr( 1 ) in data:
244
            markers = re.findall( marker, data )
245
            if markers:
246
                self.lastPid = int( markers[ 0 ][ 1: ] )
247
                data = re.sub( marker, '', data )
248
        # Look for sentinel/EOF
249 4065511a Bob Lantz
        if len( data ) > 0 and data[ -1 ] == chr( 127 ):
250 89bf3103 Brandon Heller
            self.waiting = False
251 e4c82e52 Bob Lantz
            data = data[ :-1 ]
252 4065511a Bob Lantz
        elif chr( 127 ) in data:
253 bcacfc05 Bob Lantz
            self.waiting = False
254 e4c82e52 Bob Lantz
            data = data.replace( chr( 127 ), '' )
255
        return data
256 723d068c Brandon Heller
257 efc9a01c Bob Lantz
    def waitOutput( self, verbose=False ):
258 80a8fa62 Bob Lantz
        """Wait for a command to complete.
259 7c371cf3 Bob Lantz
           Completion is signaled by a sentinel character, ASCII(127)
260 80a8fa62 Bob Lantz
           appearing in the output stream.  Wait for the sentinel and return
261 efc9a01c Bob Lantz
           the output, including trailing newline.
262
           verbose: print output interactively"""
263
        log = info if verbose else debug
264 89bf3103 Brandon Heller
        output = ''
265 e4c82e52 Bob Lantz
        while self.waiting:
266
            data = self.monitor()
267 4065511a Bob Lantz
            output += data
268
            log( data )
269 89bf3103 Brandon Heller
        return output
270
271 121eb449 Bob Lantz
    def cmd( self, *args, **kwargs ):
272 80a8fa62 Bob Lantz
        """Send a command, wait for output, and return it.
273
           cmd: string"""
274 121eb449 Bob Lantz
        verbose = kwargs.get( 'verbose', False )
275 efc9a01c Bob Lantz
        log = info if verbose else debug
276 121eb449 Bob Lantz
        log( '*** %s : %s\n' % ( self.name, args ) )
277
        self.sendCmd( *args, **kwargs )
278 efc9a01c Bob Lantz
        return self.waitOutput( verbose )
279 89bf3103 Brandon Heller
280 121eb449 Bob Lantz
    def cmdPrint( self, *args):
281 80a8fa62 Bob Lantz
        """Call cmd and printing its output
282
           cmd: string"""
283 121eb449 Bob Lantz
        return self.cmd( *args, **{ 'verbose': True } )
284 89bf3103 Brandon Heller
285
    # Interface management, configuration, and routing
286 efc9a01c Bob Lantz
287
    # BL notes: This might be a bit redundant or over-complicated.
288
    # However, it does allow a bit of specialization, including
289
    # changing the canonical interface names. It's also tricky since
290
    # the real interfaces are created as veth pairs, so we can't
291
    # make a single interface at a time.
292
293 d44a5843 Bob Lantz
    def newPort( self ):
294
        "Return the next port number to allocate."
295
        if len( self.ports ) > 0:
296
            return max( self.ports.values() ) + 1
297 dd159b4a Bob Lantz
        return self.portBase
298 89bf3103 Brandon Heller
299 121eb449 Bob Lantz
    def addIntf( self, intf, port=None ):
300 efc9a01c Bob Lantz
        """Add an interface.
301 a6bcad8f Bob Lantz
           intf: interface
302 121eb449 Bob Lantz
           port: port number (optional, typically OpenFlow port number)"""
303
        if port is None:
304
            port = self.newPort()
305 efc9a01c Bob Lantz
        self.intfs[ port ] = intf
306
        self.ports[ intf ] = port
307 a6bcad8f Bob Lantz
        self.nameToIntf[ intf.name ] = intf
308 84a91a14 Bob Lantz
        debug( '\n' )
309 14ff3ad3 Bob Lantz
        debug( 'added intf %s:%d to node %s\n' % ( intf, port, self.name ) )
310 efc9a01c Bob Lantz
        if self.inNamespace:
311 84a91a14 Bob Lantz
            debug( 'moving', intf, 'into namespace for', self.name, '\n' )
312 a6bcad8f Bob Lantz
            moveIntf( intf.name, self )
313 efc9a01c Bob Lantz
314 a6bcad8f Bob Lantz
    def defaultIntf( self ):
315
        "Return interface for lowest port"
316
        ports = self.intfs.keys()
317
        if ports:
318
            return self.intfs[ min( ports ) ]
319 efc99154 Bob Lantz
        else:
320
            warn( '*** defaultIntf: warning:', self.name,
321
                 'has no interfaces\n' )
322 efc9a01c Bob Lantz
323 a6bcad8f Bob Lantz
    def intf( self, intf='' ):
324 efc99154 Bob Lantz
        """Return our interface object with given name,
325 a6bcad8f Bob Lantz
           or default intf if name is empty"""
326
        if not intf:
327
            return self.defaultIntf()
328
        elif type( intf) is str:
329
            return self.nameToIntf[ intf ]
330
        else:
331 efc99154 Bob Lantz
            return None
332 a6bcad8f Bob Lantz
333 8856d284 Bob Lantz
    def connectionsTo( self, node):
334
        "Return [ intf1, intf2... ] for all intfs that connect self to node."
335 fb2f6523 Bob Lantz
        # We could optimize this if it is important
336 8856d284 Bob Lantz
        connections = []
337
        for intf in self.intfList():
338 a6bcad8f Bob Lantz
            link = intf.link
339 8856d284 Bob Lantz
            if link:
340
                node1, node2 = link.intf1.node, link.intf2.node
341
                if node1 == self and node2 == node:
342
                    connections += [ ( intf, link.intf2 ) ]
343
                elif node1 == node and node2 == self:
344
                    connections += [ ( intf, link.intf1 ) ]
345
        return connections
346 d44a5843 Bob Lantz
347 086ef80e Bob Lantz
    def deleteIntfs( self ):
348
        "Delete all of our interfaces."
349
        # In theory the interfaces should go away after we shut down.
350
        # However, this takes time, so we're better off removing them
351
        # explicitly so that we won't get errors if we run before they
352 d44a5843 Bob Lantz
        # have been removed by the kernel. Unfortunately this is very slow,
353
        # at least with Linux kernels before 2.6.33
354 086ef80e Bob Lantz
        for intf in self.intfs.values():
355 a6bcad8f Bob Lantz
            intf.delete()
356 086ef80e Bob Lantz
            info( '.' )
357
358 a6bcad8f Bob Lantz
    # Routing support
359 376bcba4 Brandon Heller
360 80a8fa62 Bob Lantz
    def setARP( self, ip, mac ):
361
        """Add an ARP entry.
362 019bff82 Bob Lantz
           ip: IP address as string
363
           mac: MAC address as string"""
364 121eb449 Bob Lantz
        result = self.cmd( 'arp', '-s', ip, mac )
365 376bcba4 Brandon Heller
        return result
366
367 80a8fa62 Bob Lantz
    def setHostRoute( self, ip, intf ):
368
        """Add route to host.
369
           ip: IP address as dotted decimal
370
           intf: string, interface name"""
371 14ff3ad3 Bob Lantz
        return self.cmd( 'route add -host', ip, 'dev', intf )
372 89bf3103 Brandon Heller
373 84a91a14 Bob Lantz
    def setDefaultRoute( self, intf=None ):
374 80a8fa62 Bob Lantz
        """Set the default route to go through intf.
375
           intf: string, interface name"""
376 84a91a14 Bob Lantz
        if not intf:
377
            intf = self.defaultIntf()
378 ec7b211c Bob Lantz
        self.cmd( 'ip route flush root 0/0' )
379 a6bcad8f Bob Lantz
        return self.cmd( 'route add default %s' % intf )
380 89bf3103 Brandon Heller
381 84a91a14 Bob Lantz
    # Convenience and configuration methods
382 54d026f6 Bob Lantz
383 b684ff78 Bob Lantz
    def setMAC( self, mac, intf=None ):
384 a6bcad8f Bob Lantz
        """Set the MAC address for an interface.
385
           intf: intf or intf name
386
           mac: MAC address as string"""
387
        return self.intf( intf ).setMAC( mac )
388
389 b684ff78 Bob Lantz
    def setIP( self, ip, prefixLen=8, intf=None ):
390 a6bcad8f Bob Lantz
        """Set the IP address for an interface.
391
           intf: interface name
392
           ip: IP address as a string
393
           prefixLen: prefix length, e.g. 8 for /8 or 16M addrs"""
394 a49c85a6 Bob Lantz
        # This should probably be rethought
395
        if '/' not in ip:
396
            ip = '%s/%s' % ( ip, prefixLen )
397
        return self.intf( intf ).setIP( ip )
398 54d026f6 Bob Lantz
399 d44a5843 Bob Lantz
    def IP( self, intf=None ):
400
        "Return IP address of a node or specific interface."
401 b684ff78 Bob Lantz
        return self.intf( intf ).IP()
402 d44a5843 Bob Lantz
403
    def MAC( self, intf=None ):
404
        "Return MAC address of a node or specific interface."
405 b684ff78 Bob Lantz
        return self.intf( intf ).IP()
406 54d026f6 Bob Lantz
407 a6bcad8f Bob Lantz
    def intfIsUp( self, intf=None ):
408 d44a5843 Bob Lantz
        "Check if an interface is up."
409 a6bcad8f Bob Lantz
        return self.intf( intf ).isUp()
410
411 84a91a14 Bob Lantz
    # The reason why we configure things in this way is so
412
    # That the parameters can be listed and documented in
413
    # the config method.
414
    # Dealing with subclasses and superclasses is slightly
415
    # annoying, but at least the information is there!
416
417
    def setParam( self, results, method, **param ):
418 216a4b7c Bob Lantz
        """Internal method: configure a *single* parameter
419
           results: dict of results to update
420
           method: config method name
421
           param: arg=value (ignore if value=None)
422
           value may also be list or dict"""
423 84a91a14 Bob Lantz
        name, value = param.items()[ 0 ]
424
        f = getattr( self, method, None )
425 216a4b7c Bob Lantz
        if not f or value is None:
426 84a91a14 Bob Lantz
            return
427
        if type( value ) is list:
428
            result = f( *value )
429
        elif type( value ) is dict:
430
            result = f( **value )
431
        else:
432
            result = f( value )
433
        results[ name ] = result
434 216a4b7c Bob Lantz
        return result
435 84a91a14 Bob Lantz
436 8856d284 Bob Lantz
    def config( self, mac=None, ip=None,
437
                defaultRoute=None, lo='up', **_params ):
438 84a91a14 Bob Lantz
        """Configure Node according to (optional) parameters:
439
           mac: MAC address for default interface
440
           ip: IP address for default interface
441
           ifconfig: arbitrary interface configuration
442
           Subclasses should override this method and call
443
           the parent class's config(**params)"""
444
        # If we were overriding this method, we would call
445
        # the superclass config method here as follows:
446 14ff3ad3 Bob Lantz
        # r = Parent.config( **_params )
447 84a91a14 Bob Lantz
        r = {}
448
        self.setParam( r, 'setMAC', mac=mac )
449
        self.setParam( r, 'setIP', ip=ip )
450
        self.setParam( r, 'defaultRoute', defaultRoute=defaultRoute )
451 8856d284 Bob Lantz
        # This should be examined
452
        self.cmd( 'ifconfig lo ' + lo )
453 84a91a14 Bob Lantz
        return r
454
455
    def configDefault( self, **moreParams ):
456
        "Configure with default parameters"
457
        self.params.update( moreParams )
458
        self.config( **self.params )
459
460 a6bcad8f Bob Lantz
    # This is here for backward compatibility
461
    def linkTo( self, node, link=Link ):
462
        """(Deprecated) Link to another node
463
           replace with Link( node1, node2)"""
464
        return link( self, node )
465 89bf3103 Brandon Heller
466
    # Other methods
467 a6bcad8f Bob Lantz
468 03dd914e Bob Lantz
    def intfList( self ):
469
        "List of our interfaces sorted by port number"
470
        return [ self.intfs[ p ] for p in sorted( self.intfs.iterkeys() ) ]
471
472 a6bcad8f Bob Lantz
    def intfNames( self ):
473 03dd914e Bob Lantz
        "The names of our interfaces sorted by port number"
474
        return [ str( i ) for i in self.intfList() ]
475 a6bcad8f Bob Lantz
476 8856d284 Bob Lantz
    def __repr__( self ):
477
        "More informative string representation"
478
        intfs = ( ','.join( [ '%s:%s' % ( i.name, i.IP() )
479
                        for i in self.intfList() ] ) )
480
        return '<%s %s: %s pid=%s> ' % (
481
                self.__class__.__name__, self.name, intfs, self.pid )
482
483 80a8fa62 Bob Lantz
    def __str__( self ):
484 8856d284 Bob Lantz
        "Abbreviated string representation"
485
        return self.name
486 89bf3103 Brandon Heller
487 14ff3ad3 Bob Lantz
    # Automatic class setup support
488
489
    isSetup = False
490
491
    @classmethod
492
    def checkSetup( cls ):
493
        "Make sure our class and superclasses are set up"
494
        while cls and not getattr( cls, 'isSetup', True ):
495
            cls.setup()
496
            cls.isSetup = True
497
            # Make pylint happy
498
            cls = getattr( type( cls ), '__base__', None )
499
500
    @classmethod
501
    def setup( cls ):
502
        "Make sure our class dependencies are available"
503
        pathCheck( 'mnexec', 'ifconfig', moduleName='Mininet')
504
505 89bf3103 Brandon Heller
506 216a4b7c Bob Lantz
class Host( Node ):
507
    "A host is simply a Node"
508
    pass
509
510
511
class CPULimitedHost( Host ):
512 84a91a14 Bob Lantz
513
    "CPU limited host"
514
515 59542784 Bob Lantz
    def __init__( self, name, sched='cfs', **kwargs ):
516
        Host.__init__( self, name, **kwargs )
517 197b083f Bob Lantz
        # Initialize class if necessary
518
        if not CPULimitedHost.inited:
519
            CPULimitedHost.init()
520 84a91a14 Bob Lantz
        # Create a cgroup and move shell into it
521 197b083f Bob Lantz
        self.cgroup = 'cpu,cpuacct,cpuset:/' + self.name
522 8a622c3a Bob Lantz
        errFail( 'cgcreate -g ' + self.cgroup )
523 197b083f Bob Lantz
        # We don't add ourselves to a cpuset because you must
524
        # specify the cpu and memory placement first
525
        errFail( 'cgclassify -g cpu,cpuacct:/%s %s' % ( self.name, self.pid ) )
526 44af37bc Bob Lantz
        # BL: Setting the correct period/quota is tricky, particularly
527
        # for RT. RT allows very small quotas, but the overhead
528
        # seems to be high. CFS has a mininimum quota of 1 ms, but
529
        # still does better with larger period values.
530
        self.period_us = kwargs.get( 'period_us', 100000 )
531 59542784 Bob Lantz
        self.sched = sched
532 84a91a14 Bob Lantz
533
    def cgroupSet( self, param, value, resource='cpu' ):
534
        "Set a cgroup parameter and return its value"
535
        cmd = 'cgset -r %s.%s=%s /%s' % (
536
            resource, param, value, self.name )
537 8e3699ec Bob Lantz
        quietRun( cmd )
538 8a622c3a Bob Lantz
        nvalue = int( self.cgroupGet( param, resource ) )
539
        if nvalue != value:
540
            error( '*** error: cgroupSet: %s set to %s instead of %s\n'
541
                   % ( param, nvalue, value ) )
542
        return nvalue
543 84a91a14 Bob Lantz
544
    def cgroupGet( self, param, resource='cpu' ):
545 14ff3ad3 Bob Lantz
        "Return value of cgroup parameter"
546 84a91a14 Bob Lantz
        cmd = 'cgget -r %s.%s /%s' % (
547
            resource, param, self.name )
548 197b083f Bob Lantz
        return int( quietRun( cmd ).split()[ -1 ] )
549 84a91a14 Bob Lantz
550 28833d86 Bob Lantz
    def cgroupDel( self ):
551
        "Clean up our cgroup"
552
        # info( '*** deleting cgroup', self.cgroup, '\n' )
553 612b21cb Bob Lantz
        _out, _err, exitcode = errRun( 'cgdelete -r ' + self.cgroup )
554 28833d86 Bob Lantz
        return exitcode != 0
555
556
    def cleanup( self ):
557
        "Clean up our cgroup"
558
        retry( retries=3, delaySecs=1, fn=self.cgroupDel )
559
560 84a91a14 Bob Lantz
    def chrt( self, prio=20 ):
561
        "Set RT scheduling priority"
562
        quietRun( 'chrt -p %s %s' % ( prio, self.pid ) )
563
        result = quietRun( 'chrt -p %s' % self.pid )
564
        firstline = result.split( '\n' )[ 0 ]
565
        lastword = firstline.split( ' ' )[ -1 ]
566 8a622c3a Bob Lantz
        if lastword != 'SCHED_RR':
567
            error( '*** error: could not assign SCHED_RR to %s\n' % self.name )
568 84a91a14 Bob Lantz
        return lastword
569
570 8a622c3a Bob Lantz
    def rtInfo( self, f ):
571
        "Internal method: return parameters for RT bandwidth"
572
        pstr, qstr = 'rt_period_us', 'rt_runtime_us'
573
        # RT uses wall clock time for period and quota
574
        quota = int( self.period_us * f * numCores() )
575
        return pstr, qstr, self.period_us, quota
576
577
    def cfsInfo( self, f):
578
        "Internal method: return parameters for CFS bandwidth"
579
        pstr, qstr = 'cfs_period_us', 'cfs_quota_us'
580
        # CFS uses wall clock time for period and CPU time for quota.
581
        quota = int( self.period_us * f * numCores() )
582
        period = self.period_us
583
        if f > 0 and quota < 1000:
584
            debug( '(cfsInfo: increasing default period) ' )
585
            quota = 1000
586
            period = int( quota / f / numCores() )
587
        return pstr, qstr, period, quota
588
589 216a4b7c Bob Lantz
    # BL comment:
590 14ff3ad3 Bob Lantz
    # This may not be the right API,
591 216a4b7c Bob Lantz
    # since it doesn't specify CPU bandwidth in "absolute"
592
    # units the way link bandwidth is specified.
593
    # We should use MIPS or SPECINT or something instead.
594
    # Alternatively, we should change from system fraction
595
    # to CPU seconds per second, essentially assuming that
596
    # all CPUs are the same.
597 8a622c3a Bob Lantz
598 216a4b7c Bob Lantz
    def setCPUFrac( self, f=-1, sched=None):
599
        """Set overall CPU fraction for this host
600
           f: CPU bandwidth limit (fraction)
601
           sched: 'rt' or 'cfs'
602
           Note 'cfs' requires CONFIG_CFS_BANDWIDTH"""
603
        if not f:
604
            return
605
        if not sched:
606
            sched = self.sched
607
        if sched == 'rt':
608 8a622c3a Bob Lantz
            pstr, qstr, period, quota = self.rtInfo( f )
609 216a4b7c Bob Lantz
        elif sched == 'cfs':
610 8a622c3a Bob Lantz
            pstr, qstr, period, quota = self.cfsInfo( f )
611 216a4b7c Bob Lantz
        else:
612
            return
613
        if quota < 0:
614 84a91a14 Bob Lantz
            # Reset to unlimited
615 216a4b7c Bob Lantz
            quota = -1
616
        # Set cgroup's period and quota
617 8e3699ec Bob Lantz
        self.cgroupSet( pstr, period )
618
        self.cgroupSet( qstr, quota )
619 216a4b7c Bob Lantz
        if sched == 'rt':
620
            # Set RT priority if necessary
621 8e3699ec Bob Lantz
            self.chrt( prio=20 )
622 8a622c3a Bob Lantz
        info( '(%s %d/%dus) ' % ( sched, quota, period ) )
623 84a91a14 Bob Lantz
624 197b083f Bob Lantz
    def setCPUs( self, cores ):
625
        "Specify (real) cores that our cgroup can run on"
626
        if type( cores ) is list:
627
            cores = ','.join( [ str( c ) for c in cores ] )
628
        self.cgroupSet( resource='cpuset', param='cpus',
629
                        value= cores )
630
        # Memory placement is probably not relevant, but we
631
        # must specify it anyway
632
        self.cgroupSet( resource='cpuset', param='mems',
633
                        value= cores )
634
        # We have to do this here after we've specified
635
        # cpus and mems
636
        errFail( 'cgclassify -g cpuset:/%s %s' % (
637
                self.name, self.pid ) )
638
639
    def config( self, cpu=None, cores=None, **params ):
640 84a91a14 Bob Lantz
        """cpu: desired overall system CPU fraction
641 197b083f Bob Lantz
           cores: (real) core(s) this host can run on
642 84a91a14 Bob Lantz
           params: parameters for Node.config()"""
643
        r = Node.config( self, **params )
644 216a4b7c Bob Lantz
        # Was considering cpu={'cpu': cpu , 'sched': sched}, but
645
        # that seems redundant
646 84a91a14 Bob Lantz
        self.setParam( r, 'setCPUFrac', cpu=cpu )
647 197b083f Bob Lantz
        self.setParam( r, 'setCPUs', cores=cores )
648 84a91a14 Bob Lantz
        return r
649
650 197b083f Bob Lantz
    inited = False
651
652
    @classmethod
653
    def init( cls ):
654
        "Initialization for CPULimitedHost class"
655
        mountCgroups()
656
        cls.inited = True
657
658
659 84a91a14 Bob Lantz
# Some important things to note:
660
#
661 bf9c6ab7 Bob Lantz
# The "IP" address which setIP() assigns to the switch is not
662 84a91a14 Bob Lantz
# an "IP address for the switch" in the sense of IP routing.
663 bf9c6ab7 Bob Lantz
# Rather, it is the IP address for the control interface,
664
# on the control network, and it is only relevant to the
665
# controller. If you are running in the root namespace
666
# (which is the only way to run OVS at the moment), the
667
# control interface is the loopback interface, and you
668
# normally never want to change its IP address!
669 84a91a14 Bob Lantz
#
670
# In general, you NEVER want to attempt to use Linux's
671
# network stack (i.e. ifconfig) to "assign" an IP address or
672
# MAC address to a switch data port. Instead, you "assign"
673
# the IP and MAC addresses in the controller by specifying
674
# packets that you want to receive or send. The "MAC" address
675
# reported by ifconfig for a switch data port is essentially
676 bf9c6ab7 Bob Lantz
# meaningless. It is important to understand this if you
677
# want to create a functional router using OpenFlow.
678 89bf3103 Brandon Heller
679 80a8fa62 Bob Lantz
class Switch( Node ):
680 6d72a3cb Bob Lantz
    """A Switch is a Node that is running (or has execed?)
681 80a8fa62 Bob Lantz
       an OpenFlow switch."""
682 89bf3103 Brandon Heller
683 8a622c3a Bob Lantz
    portBase = 1  # Switches start with port 1 in OpenFlow
684 dd159b4a Bob Lantz
685 84a91a14 Bob Lantz
    def __init__( self, name, dpid=None, opts='', listenPort=None, **params):
686
        """dpid: dpid for switch (or None for default)
687
           opts: additional switch options
688
           listenPort: port to listen on for dpctl connections"""
689
        Node.__init__( self, name, **params )
690
        self.dpid = dpid if dpid else self.defaultDpid()
691 121eb449 Bob Lantz
        self.opts = opts
692 ccca871a Brandon Heller
        self.listenPort = listenPort
693 8856d284 Bob Lantz
        if not self.inNamespace:
694 14c19260 Bob Lantz
            self.controlIntf = Intf( 'lo', self, port=0 )
695 84a91a14 Bob Lantz
696
    def defaultDpid( self ):
697
        "Derive dpid from switch name, s1 -> 1"
698
        dpid = int( re.findall( '\d+', self.name )[ 0 ] )
699
        dpid = hex( dpid )[ 2: ]
700 8dcefd5f Bob Lantz
        dpid = '0' * ( 16 - len( dpid ) ) + dpid
701 84a91a14 Bob Lantz
        return dpid
702 121eb449 Bob Lantz
703 9802686b Bob Lantz
    def defaultIntf( self ):
704 8856d284 Bob Lantz
        "Return control interface"
705
        if self.controlIntf:
706
            return self.controlIntf
707
        else:
708
            return Node.defaultIntf( self )
709 121eb449 Bob Lantz
710
    def sendCmd( self, *cmd, **kwargs ):
711 80a8fa62 Bob Lantz
        """Send command to Node.
712
           cmd: string"""
713 121eb449 Bob Lantz
        kwargs.setdefault( 'printPid', False )
714 1bb4412f Brandon Heller
        if not self.execed:
715 121eb449 Bob Lantz
            return Node.sendCmd( self, *cmd, **kwargs )
716 1bb4412f Brandon Heller
        else:
717 6d72a3cb Bob Lantz
            error( '*** Error: %s has execed and cannot accept commands' %
718 80a8fa62 Bob Lantz
                     self.name )
719 89bf3103 Brandon Heller
720 8856d284 Bob Lantz
    def __repr__( self ):
721
        "More informative string representation"
722
        intfs = ( ','.join( [ '%s:%s' % ( i.name, i.IP() )
723
                        for i in self.intfList() ] ) )
724
        return '<%s %s: %s pid=%s> ' % (
725
                self.__class__.__name__, self.name, intfs, self.pid )
726
727 80a8fa62 Bob Lantz
class UserSwitch( Switch ):
728 d44a5843 Bob Lantz
    "User-space switch."
729 89bf3103 Brandon Heller
730 b2ef87ae Bob Lantz
    def __init__( self, name, **kwargs ):
731 80a8fa62 Bob Lantz
        """Init.
732
           name: name for the switch"""
733 d44a5843 Bob Lantz
        Switch.__init__( self, name, **kwargs )
734 57fd19ef Bob Lantz
        pathCheck( 'ofdatapath', 'ofprotocol',
735 f9654e56 Bob Lantz
            moduleName='the OpenFlow reference user switch (openflow.org)' )
736 8856d284 Bob Lantz
        if self.listenPort:
737
            self.opts += ' --listen=ptcp:%i ' % self.listenPort
738 89bf3103 Brandon Heller
739 14ff3ad3 Bob Lantz
    @classmethod
740
    def setup( cls ):
741 b055728f Brandon Heller
        "Ensure any dependencies are loaded; if not, try to load them."
742 e900a16c Bob Lantz
        if not os.path.exists( '/dev/net/tun' ):
743
            moduleDeps( add=TUN )
744 b055728f Brandon Heller
745 8856d284 Bob Lantz
    def dpctl( self, *args ):
746
        "Run dpctl command"
747
        if not self.listenPort:
748 14c19260 Bob Lantz
            return "can't run dpctl without passive listening port"
749
        return self.cmd( 'dpctl ' + ' '.join( args ) +
750 8856d284 Bob Lantz
                         ' tcp:127.0.0.1:%i' % self.listenPort )
751
752 80a8fa62 Bob Lantz
    def start( self, controllers ):
753
        """Start OpenFlow reference user datapath.
754 6d72a3cb Bob Lantz
           Log to /tmp/sN-{ofd,ofp}.log.
755 efc9a01c Bob Lantz
           controllers: list of controller objects"""
756 019bff82 Bob Lantz
        controller = controllers[ 0 ]
757 89bf3103 Brandon Heller
        ofdlog = '/tmp/' + self.name + '-ofd.log'
758
        ofplog = '/tmp/' + self.name + '-ofp.log'
759 80a8fa62 Bob Lantz
        self.cmd( 'ifconfig lo up' )
760 14c19260 Bob Lantz
        intfs = [ str( i ) for i in self.intfList() if not i.IP() ]
761 efc9a01c Bob Lantz
        self.cmd( 'ofdatapath -i ' + ','.join( intfs ) +
762 14ff3ad3 Bob Lantz
            ' punix:/tmp/' + self.name + ' -d ' + self.dpid +
763 84a91a14 Bob Lantz
            ' --no-slicing ' +
764 6d72a3cb Bob Lantz
            ' 1> ' + ofdlog + ' 2> ' + ofdlog + ' &' )
765 efc9a01c Bob Lantz
        self.cmd( 'ofprotocol unix:/tmp/' + self.name +
766 272d496d Bob Lantz
            ' tcp:%s:%d' % ( controller.IP(), controller.port ) +
767 a6661f0a Bob Lantz
            ' --fail=closed ' + self.opts +
768 6d72a3cb Bob Lantz
            ' 1> ' + ofplog + ' 2>' + ofplog + ' &' )
769 89bf3103 Brandon Heller
770 80a8fa62 Bob Lantz
    def stop( self ):
771
        "Stop OpenFlow reference user datapath."
772
        self.cmd( 'kill %ofdatapath' )
773
        self.cmd( 'kill %ofprotocol' )
774 086ef80e Bob Lantz
        self.deleteIntfs()
775 1bb4412f Brandon Heller
776 07aad110 Brandon Heller
777 8a7d42db Bob Lantz
class OVSLegacyKernelSwitch( Switch ):
778
    """Open VSwitch legacy kernel-space switch using ovs-openflowd.
779 80a8fa62 Bob Lantz
       Currently only works in the root namespace."""
780 f7c2df25 Brandon Heller
781 086ef80e Bob Lantz
    def __init__( self, name, dp=None, **kwargs ):
782 80a8fa62 Bob Lantz
        """Init.
783 e3a2ef01 Bob Lantz
           name: name for switch
784 6d72a3cb Bob Lantz
           dp: netlink id (0, 1, 2, ...)
785 d44a5843 Bob Lantz
           defaultMAC: default MAC as unsigned int; random value if None"""
786
        Switch.__init__( self, name, **kwargs )
787 612b21cb Bob Lantz
        self.dp = dp if dp else self.name
788 121eb449 Bob Lantz
        self.intf = self.dp
789 d44a5843 Bob Lantz
        if self.inNamespace:
790
            error( "OVSKernelSwitch currently only works"
791 b480b6ef Brandon Heller
                " in the root namespace.\n" )
792 d44a5843 Bob Lantz
            exit( 1 )
793 f7c2df25 Brandon Heller
794 14ff3ad3 Bob Lantz
    @classmethod
795
    def setup( cls ):
796 b055728f Brandon Heller
        "Ensure any dependencies are loaded; if not, try to load them."
797 f9654e56 Bob Lantz
        pathCheck( 'ovs-dpctl', 'ovs-openflowd',
798
            moduleName='Open vSwitch (openvswitch.org)')
799
        moduleDeps( subtract=OF_KMOD, add=OVS_KMOD )
800 b055728f Brandon Heller
801 80a8fa62 Bob Lantz
    def start( self, controllers ):
802
        "Start up kernel datapath."
803 f7c2df25 Brandon Heller
        ofplog = '/tmp/' + self.name + '-ofp.log'
804 80a8fa62 Bob Lantz
        quietRun( 'ifconfig lo up' )
805 f7c2df25 Brandon Heller
        # Delete local datapath if it exists;
806
        # then create a new one monitoring the given interfaces
807 8856d284 Bob Lantz
        self.cmd( 'ovs-dpctl del-dp ' + self.dp )
808 121eb449 Bob Lantz
        self.cmd( 'ovs-dpctl add-dp ' + self.dp )
809 14c19260 Bob Lantz
        intfs = [ str( i ) for i in self.intfList() if not i.IP() ]
810 5cc80828 Bob Lantz
        self.cmd( 'ovs-dpctl', 'add-if', self.dp, ' '.join( intfs ) )
811 f7c2df25 Brandon Heller
        # Run protocol daemon
812 019bff82 Bob Lantz
        controller = controllers[ 0 ]
813 121eb449 Bob Lantz
        self.cmd( 'ovs-openflowd ' + self.dp +
814 a6661f0a Bob Lantz
            ' tcp:%s:%d' % ( controller.IP(), controller.port ) +
815 14ff3ad3 Bob Lantz
            ' --fail=secure ' + self.opts +
816 84a91a14 Bob Lantz
            ' --datapath-id=' + self.dpid +
817 121eb449 Bob Lantz
            ' 1>' + ofplog + ' 2>' + ofplog + '&' )
818 f7c2df25 Brandon Heller
        self.execed = False
819
820 80a8fa62 Bob Lantz
    def stop( self ):
821
        "Terminate kernel datapath."
822 121eb449 Bob Lantz
        quietRun( 'ovs-dpctl del-dp ' + self.dp )
823 75d72d96 Bob Lantz
        self.cmd( 'kill %ovs-openflowd' )
824 086ef80e Bob Lantz
        self.deleteIntfs()
825 f7c2df25 Brandon Heller
826
827 8a7d42db Bob Lantz
class OVSSwitch( Switch ):
828
    "Open vSwitch switch. Depends on ovs-vsctl."
829
830 84a91a14 Bob Lantz
    def __init__( self, name, **params ):
831 8a7d42db Bob Lantz
        """Init.
832
           name: name for switch
833
           defaultMAC: default MAC as unsigned int; random value if None"""
834 84a91a14 Bob Lantz
        Switch.__init__( self, name, **params )
835 14ff3ad3 Bob Lantz
836
    @classmethod
837
    def setup( cls ):
838 8a7d42db Bob Lantz
        "Make sure Open vSwitch is installed and working"
839 14ff3ad3 Bob Lantz
        pathCheck( 'ovs-vsctl',
840 8a7d42db Bob Lantz
            moduleName='Open vSwitch (openvswitch.org)')
841
        moduleDeps( subtract=OF_KMOD, add=OVS_KMOD )
842
        out, err, exitcode = errRun( 'ovs-vsctl -t 1 show' )
843
        if exitcode:
844 14ff3ad3 Bob Lantz
            error( out + err +
845 8a7d42db Bob Lantz
                   'ovs-vsctl exited with code %d\n' % exitcode +
846
                   '*** Error connecting to ovs-db with ovs-vsctl\n'
847
                   'Make sure that Open vSwitch is installed, '
848
                   'that ovsdb-server is running, and that\n'
849
                   '"ovs-vsctl show" works correctly.\n'
850 14ff3ad3 Bob Lantz
                   'You may wish to try '
851
                   '"service openvswitch-switch start".\n' )
852 8a7d42db Bob Lantz
            exit( 1 )
853
854 8856d284 Bob Lantz
    def dpctl( self, *args ):
855
        "Run ovs-dpctl command"
856
        return self.cmd( 'ovs-dpctl', args[ 0 ], self, *args[ 1: ] )
857
858 1aec55d9 Bob Lantz
    @staticmethod
859
    def TCReapply( intf ):
860
        """Unfortunately OVS and Mininet are fighting
861
           over tc queuing disciplines. As a quick hack/
862
           workaround, we clear OVS's and reapply our own."""
863
        if type( intf ) is TCIntf:
864
            intf.config( **intf.params )
865
866 8856d284 Bob Lantz
    def attach( self, intf ):
867
        "Connect a data port"
868
        self.cmd( 'ovs-vsctl add-port', self, intf )
869
        self.cmd( 'ifconfig', intf, 'up' )
870 1aec55d9 Bob Lantz
        self.TCReapply( intf )
871 8856d284 Bob Lantz
872
    def detach( self, intf ):
873
        "Disconnect a data port"
874
        self.cmd( 'ovs-vsctl del-port', self, intf )
875
876 8a7d42db Bob Lantz
    def start( self, controllers ):
877
        "Start up a new OVS OpenFlow switch using ovs-vsctl"
878 14ff3ad3 Bob Lantz
        if self.inNamespace:
879 8856d284 Bob Lantz
            raise Exception(
880 14ff3ad3 Bob Lantz
                'OVS kernel switch does not work in a namespace' )
881 8856d284 Bob Lantz
        # We should probably call config instead, but this
882
        # requires some rethinking...
883
        self.cmd( 'ifconfig lo up' )
884 8a7d42db Bob Lantz
        # Annoyingly, --if-exists option seems not to work
885 8856d284 Bob Lantz
        self.cmd( 'ovs-vsctl del-br', self )
886
        self.cmd( 'ovs-vsctl add-br', self )
887
        self.cmd( 'ovs-vsctl set-fail-mode', self, 'secure' )
888
        for intf in self.intfList():
889
            if not intf.IP():
890
                self.attach( intf )
891 8a7d42db Bob Lantz
        # Add controllers
892 14ff3ad3 Bob Lantz
        clist = ','.join( [ 'tcp:%s:%d' % ( c.IP(), c.port )
893
                            for c in controllers ] )
894 8856d284 Bob Lantz
        self.cmd( 'ovs-vsctl set-controller', self, clist )
895 8a7d42db Bob Lantz
896
    def stop( self ):
897
        "Terminate OVS switch."
898 8856d284 Bob Lantz
        self.cmd( 'ovs-vsctl del-br', self )
899 3f61ea71 Bob Lantz
        self.deleteIntfs()
900 8a7d42db Bob Lantz
901
OVSKernelSwitch = OVSSwitch
902
903 84a91a14 Bob Lantz
904 80a8fa62 Bob Lantz
class Controller( Node ):
905 7c371cf3 Bob Lantz
    """A Controller is a Node that is running (or has execed?) an
906 80a8fa62 Bob Lantz
       OpenFlow controller."""
907 1bb4412f Brandon Heller
908 57fd19ef Bob Lantz
    def __init__( self, name, inNamespace=False, command='controller',
909 84a91a14 Bob Lantz
                 cargs='-v ptcp:%d', cdir=None, ip="127.0.0.1",
910
                 port=6633, **params ):
911 57fd19ef Bob Lantz
        self.command = command
912 1bb4412f Brandon Heller
        self.cargs = cargs
913
        self.cdir = cdir
914 84a91a14 Bob Lantz
        self.ip = ip
915 60d9ead6 David Erickson
        self.port = port
916 086ef80e Bob Lantz
        Node.__init__( self, name, inNamespace=inNamespace,
917 84a91a14 Bob Lantz
            ip=ip, **params  )
918 1bb4412f Brandon Heller
919 80a8fa62 Bob Lantz
    def start( self ):
920
        """Start <controller> <args> on controller.
921
           Log to /tmp/cN.log"""
922 57fd19ef Bob Lantz
        pathCheck( self.command )
923 1bb4412f Brandon Heller
        cout = '/tmp/' + self.name + '.log'
924
        if self.cdir is not None:
925 efc9a01c Bob Lantz
            self.cmd( 'cd ' + self.cdir )
926 57fd19ef Bob Lantz
        self.cmd( self.command + ' ' + self.cargs % self.port +
927 e3f6ecca Bob Lantz
            ' 1>' + cout + ' 2>' + cout + '&' )
928 723d068c Brandon Heller
        self.execed = False
929 89bf3103 Brandon Heller
930 80a8fa62 Bob Lantz
    def stop( self ):
931
        "Stop controller."
932 57fd19ef Bob Lantz
        self.cmd( 'kill %' + self.command )
933 1bb4412f Brandon Heller
        self.terminate()
934 89bf3103 Brandon Heller
935 d44a5843 Bob Lantz
    def IP( self, intf=None ):
936 80a8fa62 Bob Lantz
        "Return IP address of the Controller"
937 a6bcad8f Bob Lantz
        if self.intfs:
938
            ip = Node.IP( self, intf )
939
        else:
940 84a91a14 Bob Lantz
            ip = self.ip
941 d44a5843 Bob Lantz
        return ip
942 723d068c Brandon Heller
943 8856d284 Bob Lantz
    def __repr__( self ):
944
        "More informative string representation"
945
        return '<%s %s: %s:%s pid=%s> ' % (
946
                self.__class__.__name__, self.name,
947
                self.IP(), self.port, self.pid )
948
949 84a91a14 Bob Lantz
950 9addfc13 Bob Lantz
class OVSController( Controller ):
951
    "Open vSwitch controller"
952
    def __init__( self, name, command='ovs-controller', **kwargs ):
953
        Controller.__init__( self, name, command=command, **kwargs )
954
955
956 80a8fa62 Bob Lantz
class NOX( Controller ):
957
    "Controller to run a NOX application."
958
959 2db4268b Bob Lantz
    def __init__( self, name, *noxArgs, **kwargs ):
960 80a8fa62 Bob Lantz
        """Init.
961
           name: name to give controller
962 2db4268b Bob Lantz
           noxArgs: arguments (strings) to pass to NOX"""
963 80a8fa62 Bob Lantz
        if not noxArgs:
964 2db4268b Bob Lantz
            warn( 'warning: no NOX modules specified; '
965
                  'running packetdump only\n' )
966 80a8fa62 Bob Lantz
            noxArgs = [ 'packetdump' ]
967 2db4268b Bob Lantz
        elif type( noxArgs ) not in ( list, tuple ):
968 f32a5468 Brandon Heller
            noxArgs = [ noxArgs ]
969 137ec305 Bob Lantz
970 4f4f1dd2 Brandon Heller
        if 'NOX_CORE_DIR' not in os.environ:
971 137ec305 Bob Lantz
            exit( 'exiting; please set missing NOX_CORE_DIR env var' )
972 80a8fa62 Bob Lantz
        noxCoreDir = os.environ[ 'NOX_CORE_DIR' ]
973 4f4f1dd2 Brandon Heller
974 80a8fa62 Bob Lantz
        Controller.__init__( self, name,
975 57fd19ef Bob Lantz
            command=noxCoreDir + '/nox_core',
976 518c75b5 Bob Lantz
            cargs='--libdir=/usr/local/lib -v -i ptcp:%s ' +
977 80a8fa62 Bob Lantz
                    ' '.join( noxArgs ),
978 121eb449 Bob Lantz
            cdir=noxCoreDir, **kwargs )
979 80a8fa62 Bob Lantz
980
981
class RemoteController( Controller ):
982
    "Controller running outside of Mininet's control."
983
984 b2ef87ae Bob Lantz
    def __init__( self, name, defaultIP='127.0.0.1',
985
                 port=6633, **kwargs):
986 80a8fa62 Bob Lantz
        """Init.
987
           name: name to give controller
988 c8641d7d Brandon Heller
           defaultIP: the IP address where the remote controller is
989 80a8fa62 Bob Lantz
           listening
990
           port: the port where the remote controller is listening"""
991 b2ef87ae Bob Lantz
        Controller.__init__( self, name, defaultIP=defaultIP, port=port,
992
            **kwargs )
993 80a8fa62 Bob Lantz
994
    def start( self ):
995
        "Overridden to do nothing."
996 60d9ead6 David Erickson
        return
997
998 80a8fa62 Bob Lantz
    def stop( self ):
999
        "Overridden to do nothing."
1000 60d9ead6 David Erickson
        return