Statistics
| Branch: | Tag: | Revision:

mininet / mininet / node.py @ 32507498

History | View | Annotate | Download (45.8 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 538a856c Bob Lantz
from operator import or_
53 3236d33b Andrew Ferguson
from time import sleep
54 60d9ead6 David Erickson
55 2db4268b Bob Lantz
from mininet.log import info, error, warn, debug
56 197b083f Bob Lantz
from mininet.util import ( quietRun, errRun, errFail, moveIntf, isShellBuiltin,
57 edf60032 Brandon Heller
                           numCores, retry, mountCgroups )
58 f0010171 Bob Lantz
from mininet.moduledeps import moduleDeps, pathCheck, OVS_KMOD, OF_KMOD, TUN
59 1aec55d9 Bob Lantz
from mininet.link import Link, Intf, TCIntf
60 2fffa0bb Brandon Heller
61 80a8fa62 Bob Lantz
class Node( object ):
62
    """A virtual network node is simply a shell in a network namespace.
63
       We communicate with it using pipes."""
64 6d72a3cb Bob Lantz
65 dd159b4a Bob Lantz
    portBase = 0  # Nodes always start with eth0/port0, even in OF 1.0
66 54d026f6 Bob Lantz
67 84a91a14 Bob Lantz
    def __init__( self, name, inNamespace=True, **params ):
68 086ef80e Bob Lantz
        """name: name of node
69
           inNamespace: in network namespace?
70 84a91a14 Bob Lantz
           params: Node parameters (see config() for details)"""
71
72
        # Make sure class actually works
73
        self.checkSetup()
74
75 89bf3103 Brandon Heller
        self.name = name
76
        self.inNamespace = inNamespace
77 84a91a14 Bob Lantz
78
        # Stash configuration parameters for future reference
79
        self.params = params
80
81
        self.intfs = {}  # dict of port numbers to interfaces
82
        self.ports = {}  # dict of interfaces to port numbers
83
                         # replace with Port objects, eventually ?
84
        self.nameToIntf = {}  # dict of interface names to Intfs
85
86 14ff3ad3 Bob Lantz
        # Make pylint happy
87
        ( self.shell, self.execed, self.pid, self.stdin, self.stdout,
88 33d548b4 Brandon Heller
            self.lastPid, self.lastCmd, self.pollOut ) = (
89
                None, None, None, None, None, None, None, None )
90 14ff3ad3 Bob Lantz
        self.waiting = False
91
        self.readbuf = ''
92
93 84a91a14 Bob Lantz
        # Start command interpreter shell
94
        self.startShell()
95
96
    # File descriptor to node mapping support
97
    # Class variables and methods
98
99
    inToNode = {}  # mapping of input fds to nodes
100
    outToNode = {}  # mapping of output fds to nodes
101
102
    @classmethod
103
    def fdToNode( cls, fd ):
104
        """Return node corresponding to given file descriptor.
105
           fd: file descriptor
106
           returns: node"""
107
        node = cls.outToNode.get( fd )
108
        return node or cls.inToNode.get( fd )
109
110
    # Command support via shell process in namespace
111
112
    def startShell( self ):
113
        "Start a shell process for running commands"
114
        if self.shell:
115
            error( "%s: shell is already running" )
116
            return
117 216a4b7c Bob Lantz
        # mnexec: (c)lose descriptors, (d)etach from tty,
118 14ff3ad3 Bob Lantz
        # (p)rint pid, and run in (n)amespace
119 121eb449 Bob Lantz
        opts = '-cdp'
120 89bf3103 Brandon Heller
        if self.inNamespace:
121 b14b1ee4 Bob Lantz
            opts += 'n'
122 216a4b7c Bob Lantz
        # bash -m: enable job control
123 1bf1a4d5 Bob Lantz
        # -s: pass $* to shell, and make process easy to find in ps
124
        cmd = [ 'mnexec', opts, 'bash', '-ms', 'mininet:' + self.name ]
125 80a8fa62 Bob Lantz
        self.shell = Popen( cmd, stdin=PIPE, stdout=PIPE, stderr=STDOUT,
126 edf60032 Brandon Heller
                            close_fds=True )
127 89bf3103 Brandon Heller
        self.stdin = self.shell.stdin
128
        self.stdout = self.shell.stdout
129 121eb449 Bob Lantz
        self.pid = self.shell.pid
130 89bf3103 Brandon Heller
        self.pollOut = select.poll()
131 80a8fa62 Bob Lantz
        self.pollOut.register( self.stdout )
132 89bf3103 Brandon Heller
        # Maintain mapping between file descriptors and nodes
133 bcacfc05 Bob Lantz
        # This is useful for monitoring multiple nodes
134 89bf3103 Brandon Heller
        # using select.poll()
135 80a8fa62 Bob Lantz
        self.outToNode[ self.stdout.fileno() ] = self
136
        self.inToNode[ self.stdin.fileno() ] = self
137 89bf3103 Brandon Heller
        self.execed = False
138 bcacfc05 Bob Lantz
        self.lastCmd = None
139
        self.lastPid = None
140 ec7b211c Bob Lantz
        self.readbuf = ''
141 bcacfc05 Bob Lantz
        self.waiting = False
142 89bf3103 Brandon Heller
143 80a8fa62 Bob Lantz
    def cleanup( self ):
144
        "Help python collect its garbage."
145 10be691b Bob Lantz
        # Intfs may end up in root NS
146
        for intfName in self.intfNames():
147
            if self.name in intfName:
148
                quietRun( 'ip link del ' + intfName )
149 89bf3103 Brandon Heller
        self.shell = None
150
151
    # Subshell I/O, commands and control
152 163a6cf3 Bob Lantz
153 28f46c8d Bob Lantz
    def read( self, maxbytes=1024 ):
154 ec7b211c Bob Lantz
        """Buffered read from node, non-blocking.
155 28f46c8d Bob Lantz
           maxbytes: maximum number of bytes to return"""
156 ec7b211c Bob Lantz
        count = len( self.readbuf )
157 28f46c8d Bob Lantz
        if count < maxbytes:
158
            data = os.read( self.stdout.fileno(), maxbytes - count )
159 ec7b211c Bob Lantz
            self.readbuf += data
160 28f46c8d Bob Lantz
        if maxbytes >= len( self.readbuf ):
161 ec7b211c Bob Lantz
            result = self.readbuf
162
            self.readbuf = ''
163
        else:
164 28f46c8d Bob Lantz
            result = self.readbuf[ :maxbytes ]
165
            self.readbuf = self.readbuf[ maxbytes: ]
166 ec7b211c Bob Lantz
        return result
167
168
    def readline( self ):
169
        """Buffered readline from node, non-blocking.
170
           returns: line (minus newline) or None"""
171
        self.readbuf += self.read( 1024 )
172
        if '\n' not in self.readbuf:
173
            return None
174
        pos = self.readbuf.find( '\n' )
175 0bd5c651 Brandon Heller
        line = self.readbuf[ 0: pos ]
176 ec7b211c Bob Lantz
        self.readbuf = self.readbuf[ pos + 1: ]
177
        return line
178 80a8fa62 Bob Lantz
179
    def write( self, data ):
180
        """Write data to node.
181
           data: string"""
182
        os.write( self.stdin.fileno(), data )
183
184
    def terminate( self ):
185 6d72a3cb Bob Lantz
        "Send kill signal to Node and clean up after it."
186 80a8fa62 Bob Lantz
        os.kill( self.pid, signal.SIGKILL )
187 89bf3103 Brandon Heller
        self.cleanup()
188
189 80a8fa62 Bob Lantz
    def stop( self ):
190
        "Stop node."
191 89bf3103 Brandon Heller
        self.terminate()
192
193 f24e70a4 Bob Lantz
    def waitReadable( self, timeoutms=None ):
194
        """Wait until node's output is readable.
195
           timeoutms: timeout in ms or None to wait indefinitely."""
196 ec7b211c Bob Lantz
        if len( self.readbuf ) == 0:
197 f24e70a4 Bob Lantz
            self.pollOut.poll( timeoutms )
198 89bf3103 Brandon Heller
199 121eb449 Bob Lantz
    def sendCmd( self, *args, **kwargs ):
200 80a8fa62 Bob Lantz
        """Send a command, followed by a command to echo a sentinel,
201 121eb449 Bob Lantz
           and return without waiting for the command to complete.
202
           args: command and arguments, or string
203
           printPid: print command's PID?"""
204 89bf3103 Brandon Heller
        assert not self.waiting
205 121eb449 Bob Lantz
        printPid = kwargs.get( 'printPid', True )
206 318ae55e Bob Lantz
        # Allow sendCmd( [ list ] )
207
        if len( args ) == 1 and type( args[ 0 ] ) is list:
208
            cmd = args[ 0 ]
209
        # Allow sendCmd( cmd, arg1, arg2... )
210
        elif len( args ) > 0:
211 121eb449 Bob Lantz
            cmd = args
212 318ae55e Bob Lantz
        # Convert to string
213 121eb449 Bob Lantz
        if not isinstance( cmd, str ):
214 a6bcad8f Bob Lantz
            cmd = ' '.join( [ str( c ) for c in cmd ] )
215 f24e70a4 Bob Lantz
        if not re.search( r'\w', cmd ):
216
            # Replace empty commands with something harmless
217
            cmd = 'echo -n'
218 d1b29d58 Bob Lantz
        self.lastCmd = cmd
219
        printPid = printPid and not isShellBuiltin( cmd )
220 c6e7eaf0 Bob Lantz
        if len( cmd ) > 0 and cmd[ -1 ] == '&':
221 d1b29d58 Bob Lantz
            # print ^A{pid}\n{sentinel}
222
            cmd += ' printf "\\001%d\n\\177" $! \n'
223 89bf3103 Brandon Heller
        else:
224 d1b29d58 Bob Lantz
            # print sentinel
225
            cmd += '; printf "\\177"'
226 bcacfc05 Bob Lantz
            if printPid and not isShellBuiltin( cmd ):
227
                cmd = 'mnexec -p ' + cmd
228 612b21cb Bob Lantz
        self.write( cmd + '\n' )
229 bcacfc05 Bob Lantz
        self.lastPid = None
230 89bf3103 Brandon Heller
        self.waiting = True
231
232 ec7b211c Bob Lantz
    def sendInt( self, sig=signal.SIGINT ):
233 bcacfc05 Bob Lantz
        "Interrupt running command."
234
        if self.lastPid:
235 b5672f15 Bob Lantz
            try:
236
                os.kill( self.lastPid, sig )
237 82b72072 Bob Lantz
            except OSError:
238 b5672f15 Bob Lantz
                pass
239 4065511a Bob Lantz
240 f24e70a4 Bob Lantz
    def monitor( self, timeoutms=None ):
241 e4c82e52 Bob Lantz
        """Monitor and return the output of a command.
242 f24e70a4 Bob Lantz
           Set self.waiting to False if command has completed.
243
           timeoutms: timeout in ms or None to wait indefinitely."""
244
        self.waitReadable( timeoutms )
245 80a8fa62 Bob Lantz
        data = self.read( 1024 )
246 bcacfc05 Bob Lantz
        # Look for PID
247
        marker = chr( 1 ) + r'\d+\n'
248
        if chr( 1 ) in data:
249
            markers = re.findall( marker, data )
250
            if markers:
251
                self.lastPid = int( markers[ 0 ][ 1: ] )
252
                data = re.sub( marker, '', data )
253
        # Look for sentinel/EOF
254 4065511a Bob Lantz
        if len( data ) > 0 and data[ -1 ] == chr( 127 ):
255 89bf3103 Brandon Heller
            self.waiting = False
256 e4c82e52 Bob Lantz
            data = data[ :-1 ]
257 4065511a Bob Lantz
        elif chr( 127 ) in data:
258 bcacfc05 Bob Lantz
            self.waiting = False
259 e4c82e52 Bob Lantz
            data = data.replace( chr( 127 ), '' )
260
        return data
261 723d068c Brandon Heller
262 efc9a01c Bob Lantz
    def waitOutput( self, verbose=False ):
263 80a8fa62 Bob Lantz
        """Wait for a command to complete.
264 7c371cf3 Bob Lantz
           Completion is signaled by a sentinel character, ASCII(127)
265 80a8fa62 Bob Lantz
           appearing in the output stream.  Wait for the sentinel and return
266 efc9a01c Bob Lantz
           the output, including trailing newline.
267
           verbose: print output interactively"""
268
        log = info if verbose else debug
269 89bf3103 Brandon Heller
        output = ''
270 e4c82e52 Bob Lantz
        while self.waiting:
271
            data = self.monitor()
272 4065511a Bob Lantz
            output += data
273
            log( data )
274 89bf3103 Brandon Heller
        return output
275
276 121eb449 Bob Lantz
    def cmd( self, *args, **kwargs ):
277 80a8fa62 Bob Lantz
        """Send a command, wait for output, and return it.
278
           cmd: string"""
279 121eb449 Bob Lantz
        verbose = kwargs.get( 'verbose', False )
280 efc9a01c Bob Lantz
        log = info if verbose else debug
281 121eb449 Bob Lantz
        log( '*** %s : %s\n' % ( self.name, args ) )
282
        self.sendCmd( *args, **kwargs )
283 efc9a01c Bob Lantz
        return self.waitOutput( verbose )
284 89bf3103 Brandon Heller
285 121eb449 Bob Lantz
    def cmdPrint( self, *args):
286 80a8fa62 Bob Lantz
        """Call cmd and printing its output
287
           cmd: string"""
288 121eb449 Bob Lantz
        return self.cmd( *args, **{ 'verbose': True } )
289 89bf3103 Brandon Heller
290 089e8130 Bob Lantz
    def popen( self, *args, **kwargs ):
291
        """Return a Popen() object in our namespace
292
           args: Popen() args, single list, or string
293
           kwargs: Popen() keyword args"""
294
        defaults = { 'stdout': PIPE, 'stderr': PIPE,
295 5ca91f9c Bob Lantz
                     'mncmd':
296 e5a15ced Bob Lantz
                     [ 'mnexec', '-da', str( self.pid ) ] }
297 089e8130 Bob Lantz
        defaults.update( kwargs )
298
        if len( args ) == 1:
299
            if type( args[ 0 ] ) is list:
300
                # popen([cmd, arg1, arg2...])
301
                cmd = args[ 0 ]
302
            elif type( args[ 0 ] ) is str:
303
                # popen("cmd arg1 arg2...")
304
                cmd = args[ 0 ].split()
305 b9100834 Bob Lantz
            else:
306
                raise Exception( 'popen() requires a string or list' )
307 089e8130 Bob Lantz
        elif len( args ) > 0:
308
            # popen( cmd, arg1, arg2... )
309 06f7408c Bob Lantz
            cmd = list( args )
310 089e8130 Bob Lantz
        # Attach to our namespace  using mnexec -a
311
        mncmd = defaults[ 'mncmd' ]
312
        del defaults[ 'mncmd' ]
313 df600200 Bob Lantz
        cmd = mncmd + cmd
314 b9100834 Bob Lantz
        # Shell requires a string, not a list!
315
        if defaults.get( 'shell', False ):
316
            cmd = ' '.join( cmd )
317 089e8130 Bob Lantz
        return Popen( cmd, **defaults )
318
319
    def pexec( self, *args, **kwargs ):
320
        """Execute a command using popen
321
           returns: out, err, exitcode"""
322
        popen = self.popen( *args, **kwargs)
323
        out, err = popen.communicate()
324
        exitcode = popen.wait()
325
        return out, err, exitcode
326
327 89bf3103 Brandon Heller
    # Interface management, configuration, and routing
328 efc9a01c Bob Lantz
329
    # BL notes: This might be a bit redundant or over-complicated.
330
    # However, it does allow a bit of specialization, including
331
    # changing the canonical interface names. It's also tricky since
332
    # the real interfaces are created as veth pairs, so we can't
333
    # make a single interface at a time.
334
335 d44a5843 Bob Lantz
    def newPort( self ):
336
        "Return the next port number to allocate."
337
        if len( self.ports ) > 0:
338
            return max( self.ports.values() ) + 1
339 dd159b4a Bob Lantz
        return self.portBase
340 89bf3103 Brandon Heller
341 121eb449 Bob Lantz
    def addIntf( self, intf, port=None ):
342 efc9a01c Bob Lantz
        """Add an interface.
343 a6bcad8f Bob Lantz
           intf: interface
344 121eb449 Bob Lantz
           port: port number (optional, typically OpenFlow port number)"""
345
        if port is None:
346
            port = self.newPort()
347 efc9a01c Bob Lantz
        self.intfs[ port ] = intf
348
        self.ports[ intf ] = port
349 a6bcad8f Bob Lantz
        self.nameToIntf[ intf.name ] = intf
350 84a91a14 Bob Lantz
        debug( '\n' )
351 14ff3ad3 Bob Lantz
        debug( 'added intf %s:%d to node %s\n' % ( intf, port, self.name ) )
352 efc9a01c Bob Lantz
        if self.inNamespace:
353 84a91a14 Bob Lantz
            debug( 'moving', intf, 'into namespace for', self.name, '\n' )
354 a6bcad8f Bob Lantz
            moveIntf( intf.name, self )
355 efc9a01c Bob Lantz
356 a6bcad8f Bob Lantz
    def defaultIntf( self ):
357
        "Return interface for lowest port"
358
        ports = self.intfs.keys()
359
        if ports:
360
            return self.intfs[ min( ports ) ]
361 efc99154 Bob Lantz
        else:
362
            warn( '*** defaultIntf: warning:', self.name,
363 edf60032 Brandon Heller
                  'has no interfaces\n' )
364 efc9a01c Bob Lantz
365 a6bcad8f Bob Lantz
    def intf( self, intf='' ):
366 bf208cde Brandon Heller
        """Return our interface object with given string name,
367
           default intf if name is falsy (None, empty string, etc).
368
           or the input intf arg.
369

370
        Having this fcn return its arg for Intf objects makes it
371
        easier to construct functions with flexible input args for
372
        interfaces (those that accept both string names and Intf objects).
373
        """
374 a6bcad8f Bob Lantz
        if not intf:
375
            return self.defaultIntf()
376 12758046 Bob Lantz
        elif type( intf ) is str:
377 a6bcad8f Bob Lantz
            return self.nameToIntf[ intf ]
378
        else:
379 bf208cde Brandon Heller
            return intf
380 efc9a01c Bob Lantz
381 fb2f6523 Bob Lantz
    def connectionsTo( self, node):
382 8856d284 Bob Lantz
        "Return [ intf1, intf2... ] for all intfs that connect self to node."
383 fb2f6523 Bob Lantz
        # We could optimize this if it is important
384
        connections = []
385 8856d284 Bob Lantz
        for intf in self.intfList():
386 a6bcad8f Bob Lantz
            link = intf.link
387 8856d284 Bob Lantz
            if link:
388
                node1, node2 = link.intf1.node, link.intf2.node
389
                if node1 == self and node2 == node:
390
                    connections += [ ( intf, link.intf2 ) ]
391
                elif node1 == node and node2 == self:
392
                    connections += [ ( intf, link.intf1 ) ]
393 fb2f6523 Bob Lantz
        return connections
394 35341142 Bob Lantz
395 10be691b Bob Lantz
    def deleteIntfs( self, checkName=True ):
396
        """Delete all of our interfaces.
397
           checkName: only delete interfaces that contain our name"""
398 086ef80e Bob Lantz
        # In theory the interfaces should go away after we shut down.
399
        # However, this takes time, so we're better off removing them
400
        # explicitly so that we won't get errors if we run before they
401 d44a5843 Bob Lantz
        # have been removed by the kernel. Unfortunately this is very slow,
402
        # at least with Linux kernels before 2.6.33
403 086ef80e Bob Lantz
        for intf in self.intfs.values():
404 10be691b Bob Lantz
            # Protect against deleting hardware interfaces
405
            if ( self.name in intf.name ) or ( not checkName ):
406
                intf.delete()
407
                info( '.' )
408 086ef80e Bob Lantz
409 a6bcad8f Bob Lantz
    # Routing support
410 376bcba4 Brandon Heller
411 80a8fa62 Bob Lantz
    def setARP( self, ip, mac ):
412
        """Add an ARP entry.
413 019bff82 Bob Lantz
           ip: IP address as string
414
           mac: MAC address as string"""
415 121eb449 Bob Lantz
        result = self.cmd( 'arp', '-s', ip, mac )
416 376bcba4 Brandon Heller
        return result
417
418 80a8fa62 Bob Lantz
    def setHostRoute( self, ip, intf ):
419
        """Add route to host.
420
           ip: IP address as dotted decimal
421
           intf: string, interface name"""
422 14ff3ad3 Bob Lantz
        return self.cmd( 'route add -host', ip, 'dev', intf )
423 89bf3103 Brandon Heller
424 84a91a14 Bob Lantz
    def setDefaultRoute( self, intf=None ):
425 80a8fa62 Bob Lantz
        """Set the default route to go through intf.
426 dd21df3c Bob Lantz
           intf: Intf or {dev <intfname> via <gw-ip> ...}"""
427
        # Note setParam won't call us if intf is none
428 12758046 Bob Lantz
        if type( intf ) is str and ' ' in intf:
429 dd21df3c Bob Lantz
            params = intf
430
        else:
431 12758046 Bob Lantz
            params = 'dev %s' % intf
432 dd21df3c Bob Lantz
        self.cmd( 'ip route del default' )
433 12758046 Bob Lantz
        return self.cmd( 'ip route add default', params )
434 89bf3103 Brandon Heller
435 84a91a14 Bob Lantz
    # Convenience and configuration methods
436 54d026f6 Bob Lantz
437 b684ff78 Bob Lantz
    def setMAC( self, mac, intf=None ):
438 a6bcad8f Bob Lantz
        """Set the MAC address for an interface.
439
           intf: intf or intf name
440
           mac: MAC address as string"""
441
        return self.intf( intf ).setMAC( mac )
442
443 b684ff78 Bob Lantz
    def setIP( self, ip, prefixLen=8, intf=None ):
444 a6bcad8f Bob Lantz
        """Set the IP address for an interface.
445 bf208cde Brandon Heller
           intf: intf or intf name
446 a6bcad8f Bob Lantz
           ip: IP address as a string
447
           prefixLen: prefix length, e.g. 8 for /8 or 16M addrs"""
448 a49c85a6 Bob Lantz
        # This should probably be rethought
449
        if '/' not in ip:
450
            ip = '%s/%s' % ( ip, prefixLen )
451
        return self.intf( intf ).setIP( ip )
452 54d026f6 Bob Lantz
453 d44a5843 Bob Lantz
    def IP( self, intf=None ):
454
        "Return IP address of a node or specific interface."
455 b684ff78 Bob Lantz
        return self.intf( intf ).IP()
456 d44a5843 Bob Lantz
457
    def MAC( self, intf=None ):
458
        "Return MAC address of a node or specific interface."
459 0aefb0e0 Bob Lantz
        return self.intf( intf ).MAC()
460 54d026f6 Bob Lantz
461 a6bcad8f Bob Lantz
    def intfIsUp( self, intf=None ):
462 d44a5843 Bob Lantz
        "Check if an interface is up."
463 a6bcad8f Bob Lantz
        return self.intf( intf ).isUp()
464
465 84a91a14 Bob Lantz
    # The reason why we configure things in this way is so
466
    # That the parameters can be listed and documented in
467
    # the config method.
468
    # Dealing with subclasses and superclasses is slightly
469
    # annoying, but at least the information is there!
470
471
    def setParam( self, results, method, **param ):
472 216a4b7c Bob Lantz
        """Internal method: configure a *single* parameter
473
           results: dict of results to update
474
           method: config method name
475
           param: arg=value (ignore if value=None)
476
           value may also be list or dict"""
477 84a91a14 Bob Lantz
        name, value = param.items()[ 0 ]
478
        f = getattr( self, method, None )
479 216a4b7c Bob Lantz
        if not f or value is None:
480 84a91a14 Bob Lantz
            return
481
        if type( value ) is list:
482
            result = f( *value )
483
        elif type( value ) is dict:
484
            result = f( **value )
485 54d026f6 Bob Lantz
        else:
486 84a91a14 Bob Lantz
            result = f( value )
487
        results[ name ] = result
488 216a4b7c Bob Lantz
        return result
489 54d026f6 Bob Lantz
490 8856d284 Bob Lantz
    def config( self, mac=None, ip=None,
491
                defaultRoute=None, lo='up', **_params ):
492 84a91a14 Bob Lantz
        """Configure Node according to (optional) parameters:
493
           mac: MAC address for default interface
494
           ip: IP address for default interface
495
           ifconfig: arbitrary interface configuration
496
           Subclasses should override this method and call
497
           the parent class's config(**params)"""
498
        # If we were overriding this method, we would call
499
        # the superclass config method here as follows:
500 14ff3ad3 Bob Lantz
        # r = Parent.config( **_params )
501 84a91a14 Bob Lantz
        r = {}
502
        self.setParam( r, 'setMAC', mac=mac )
503
        self.setParam( r, 'setIP', ip=ip )
504 e5754ae9 Shaun Crampton
        self.setParam( r, 'setDefaultRoute', defaultRoute=defaultRoute )
505 8856d284 Bob Lantz
        # This should be examined
506
        self.cmd( 'ifconfig lo ' + lo )
507 84a91a14 Bob Lantz
        return r
508
509
    def configDefault( self, **moreParams ):
510
        "Configure with default parameters"
511
        self.params.update( moreParams )
512
        self.config( **self.params )
513
514 a6bcad8f Bob Lantz
    # This is here for backward compatibility
515
    def linkTo( self, node, link=Link ):
516
        """(Deprecated) Link to another node
517
           replace with Link( node1, node2)"""
518
        return link( self, node )
519 89bf3103 Brandon Heller
520
    # Other methods
521 a6bcad8f Bob Lantz
522 03dd914e Bob Lantz
    def intfList( self ):
523
        "List of our interfaces sorted by port number"
524
        return [ self.intfs[ p ] for p in sorted( self.intfs.iterkeys() ) ]
525
526 a6bcad8f Bob Lantz
    def intfNames( self ):
527 03dd914e Bob Lantz
        "The names of our interfaces sorted by port number"
528
        return [ str( i ) for i in self.intfList() ]
529 a6bcad8f Bob Lantz
530 8856d284 Bob Lantz
    def __repr__( self ):
531
        "More informative string representation"
532
        intfs = ( ','.join( [ '%s:%s' % ( i.name, i.IP() )
533 edf60032 Brandon Heller
                              for i in self.intfList() ] ) )
534 8856d284 Bob Lantz
        return '<%s %s: %s pid=%s> ' % (
535 c0095746 Brandon Heller
            self.__class__.__name__, self.name, intfs, self.pid )
536 8856d284 Bob Lantz
537 80a8fa62 Bob Lantz
    def __str__( self ):
538 8856d284 Bob Lantz
        "Abbreviated string representation"
539
        return self.name
540 89bf3103 Brandon Heller
541 14ff3ad3 Bob Lantz
    # Automatic class setup support
542
543
    isSetup = False
544
545
    @classmethod
546
    def checkSetup( cls ):
547
        "Make sure our class and superclasses are set up"
548
        while cls and not getattr( cls, 'isSetup', True ):
549
            cls.setup()
550
            cls.isSetup = True
551
            # Make pylint happy
552
            cls = getattr( type( cls ), '__base__', None )
553
554
    @classmethod
555
    def setup( cls ):
556
        "Make sure our class dependencies are available"
557
        pathCheck( 'mnexec', 'ifconfig', moduleName='Mininet')
558 89bf3103 Brandon Heller
559
560 80a8fa62 Bob Lantz
class Host( Node ):
561 216a4b7c Bob Lantz
    "A host is simply a Node"
562
    pass
563
564
565
class CPULimitedHost( Host ):
566 84a91a14 Bob Lantz
567
    "CPU limited host"
568
569 59542784 Bob Lantz
    def __init__( self, name, sched='cfs', **kwargs ):
570
        Host.__init__( self, name, **kwargs )
571 197b083f Bob Lantz
        # Initialize class if necessary
572
        if not CPULimitedHost.inited:
573
            CPULimitedHost.init()
574 84a91a14 Bob Lantz
        # Create a cgroup and move shell into it
575 197b083f Bob Lantz
        self.cgroup = 'cpu,cpuacct,cpuset:/' + self.name
576 8a622c3a Bob Lantz
        errFail( 'cgcreate -g ' + self.cgroup )
577 197b083f Bob Lantz
        # We don't add ourselves to a cpuset because you must
578
        # specify the cpu and memory placement first
579
        errFail( 'cgclassify -g cpu,cpuacct:/%s %s' % ( self.name, self.pid ) )
580 44af37bc Bob Lantz
        # BL: Setting the correct period/quota is tricky, particularly
581
        # for RT. RT allows very small quotas, but the overhead
582
        # seems to be high. CFS has a mininimum quota of 1 ms, but
583
        # still does better with larger period values.
584
        self.period_us = kwargs.get( 'period_us', 100000 )
585 59542784 Bob Lantz
        self.sched = sched
586 089e8130 Bob Lantz
        self.rtprio = 20
587 84a91a14 Bob Lantz
588
    def cgroupSet( self, param, value, resource='cpu' ):
589
        "Set a cgroup parameter and return its value"
590
        cmd = 'cgset -r %s.%s=%s /%s' % (
591
            resource, param, value, self.name )
592 8e3699ec Bob Lantz
        quietRun( cmd )
593 8a622c3a Bob Lantz
        nvalue = int( self.cgroupGet( param, resource ) )
594
        if nvalue != value:
595
            error( '*** error: cgroupSet: %s set to %s instead of %s\n'
596
                   % ( param, nvalue, value ) )
597
        return nvalue
598 84a91a14 Bob Lantz
599
    def cgroupGet( self, param, resource='cpu' ):
600 14ff3ad3 Bob Lantz
        "Return value of cgroup parameter"
601 84a91a14 Bob Lantz
        cmd = 'cgget -r %s.%s /%s' % (
602
            resource, param, self.name )
603 197b083f Bob Lantz
        return int( quietRun( cmd ).split()[ -1 ] )
604 84a91a14 Bob Lantz
605 28833d86 Bob Lantz
    def cgroupDel( self ):
606
        "Clean up our cgroup"
607
        # info( '*** deleting cgroup', self.cgroup, '\n' )
608 612b21cb Bob Lantz
        _out, _err, exitcode = errRun( 'cgdelete -r ' + self.cgroup )
609 28833d86 Bob Lantz
        return exitcode != 0
610
611 089e8130 Bob Lantz
    def popen( self, *args, **kwargs ):
612
        """Return a Popen() object in node's namespace
613
           args: Popen() args, single list, or string
614
           kwargs: Popen() keyword args"""
615
        # Tell mnexec to execute command in our cgroup
616 e5a15ced Bob Lantz
        mncmd = [ 'mnexec', '-da', str( self.pid ),
617 df600200 Bob Lantz
                  '-g', self.name ]
618 089e8130 Bob Lantz
        if self.sched == 'rt':
619 df600200 Bob Lantz
            mncmd += [ '-r', str( self.rtprio ) ]
620 089e8130 Bob Lantz
        return Host.popen( self, *args, mncmd=mncmd, **kwargs )
621
622 28833d86 Bob Lantz
    def cleanup( self ):
623 59eeeadb Brian O'Connor
        "Clean up Node, then clean up our cgroup"
624
        super( CPULimitedHost, self ).cleanup()
625 28833d86 Bob Lantz
        retry( retries=3, delaySecs=1, fn=self.cgroupDel )
626
627 089e8130 Bob Lantz
    def chrt( self ):
628 84a91a14 Bob Lantz
        "Set RT scheduling priority"
629 089e8130 Bob Lantz
        quietRun( 'chrt -p %s %s' % ( self.rtprio, self.pid ) )
630 84a91a14 Bob Lantz
        result = quietRun( 'chrt -p %s' % self.pid )
631
        firstline = result.split( '\n' )[ 0 ]
632
        lastword = firstline.split( ' ' )[ -1 ]
633 8a622c3a Bob Lantz
        if lastword != 'SCHED_RR':
634
            error( '*** error: could not assign SCHED_RR to %s\n' % self.name )
635 84a91a14 Bob Lantz
        return lastword
636
637 8a622c3a Bob Lantz
    def rtInfo( self, f ):
638
        "Internal method: return parameters for RT bandwidth"
639
        pstr, qstr = 'rt_period_us', 'rt_runtime_us'
640
        # RT uses wall clock time for period and quota
641
        quota = int( self.period_us * f * numCores() )
642
        return pstr, qstr, self.period_us, quota
643
644
    def cfsInfo( self, f):
645
        "Internal method: return parameters for CFS bandwidth"
646
        pstr, qstr = 'cfs_period_us', 'cfs_quota_us'
647
        # CFS uses wall clock time for period and CPU time for quota.
648
        quota = int( self.period_us * f * numCores() )
649
        period = self.period_us
650
        if f > 0 and quota < 1000:
651
            debug( '(cfsInfo: increasing default period) ' )
652
            quota = 1000
653
            period = int( quota / f / numCores() )
654
        return pstr, qstr, period, quota
655
656 216a4b7c Bob Lantz
    # BL comment:
657 14ff3ad3 Bob Lantz
    # This may not be the right API,
658 216a4b7c Bob Lantz
    # since it doesn't specify CPU bandwidth in "absolute"
659
    # units the way link bandwidth is specified.
660
    # We should use MIPS or SPECINT or something instead.
661
    # Alternatively, we should change from system fraction
662
    # to CPU seconds per second, essentially assuming that
663
    # all CPUs are the same.
664 8a622c3a Bob Lantz
665 216a4b7c Bob Lantz
    def setCPUFrac( self, f=-1, sched=None):
666
        """Set overall CPU fraction for this host
667
           f: CPU bandwidth limit (fraction)
668
           sched: 'rt' or 'cfs'
669
           Note 'cfs' requires CONFIG_CFS_BANDWIDTH"""
670
        if not f:
671
            return
672
        if not sched:
673
            sched = self.sched
674
        if sched == 'rt':
675 8a622c3a Bob Lantz
            pstr, qstr, period, quota = self.rtInfo( f )
676 216a4b7c Bob Lantz
        elif sched == 'cfs':
677 8a622c3a Bob Lantz
            pstr, qstr, period, quota = self.cfsInfo( f )
678 216a4b7c Bob Lantz
        else:
679
            return
680
        if quota < 0:
681 84a91a14 Bob Lantz
            # Reset to unlimited
682 216a4b7c Bob Lantz
            quota = -1
683
        # Set cgroup's period and quota
684 8e3699ec Bob Lantz
        self.cgroupSet( pstr, period )
685
        self.cgroupSet( qstr, quota )
686 216a4b7c Bob Lantz
        if sched == 'rt':
687
            # Set RT priority if necessary
688 df600200 Bob Lantz
            self.chrt()
689 8a622c3a Bob Lantz
        info( '(%s %d/%dus) ' % ( sched, quota, period ) )
690 84a91a14 Bob Lantz
691 669e420c Bob Lantz
    def setCPUs( self, cores, mems=0 ):
692 197b083f Bob Lantz
        "Specify (real) cores that our cgroup can run on"
693
        if type( cores ) is list:
694
            cores = ','.join( [ str( c ) for c in cores ] )
695
        self.cgroupSet( resource='cpuset', param='cpus',
696 669e420c Bob Lantz
                        value=cores )
697 197b083f Bob Lantz
        # Memory placement is probably not relevant, but we
698
        # must specify it anyway
699
        self.cgroupSet( resource='cpuset', param='mems',
700 669e420c Bob Lantz
                        value=mems)
701 197b083f Bob Lantz
        # We have to do this here after we've specified
702
        # cpus and mems
703
        errFail( 'cgclassify -g cpuset:/%s %s' % (
704 c0095746 Brandon Heller
                 self.name, self.pid ) )
705 197b083f Bob Lantz
706
    def config( self, cpu=None, cores=None, **params ):
707 84a91a14 Bob Lantz
        """cpu: desired overall system CPU fraction
708 197b083f Bob Lantz
           cores: (real) core(s) this host can run on
709 84a91a14 Bob Lantz
           params: parameters for Node.config()"""
710
        r = Node.config( self, **params )
711 216a4b7c Bob Lantz
        # Was considering cpu={'cpu': cpu , 'sched': sched}, but
712
        # that seems redundant
713 84a91a14 Bob Lantz
        self.setParam( r, 'setCPUFrac', cpu=cpu )
714 197b083f Bob Lantz
        self.setParam( r, 'setCPUs', cores=cores )
715 84a91a14 Bob Lantz
        return r
716
717 197b083f Bob Lantz
    inited = False
718 89bf3103 Brandon Heller
719 197b083f Bob Lantz
    @classmethod
720
    def init( cls ):
721
        "Initialization for CPULimitedHost class"
722
        mountCgroups()
723
        cls.inited = True
724
725
726 84a91a14 Bob Lantz
# Some important things to note:
727
#
728 bf9c6ab7 Bob Lantz
# The "IP" address which setIP() assigns to the switch is not
729 84a91a14 Bob Lantz
# an "IP address for the switch" in the sense of IP routing.
730 bf9c6ab7 Bob Lantz
# Rather, it is the IP address for the control interface,
731
# on the control network, and it is only relevant to the
732
# controller. If you are running in the root namespace
733
# (which is the only way to run OVS at the moment), the
734
# control interface is the loopback interface, and you
735
# normally never want to change its IP address!
736 84a91a14 Bob Lantz
#
737
# In general, you NEVER want to attempt to use Linux's
738
# network stack (i.e. ifconfig) to "assign" an IP address or
739
# MAC address to a switch data port. Instead, you "assign"
740
# the IP and MAC addresses in the controller by specifying
741
# packets that you want to receive or send. The "MAC" address
742
# reported by ifconfig for a switch data port is essentially
743 bf9c6ab7 Bob Lantz
# meaningless. It is important to understand this if you
744
# want to create a functional router using OpenFlow.
745 89bf3103 Brandon Heller
746 80a8fa62 Bob Lantz
class Switch( Node ):
747 6d72a3cb Bob Lantz
    """A Switch is a Node that is running (or has execed?)
748 80a8fa62 Bob Lantz
       an OpenFlow switch."""
749 89bf3103 Brandon Heller
750 8a622c3a Bob Lantz
    portBase = 1  # Switches start with port 1 in OpenFlow
751 0d94548a Bob Lantz
    dpidLen = 16  # digits in dpid passed to switch
752 dd159b4a Bob Lantz
753 84a91a14 Bob Lantz
    def __init__( self, name, dpid=None, opts='', listenPort=None, **params):
754 55cf19c4 Bob Lantz
        """dpid: dpid for switch (or None to derive from name, e.g. s1 -> 1)
755 84a91a14 Bob Lantz
           opts: additional switch options
756
           listenPort: port to listen on for dpctl connections"""
757
        Node.__init__( self, name, **params )
758
        self.dpid = dpid if dpid else self.defaultDpid()
759 121eb449 Bob Lantz
        self.opts = opts
760 ccca871a Brandon Heller
        self.listenPort = listenPort
761 8856d284 Bob Lantz
        if not self.inNamespace:
762 14c19260 Bob Lantz
            self.controlIntf = Intf( 'lo', self, port=0 )
763 84a91a14 Bob Lantz
764
    def defaultDpid( self ):
765
        "Derive dpid from switch name, s1 -> 1"
766 55cf19c4 Bob Lantz
        try:
767 33e39a24 Bob Lantz
            dpid = int( re.findall( r'\d+', self.name )[ 0 ] )
768 55cf19c4 Bob Lantz
            dpid = hex( dpid )[ 2: ]
769 0d94548a Bob Lantz
            dpid = '0' * ( self.dpidLen - len( dpid ) ) + dpid
770 55cf19c4 Bob Lantz
            return dpid
771
        except IndexError:
772
            raise Exception( 'Unable to derive default datapath ID - '
773
                             'please either specify a dpid or use a '
774
                             'canonical switch name such as s23.' )
775 121eb449 Bob Lantz
776 9802686b Bob Lantz
    def defaultIntf( self ):
777 8856d284 Bob Lantz
        "Return control interface"
778
        if self.controlIntf:
779
            return self.controlIntf
780
        else:
781
            return Node.defaultIntf( self )
782 121eb449 Bob Lantz
783
    def sendCmd( self, *cmd, **kwargs ):
784 80a8fa62 Bob Lantz
        """Send command to Node.
785
           cmd: string"""
786 121eb449 Bob Lantz
        kwargs.setdefault( 'printPid', False )
787 1bb4412f Brandon Heller
        if not self.execed:
788 121eb449 Bob Lantz
            return Node.sendCmd( self, *cmd, **kwargs )
789 1bb4412f Brandon Heller
        else:
790 6d72a3cb Bob Lantz
            error( '*** Error: %s has execed and cannot accept commands' %
791 2e089b5e Brandon Heller
                   self.name )
792 89bf3103 Brandon Heller
793 538a856c Bob Lantz
    def connected( self ):
794
        "Is the switch connected to a controller? (override this method)"
795 33e39a24 Bob Lantz
        return False and self  # satisfy pylint
796 538a856c Bob Lantz
797 8856d284 Bob Lantz
    def __repr__( self ):
798
        "More informative string representation"
799
        intfs = ( ','.join( [ '%s:%s' % ( i.name, i.IP() )
800 edf60032 Brandon Heller
                              for i in self.intfList() ] ) )
801 8856d284 Bob Lantz
        return '<%s %s: %s pid=%s> ' % (
802 c0095746 Brandon Heller
            self.__class__.__name__, self.name, intfs, self.pid )
803 89bf3103 Brandon Heller
804 80a8fa62 Bob Lantz
class UserSwitch( Switch ):
805 d44a5843 Bob Lantz
    "User-space switch."
806 89bf3103 Brandon Heller
807 0d94548a Bob Lantz
    dpidLen = 12
808
809 aa554d98 Bob Lantz
    def __init__( self, name, dpopts='--no-slicing', **kwargs ):
810 80a8fa62 Bob Lantz
        """Init.
811 aa554d98 Bob Lantz
           name: name for the switch
812
           dpopts: additional arguments to ofdatapath (--no-slicing)"""
813 d44a5843 Bob Lantz
        Switch.__init__( self, name, **kwargs )
814 57fd19ef Bob Lantz
        pathCheck( 'ofdatapath', 'ofprotocol',
815 edf60032 Brandon Heller
                   moduleName='the OpenFlow reference user switch' +
816
                              '(openflow.org)' )
817 8856d284 Bob Lantz
        if self.listenPort:
818
            self.opts += ' --listen=ptcp:%i ' % self.listenPort
819 804c4bbf Bob Lantz
        self.dpopts = dpopts
820 89bf3103 Brandon Heller
821 14ff3ad3 Bob Lantz
    @classmethod
822
    def setup( cls ):
823 b055728f Brandon Heller
        "Ensure any dependencies are loaded; if not, try to load them."
824 e900a16c Bob Lantz
        if not os.path.exists( '/dev/net/tun' ):
825
            moduleDeps( add=TUN )
826 b055728f Brandon Heller
827 8856d284 Bob Lantz
    def dpctl( self, *args ):
828
        "Run dpctl command"
829
        if not self.listenPort:
830 14c19260 Bob Lantz
            return "can't run dpctl without passive listening port"
831
        return self.cmd( 'dpctl ' + ' '.join( args ) +
832 8856d284 Bob Lantz
                         ' tcp:127.0.0.1:%i' % self.listenPort )
833
834 538a856c Bob Lantz
    def connected( self ):
835
        "Is the switch connected to a controller?"
836
        return 'remote.is-connected=true' in self.dpctl( 'status' )
837
838 3236d33b Andrew Ferguson
    @staticmethod
839
    def TCReapply( intf ):
840
        """Unfortunately user switch and Mininet are fighting
841
           over tc queuing disciplines. To resolve the conflict,
842
           we re-create the user switch's configuration, but as a
843
           leaf of the TCIntf-created configuration."""
844
        if type( intf ) is TCIntf:
845
            ifspeed = 10000000000 # 10 Gbps
846
            minspeed = ifspeed * 0.001
847
848
            res = intf.config( **intf.params )
849
            parent = res['parent']
850
851
            # Re-add qdisc, root, and default classes user switch created, but
852
            # with new parent, as setup by Mininet's TCIntf
853
            intf.tc( "%s qdisc add dev %s " + parent +
854
                     " handle 1: htb default 0xfffe" )
855
            intf.tc( "%s class add dev %s classid 1:0xffff parent 1: htb rate "
856
                     + str(ifspeed) )
857
            intf.tc( "%s class add dev %s classid 1:0xfffe parent 1:0xffff " +
858
                     "htb rate " + str(minspeed) + " ceil " + str(ifspeed) )
859
860 80a8fa62 Bob Lantz
    def start( self, controllers ):
861
        """Start OpenFlow reference user datapath.
862 6d72a3cb Bob Lantz
           Log to /tmp/sN-{ofd,ofp}.log.
863 efc9a01c Bob Lantz
           controllers: list of controller objects"""
864 b69ef234 Bob Lantz
        # Add controllers
865
        clist = ','.join( [ 'tcp:%s:%d' % ( c.IP(), c.port )
866 edf60032 Brandon Heller
                            for c in controllers ] )
867 89bf3103 Brandon Heller
        ofdlog = '/tmp/' + self.name + '-ofd.log'
868
        ofplog = '/tmp/' + self.name + '-ofp.log'
869 80a8fa62 Bob Lantz
        self.cmd( 'ifconfig lo up' )
870 14c19260 Bob Lantz
        intfs = [ str( i ) for i in self.intfList() if not i.IP() ]
871 efc9a01c Bob Lantz
        self.cmd( 'ofdatapath -i ' + ','.join( intfs ) +
872 804c4bbf Bob Lantz
                  ' punix:/tmp/' + self.name + ' -d %s ' % self.dpid +
873
                  self.dpopts +
874 edf60032 Brandon Heller
                  ' 1> ' + ofdlog + ' 2> ' + ofdlog + ' &' )
875 efc9a01c Bob Lantz
        self.cmd( 'ofprotocol unix:/tmp/' + self.name +
876 edf60032 Brandon Heller
                  ' ' + clist +
877
                  ' --fail=closed ' + self.opts +
878
                  ' 1> ' + ofplog + ' 2>' + ofplog + ' &' )
879 be13072f Brian O'Connor
        if "no-slicing" not in self.dpopts:
880
            # Only TCReapply if slicing is enable
881
            sleep(1) # Allow ofdatapath to start before re-arranging qdisc's
882
            for intf in self.intfList():
883
                if not intf.IP():
884
                    self.TCReapply( intf )
885 89bf3103 Brandon Heller
886 80a8fa62 Bob Lantz
    def stop( self ):
887
        "Stop OpenFlow reference user datapath."
888
        self.cmd( 'kill %ofdatapath' )
889
        self.cmd( 'kill %ofprotocol' )
890 086ef80e Bob Lantz
        self.deleteIntfs()
891 1bb4412f Brandon Heller
892 82b72072 Bob Lantz
893 8a7d42db Bob Lantz
class OVSLegacyKernelSwitch( Switch ):
894
    """Open VSwitch legacy kernel-space switch using ovs-openflowd.
895 80a8fa62 Bob Lantz
       Currently only works in the root namespace."""
896 f7c2df25 Brandon Heller
897 086ef80e Bob Lantz
    def __init__( self, name, dp=None, **kwargs ):
898 80a8fa62 Bob Lantz
        """Init.
899 e3a2ef01 Bob Lantz
           name: name for switch
900 6d72a3cb Bob Lantz
           dp: netlink id (0, 1, 2, ...)
901 d44a5843 Bob Lantz
           defaultMAC: default MAC as unsigned int; random value if None"""
902
        Switch.__init__( self, name, **kwargs )
903 612b21cb Bob Lantz
        self.dp = dp if dp else self.name
904 121eb449 Bob Lantz
        self.intf = self.dp
905 d44a5843 Bob Lantz
        if self.inNamespace:
906
            error( "OVSKernelSwitch currently only works"
907 edf60032 Brandon Heller
                   " in the root namespace.\n" )
908 d44a5843 Bob Lantz
            exit( 1 )
909 f7c2df25 Brandon Heller
910 14ff3ad3 Bob Lantz
    @classmethod
911
    def setup( cls ):
912 b055728f Brandon Heller
        "Ensure any dependencies are loaded; if not, try to load them."
913 f9654e56 Bob Lantz
        pathCheck( 'ovs-dpctl', 'ovs-openflowd',
914 edf60032 Brandon Heller
                   moduleName='Open vSwitch (openvswitch.org)')
915 f9654e56 Bob Lantz
        moduleDeps( subtract=OF_KMOD, add=OVS_KMOD )
916 b055728f Brandon Heller
917 80a8fa62 Bob Lantz
    def start( self, controllers ):
918
        "Start up kernel datapath."
919 f7c2df25 Brandon Heller
        ofplog = '/tmp/' + self.name + '-ofp.log'
920 80a8fa62 Bob Lantz
        quietRun( 'ifconfig lo up' )
921 f7c2df25 Brandon Heller
        # Delete local datapath if it exists;
922
        # then create a new one monitoring the given interfaces
923 8856d284 Bob Lantz
        self.cmd( 'ovs-dpctl del-dp ' + self.dp )
924 121eb449 Bob Lantz
        self.cmd( 'ovs-dpctl add-dp ' + self.dp )
925 14c19260 Bob Lantz
        intfs = [ str( i ) for i in self.intfList() if not i.IP() ]
926 5cc80828 Bob Lantz
        self.cmd( 'ovs-dpctl', 'add-if', self.dp, ' '.join( intfs ) )
927 f7c2df25 Brandon Heller
        # Run protocol daemon
928 b69ef234 Bob Lantz
        clist = ','.join( [ 'tcp:%s:%d' % ( c.IP(), c.port )
929 edf60032 Brandon Heller
                            for c in controllers ] )
930 121eb449 Bob Lantz
        self.cmd( 'ovs-openflowd ' + self.dp +
931 edf60032 Brandon Heller
                  ' ' + clist +
932
                  ' --fail=secure ' + self.opts +
933
                  ' --datapath-id=' + self.dpid +
934
                  ' 1>' + ofplog + ' 2>' + ofplog + '&' )
935 f7c2df25 Brandon Heller
        self.execed = False
936
937 80a8fa62 Bob Lantz
    def stop( self ):
938
        "Terminate kernel datapath."
939 121eb449 Bob Lantz
        quietRun( 'ovs-dpctl del-dp ' + self.dp )
940 75d72d96 Bob Lantz
        self.cmd( 'kill %ovs-openflowd' )
941 086ef80e Bob Lantz
        self.deleteIntfs()
942 f7c2df25 Brandon Heller
943
944 8a7d42db Bob Lantz
class OVSSwitch( Switch ):
945
    "Open vSwitch switch. Depends on ovs-vsctl."
946
947 153d598d Murphy McCauley
    def __init__( self, name, failMode='secure', datapath='kernel', **params ):
948 8a7d42db Bob Lantz
        """Init.
949
           name: name for switch
950 153d598d Murphy McCauley
           failMode: controller loss behavior (secure|open)
951
           datapath: userspace or kernel mode (kernel|user)"""
952 84a91a14 Bob Lantz
        Switch.__init__( self, name, **params )
953 92b601ab Bob Lantz
        self.failMode = failMode
954 153d598d Murphy McCauley
        self.datapath = datapath
955 14ff3ad3 Bob Lantz
956
    @classmethod
957
    def setup( cls ):
958 8a7d42db Bob Lantz
        "Make sure Open vSwitch is installed and working"
959 14ff3ad3 Bob Lantz
        pathCheck( 'ovs-vsctl',
960 edf60032 Brandon Heller
                   moduleName='Open vSwitch (openvswitch.org)')
961 28c2cdc2 Bob Lantz
        # This should no longer be needed, and it breaks
962
        # with OVS 1.7 which has renamed the kernel module:
963
        #  moduleDeps( subtract=OF_KMOD, add=OVS_KMOD )
964 8a7d42db Bob Lantz
        out, err, exitcode = errRun( 'ovs-vsctl -t 1 show' )
965
        if exitcode:
966 14ff3ad3 Bob Lantz
            error( out + err +
967 8a7d42db Bob Lantz
                   'ovs-vsctl exited with code %d\n' % exitcode +
968
                   '*** Error connecting to ovs-db with ovs-vsctl\n'
969
                   'Make sure that Open vSwitch is installed, '
970
                   'that ovsdb-server is running, and that\n'
971
                   '"ovs-vsctl show" works correctly.\n'
972 14ff3ad3 Bob Lantz
                   'You may wish to try '
973
                   '"service openvswitch-switch start".\n' )
974 8a7d42db Bob Lantz
            exit( 1 )
975
976 8856d284 Bob Lantz
    def dpctl( self, *args ):
977 229f112f Bob Lantz
        "Run ovs-ofctl command"
978
        return self.cmd( 'ovs-ofctl', args[ 0 ], self, *args[ 1: ] )
979 8856d284 Bob Lantz
980 1aec55d9 Bob Lantz
    @staticmethod
981
    def TCReapply( intf ):
982
        """Unfortunately OVS and Mininet are fighting
983
           over tc queuing disciplines. As a quick hack/
984
           workaround, we clear OVS's and reapply our own."""
985
        if type( intf ) is TCIntf:
986
            intf.config( **intf.params )
987
988 8856d284 Bob Lantz
    def attach( self, intf ):
989
        "Connect a data port"
990
        self.cmd( 'ovs-vsctl add-port', self, intf )
991
        self.cmd( 'ifconfig', intf, 'up' )
992 1aec55d9 Bob Lantz
        self.TCReapply( intf )
993 8856d284 Bob Lantz
994
    def detach( self, intf ):
995
        "Disconnect a data port"
996
        self.cmd( 'ovs-vsctl del-port', self, intf )
997
998 538a856c Bob Lantz
    def controllerUUIDs( self ):
999
        "Return ovsdb UUIDs for our controllers"
1000
        uuids = []
1001
        controllers = self.cmd( 'ovs-vsctl -- get Bridge', self,
1002
                               'Controller' ).strip()
1003
        if controllers.startswith( '[' ) and controllers.endswith( ']' ):
1004
            controllers = controllers[ 1 : -1 ]
1005
            uuids = [ c.strip() for c in controllers.split( ',' ) ]
1006
        return uuids
1007
1008
    def connected( self ):
1009
        "Are we connected to at least one of our controllers?"
1010
        results = [ 'true' in self.cmd( 'ovs-vsctl -- get Controller',
1011
                                         uuid, 'is_connected' )
1012
                    for uuid in self.controllerUUIDs() ]
1013
        return reduce( or_, results, False )
1014
1015 8a7d42db Bob Lantz
    def start( self, controllers ):
1016
        "Start up a new OVS OpenFlow switch using ovs-vsctl"
1017 14ff3ad3 Bob Lantz
        if self.inNamespace:
1018 8856d284 Bob Lantz
            raise Exception(
1019 14ff3ad3 Bob Lantz
                'OVS kernel switch does not work in a namespace' )
1020 8856d284 Bob Lantz
        # We should probably call config instead, but this
1021
        # requires some rethinking...
1022
        self.cmd( 'ifconfig lo up' )
1023 8a7d42db Bob Lantz
        # Annoyingly, --if-exists option seems not to work
1024 8856d284 Bob Lantz
        self.cmd( 'ovs-vsctl del-br', self )
1025
        self.cmd( 'ovs-vsctl add-br', self )
1026 153d598d Murphy McCauley
        if self.datapath == 'user':
1027
            self.cmd( 'ovs-vsctl set bridge', self,'datapath_type=netdev' )
1028 1a658054 Bob Lantz
        int( self.dpid, 16 ) # DPID must be a hex string
1029 b97c1dbd Bob Lantz
        self.cmd( 'ovs-vsctl -- set Bridge', self,
1030
                  'other_config:datapath-id=' + self.dpid )
1031 92b601ab Bob Lantz
        self.cmd( 'ovs-vsctl set-fail-mode', self, self.failMode )
1032 8856d284 Bob Lantz
        for intf in self.intfList():
1033
            if not intf.IP():
1034
                self.attach( intf )
1035 8a7d42db Bob Lantz
        # Add controllers
1036 915c6d4a Brandon Heller
        clist = ' '.join( [ 'tcp:%s:%d' % ( c.IP(), c.port )
1037 14ff3ad3 Bob Lantz
                            for c in controllers ] )
1038 3744638e Brandon Heller
        if self.listenPort:
1039
            clist += ' ptcp:%s' % self.listenPort
1040 8856d284 Bob Lantz
        self.cmd( 'ovs-vsctl set-controller', self, clist )
1041 877e7efb Bob Lantz
        # Reconnect quickly to controllers (1s vs. 15s max_backoff)
1042 538a856c Bob Lantz
        for uuid in self.controllerUUIDs():
1043
            if uuid.count( '-' ) != 4:
1044
                # Doesn't look like a UUID
1045
                continue
1046
            uuid = uuid.strip()
1047
            self.cmd( 'ovs-vsctl set Controller', uuid,
1048
                      'max_backoff=1000' )
1049 8a7d42db Bob Lantz
1050
    def stop( self ):
1051
        "Terminate OVS switch."
1052 8856d284 Bob Lantz
        self.cmd( 'ovs-vsctl del-br', self )
1053 3f61ea71 Bob Lantz
        self.deleteIntfs()
1054 8a7d42db Bob Lantz
1055
OVSKernelSwitch = OVSSwitch
1056
1057 84a91a14 Bob Lantz
1058 27da832d Rich Lane
class IVSSwitch(Switch):
1059 71ffb002 Rich Lane
    """IVS virtual switch"""
1060 27da832d Rich Lane
1061
    def __init__( self, name, **kwargs ):
1062
        Switch.__init__( self, name, **kwargs )
1063
1064
    @classmethod
1065
    def setup( cls ):
1066
        "Make sure IVS is installed"
1067
        pathCheck( 'ivs-ctl', 'ivs',
1068
                   moduleName="Indigo Virtual Switch (projectfloodlight.org)" )
1069
        out, err, exitcode = errRun( 'ivs-ctl show' )
1070
        if exitcode:
1071
            error( out + err +
1072
                   'ivs-ctl exited with code %d\n' % exitcode +
1073
                   '*** The openvswitch kernel module might '
1074
                   'not be loaded. Try modprobe openvswitch.\n' )
1075
            exit( 1 )
1076
1077
    def start( self, controllers ):
1078
        "Start up a new IVS switch"
1079
        args = ['ivs']
1080
        args.extend( ['--name', self.name] )
1081
        args.extend( ['--dpid', self.dpid] )
1082
        args.extend( ['--verbose'] )
1083
        for intf in self.intfs.values():
1084
            if not intf.IP():
1085
                args.extend( ['-i', intf.name] )
1086
        for c in controllers:
1087
            args.extend( ['-c', '%s:%d' % (c.IP(), c.port)] )
1088 91261b27 Rich Lane
        if self.listenPort:
1089
            args.extend( ['--listen', '127.0.0.1:%i' % self.listenPort] )
1090 27da832d Rich Lane
1091 0a543602 Rich Lane
        logfile = '/tmp/ivs.%s.log' % self.name
1092
1093
        self.cmd( ' '.join(args) + ' >' + logfile + ' 2>&1 </dev/null &' )
1094 27da832d Rich Lane
1095
    def stop( self ):
1096
        "Terminate IVS switch."
1097 0a543602 Rich Lane
        self.cmd( 'kill %ivs' )
1098 27da832d Rich Lane
        self.deleteIntfs()
1099
1100
    def attach( self, intf ):
1101
        "Connect a data port"
1102
        self.cmd( 'ivs-ctl', 'add-port', '--datapath', self.name, intf )
1103
1104
    def detach( self, intf ):
1105
        "Disconnect a data port"
1106
        self.cmd( 'ivs-ctl', 'del-port', '--datapath', self.name, intf )
1107
1108
    def dpctl( self, *args ):
1109
        "Run dpctl command"
1110 91261b27 Rich Lane
        if not self.listenPort:
1111
            return "can't run dpctl without passive listening port"
1112 803c0a6e Rich Lane
        return self.cmd( 'ovs-ofctl ' + ' '.join( args ) +
1113 91261b27 Rich Lane
                         ' tcp:127.0.0.1:%i' % self.listenPort )
1114 27da832d Rich Lane
1115
1116 80a8fa62 Bob Lantz
class Controller( Node ):
1117 7c371cf3 Bob Lantz
    """A Controller is a Node that is running (or has execed?) an
1118 80a8fa62 Bob Lantz
       OpenFlow controller."""
1119 1bb4412f Brandon Heller
1120 57fd19ef Bob Lantz
    def __init__( self, name, inNamespace=False, command='controller',
1121 edf60032 Brandon Heller
                  cargs='-v ptcp:%d', cdir=None, ip="127.0.0.1",
1122
                  port=6633, **params ):
1123 57fd19ef Bob Lantz
        self.command = command
1124 1bb4412f Brandon Heller
        self.cargs = cargs
1125
        self.cdir = cdir
1126 84a91a14 Bob Lantz
        self.ip = ip
1127 60d9ead6 David Erickson
        self.port = port
1128 086ef80e Bob Lantz
        Node.__init__( self, name, inNamespace=inNamespace,
1129 edf60032 Brandon Heller
                       ip=ip, **params  )
1130 ec969b7f Bob Lantz
        self.cmd( 'ifconfig lo up' )  # Shouldn't be necessary
1131
        self.checkListening()
1132
1133
    def checkListening( self ):
1134
        "Make sure no controllers are running on our port"
1135 b453e006 Brandon Heller
        # Verify that Telnet is installed first:
1136 c8b85746 Bob Lantz
        out, _err, returnCode = errRun( "which telnet" )
1137 b453e006 Brandon Heller
        if 'telnet' not in out or returnCode != 0:
1138
            raise Exception( "Error running telnet to check for listening "
1139
                             "controllers; please check that it is "
1140
                             "installed." )
1141 ec969b7f Bob Lantz
        listening = self.cmd( "echo A | telnet -e A %s %d" %
1142
                              ( self.ip, self.port ) )
1143
        if 'Unable' not in listening:
1144
            servers = self.cmd( 'netstat -atp' ).split( '\n' )
1145
            pstr = ':%d ' % self.port
1146 28c2cdc2 Bob Lantz
            clist = servers[ 0:1 ] + [ s for s in servers if pstr in s ]
1147 ec969b7f Bob Lantz
            raise Exception( "Please shut down the controller which is"
1148
                             " running on port %d:\n" % self.port +
1149 28c2cdc2 Bob Lantz
                             '\n'.join( clist ) )
1150 1bb4412f Brandon Heller
1151 80a8fa62 Bob Lantz
    def start( self ):
1152
        """Start <controller> <args> on controller.
1153
           Log to /tmp/cN.log"""
1154 57fd19ef Bob Lantz
        pathCheck( self.command )
1155 1bb4412f Brandon Heller
        cout = '/tmp/' + self.name + '.log'
1156
        if self.cdir is not None:
1157 efc9a01c Bob Lantz
            self.cmd( 'cd ' + self.cdir )
1158 57fd19ef Bob Lantz
        self.cmd( self.command + ' ' + self.cargs % self.port +
1159 edf60032 Brandon Heller
                  ' 1>' + cout + ' 2>' + cout + '&' )
1160 723d068c Brandon Heller
        self.execed = False
1161 89bf3103 Brandon Heller
1162 80a8fa62 Bob Lantz
    def stop( self ):
1163
        "Stop controller."
1164 57fd19ef Bob Lantz
        self.cmd( 'kill %' + self.command )
1165 1bb4412f Brandon Heller
        self.terminate()
1166 89bf3103 Brandon Heller
1167 d44a5843 Bob Lantz
    def IP( self, intf=None ):
1168 80a8fa62 Bob Lantz
        "Return IP address of the Controller"
1169 a6bcad8f Bob Lantz
        if self.intfs:
1170
            ip = Node.IP( self, intf )
1171
        else:
1172 84a91a14 Bob Lantz
            ip = self.ip
1173 d44a5843 Bob Lantz
        return ip
1174 723d068c Brandon Heller
1175 8856d284 Bob Lantz
    def __repr__( self ):
1176
        "More informative string representation"
1177
        return '<%s %s: %s:%s pid=%s> ' % (
1178 c0095746 Brandon Heller
            self.__class__.__name__, self.name,
1179
            self.IP(), self.port, self.pid )
1180 89bf3103 Brandon Heller
1181 84a91a14 Bob Lantz
1182 9addfc13 Bob Lantz
class OVSController( Controller ):
1183
    "Open vSwitch controller"
1184
    def __init__( self, name, command='ovs-controller', **kwargs ):
1185
        Controller.__init__( self, name, command=command, **kwargs )
1186 80a8fa62 Bob Lantz
1187
1188
class NOX( Controller ):
1189
    "Controller to run a NOX application."
1190
1191 2db4268b Bob Lantz
    def __init__( self, name, *noxArgs, **kwargs ):
1192 80a8fa62 Bob Lantz
        """Init.
1193
           name: name to give controller
1194 2db4268b Bob Lantz
           noxArgs: arguments (strings) to pass to NOX"""
1195 80a8fa62 Bob Lantz
        if not noxArgs:
1196 2db4268b Bob Lantz
            warn( 'warning: no NOX modules specified; '
1197
                  'running packetdump only\n' )
1198 80a8fa62 Bob Lantz
            noxArgs = [ 'packetdump' ]
1199 2db4268b Bob Lantz
        elif type( noxArgs ) not in ( list, tuple ):
1200 f32a5468 Brandon Heller
            noxArgs = [ noxArgs ]
1201 137ec305 Bob Lantz
1202 4f4f1dd2 Brandon Heller
        if 'NOX_CORE_DIR' not in os.environ:
1203 137ec305 Bob Lantz
            exit( 'exiting; please set missing NOX_CORE_DIR env var' )
1204 80a8fa62 Bob Lantz
        noxCoreDir = os.environ[ 'NOX_CORE_DIR' ]
1205 4f4f1dd2 Brandon Heller
1206 80a8fa62 Bob Lantz
        Controller.__init__( self, name,
1207 edf60032 Brandon Heller
                             command=noxCoreDir + '/nox_core',
1208
                             cargs='--libdir=/usr/local/lib -v -i ptcp:%s ' +
1209 2e089b5e Brandon Heller
                             ' '.join( noxArgs ),
1210 edf60032 Brandon Heller
                             cdir=noxCoreDir,
1211
                             **kwargs )
1212 80a8fa62 Bob Lantz
1213
1214
class RemoteController( Controller ):
1215
    "Controller running outside of Mininet's control."
1216
1217 0eba655d Bob Lantz
    def __init__( self, name, ip='127.0.0.1',
1218 edf60032 Brandon Heller
                  port=6633, **kwargs):
1219 80a8fa62 Bob Lantz
        """Init.
1220
           name: name to give controller
1221 2aafefc2 Bob Lantz
           ip: the IP address where the remote controller is
1222 80a8fa62 Bob Lantz
           listening
1223
           port: the port where the remote controller is listening"""
1224 edf60032 Brandon Heller
        Controller.__init__( self, name, ip=ip, port=port, **kwargs )
1225 80a8fa62 Bob Lantz
1226
    def start( self ):
1227
        "Overridden to do nothing."
1228 60d9ead6 David Erickson
        return
1229
1230 80a8fa62 Bob Lantz
    def stop( self ):
1231
        "Overridden to do nothing."
1232 60d9ead6 David Erickson
        return
1233 2b35a2ca James Page
1234
    def checkListening( self ):
1235 54c51c02 Bob Lantz
        "Warn if remote controller is not accessible"
1236 2b35a2ca James Page
        listening = self.cmd( "echo A | telnet -e A %s %d" %
1237
                              ( self.ip, self.port ) )
1238
        if 'Unable' in listening:
1239 54c51c02 Bob Lantz
            warn( "Unable to contact the remote controller"
1240 2e089b5e Brandon Heller
                  " at %s:%d\n" % ( self.ip, self.port ) )