Statistics
| Branch: | Tag: | Revision:

mininet / mininet / node.py @ master

History | View | Annotate | Download (58.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 6e5ac34b Bob Lantz
    functionality are provided in the examples/ directory. By default,
15
    hosts share the root file system, but they may also specify private
16
    directories.
17 7d4b7b7f Bob Lantz

18 0dbfd3a6 Bob Lantz
CPULimitedHost: a virtual host whose CPU bandwidth is limited by
19
    RT or CFS bandwidth limiting.
20

21 7d4b7b7f Bob Lantz
Switch: superclass for switch nodes.
22

23
UserSwitch: a switch using the user-space switch from the OpenFlow
24
    reference implementation.
25

26 79c944ae Bob Lantz
OVSSwitch: a switch using the Open vSwitch OpenFlow-compatible switch
27 7d4b7b7f Bob Lantz
    implementation (openvswitch.org).
28

29 79c944ae Bob Lantz
OVSBridge: an Ethernet bridge implemented using Open vSwitch.
30
    Supports STP.
31

32
IVSSwitch: OpenFlow switch using the Indigo Virtual Switch.
33

34 7d4b7b7f Bob Lantz
Controller: superclass for OpenFlow controllers. The default controller
35
    is controller(8) from the reference implementation.
36

37 79c944ae Bob Lantz
OVSController: The test controller from Open vSwitch.
38

39 7d4b7b7f Bob Lantz
NOXController: a controller node using NOX (noxrepo.org).
40

41 c2be20f0 Bob Lantz
Ryu: The Ryu controller (https://osrg.github.io/ryu/)
42 79c944ae Bob Lantz

43 7d4b7b7f Bob Lantz
RemoteController: a remote controller node, which may use any
44 7c371cf3 Bob Lantz
    arbitrary OpenFlow-compatible controller, and which is not
45 79c944ae Bob Lantz
    created or managed by Mininet.
46 31b43002 Bob Lantz

47 80be5642 Bob Lantz
Future enhancements:
48

49
- Possibly make Node, Switch and Controller more abstract so that
50
  they can be used for both local and remote nodes
51

52
- Create proxy objects for remote nodes (Mininet: Cluster Edition)
53 7d4b7b7f Bob Lantz
"""
54 89bf3103 Brandon Heller
55 723d068c Brandon Heller
import os
56 549f1ebc Bob Lantz
import pty
57 75d72d96 Bob Lantz
import re
58 723d068c Brandon Heller
import signal
59
import select
60 50774e40 Bob Lantz
from subprocess import Popen, PIPE
61 3236d33b Andrew Ferguson
from time import sleep
62 60d9ead6 David Erickson
63 2db4268b Bob Lantz
from mininet.log import info, error, warn, debug
64 197b083f Bob Lantz
from mininet.util import ( quietRun, errRun, errFail, moveIntf, isShellBuiltin,
65 edf60032 Brandon Heller
                           numCores, retry, mountCgroups )
66 79c944ae Bob Lantz
from mininet.moduledeps import moduleDeps, pathCheck, TUN
67 c069542c Bob Lantz
from mininet.link import Link, Intf, TCIntf, OVSIntf
68 f1e42ba5 Cody
from re import findall
69
from distutils.version import StrictVersion
70 2fffa0bb Brandon Heller
71 80a8fa62 Bob Lantz
class Node( object ):
72
    """A virtual network node is simply a shell in a network namespace.
73
       We communicate with it using pipes."""
74 6d72a3cb Bob Lantz
75 dd159b4a Bob Lantz
    portBase = 0  # Nodes always start with eth0/port0, even in OF 1.0
76 54d026f6 Bob Lantz
77 84a91a14 Bob Lantz
    def __init__( self, name, inNamespace=True, **params ):
78 086ef80e Bob Lantz
        """name: name of node
79
           inNamespace: in network namespace?
80 736db20c cody burkard
           privateDirs: list of private directory strings or tuples
81 84a91a14 Bob Lantz
           params: Node parameters (see config() for details)"""
82
83
        # Make sure class actually works
84
        self.checkSetup()
85
86 c265deed Bob Lantz
        self.name = params.get( 'name', name )
87 736db20c cody burkard
        self.privateDirs = params.get( 'privateDirs', [] )
88 c265deed Bob Lantz
        self.inNamespace = params.get( 'inNamespace', inNamespace )
89 84a91a14 Bob Lantz
90
        # Stash configuration parameters for future reference
91
        self.params = params
92
93
        self.intfs = {}  # dict of port numbers to interfaces
94
        self.ports = {}  # dict of interfaces to port numbers
95
                         # replace with Port objects, eventually ?
96
        self.nameToIntf = {}  # dict of interface names to Intfs
97
98 14ff3ad3 Bob Lantz
        # Make pylint happy
99
        ( self.shell, self.execed, self.pid, self.stdin, self.stdout,
100 33d548b4 Brandon Heller
            self.lastPid, self.lastCmd, self.pollOut ) = (
101
                None, None, None, None, None, None, None, None )
102 14ff3ad3 Bob Lantz
        self.waiting = False
103
        self.readbuf = ''
104
105 84a91a14 Bob Lantz
        # Start command interpreter shell
106
        self.startShell()
107 736db20c cody burkard
        self.mountPrivateDirs()
108 84a91a14 Bob Lantz
109
    # File descriptor to node mapping support
110
    # Class variables and methods
111
112
    inToNode = {}  # mapping of input fds to nodes
113
    outToNode = {}  # mapping of output fds to nodes
114
115
    @classmethod
116
    def fdToNode( cls, fd ):
117
        """Return node corresponding to given file descriptor.
118
           fd: file descriptor
119
           returns: node"""
120
        node = cls.outToNode.get( fd )
121
        return node or cls.inToNode.get( fd )
122
123
    # Command support via shell process in namespace
124 c265deed Bob Lantz
    def startShell( self, mnopts=None ):
125 84a91a14 Bob Lantz
        "Start a shell process for running commands"
126
        if self.shell:
127 c265deed Bob Lantz
            error( "%s: shell is already running\n" % self.name )
128 84a91a14 Bob Lantz
            return
129 216a4b7c Bob Lantz
        # mnexec: (c)lose descriptors, (d)etach from tty,
130 14ff3ad3 Bob Lantz
        # (p)rint pid, and run in (n)amespace
131 c265deed Bob Lantz
        opts = '-cd' if mnopts is None else mnopts
132 89bf3103 Brandon Heller
        if self.inNamespace:
133 b14b1ee4 Bob Lantz
            opts += 'n'
134 d90a4551 Bob Lantz
        # bash -i: force interactive
135 1bf1a4d5 Bob Lantz
        # -s: pass $* to shell, and make process easy to find in ps
136 82e0e9f3 Bob Lantz
        # prompt is set to sentinel chr( 127 )
137 7a3159c9 Bob Lantz
        cmd = [ 'mnexec', opts, 'env', 'PS1=' + chr( 127 ),
138 d90a4551 Bob Lantz
                'bash', '--norc', '-is', 'mininet:' + self.name ]
139 549f1ebc Bob Lantz
        # Spawn a shell subprocess in a pseudo-tty, to disable buffering
140
        # in the subprocess and insulate it from signals (e.g. SIGINT)
141
        # received by the parent
142
        master, slave = pty.openpty()
143 c265deed Bob Lantz
        self.shell = self._popen( cmd, stdin=slave, stdout=slave, stderr=slave,
144 549f1ebc Bob Lantz
                                  close_fds=False )
145 c265deed Bob Lantz
        self.stdin = os.fdopen( master, 'rw' )
146 549f1ebc Bob Lantz
        self.stdout = self.stdin
147 121eb449 Bob Lantz
        self.pid = self.shell.pid
148 89bf3103 Brandon Heller
        self.pollOut = select.poll()
149 80a8fa62 Bob Lantz
        self.pollOut.register( self.stdout )
150 89bf3103 Brandon Heller
        # Maintain mapping between file descriptors and nodes
151 bcacfc05 Bob Lantz
        # This is useful for monitoring multiple nodes
152 89bf3103 Brandon Heller
        # using select.poll()
153 80a8fa62 Bob Lantz
        self.outToNode[ self.stdout.fileno() ] = self
154
        self.inToNode[ self.stdin.fileno() ] = self
155 89bf3103 Brandon Heller
        self.execed = False
156 bcacfc05 Bob Lantz
        self.lastCmd = None
157
        self.lastPid = None
158 ec7b211c Bob Lantz
        self.readbuf = ''
159 549f1ebc Bob Lantz
        # Wait for prompt
160
        while True:
161
            data = self.read( 1024 )
162 771850b9 Bob Lantz
            if data[ -1 ] == chr( 127 ):
163 549f1ebc Bob Lantz
                break
164
            self.pollOut.poll()
165
        self.waiting = False
166 d90a4551 Bob Lantz
        # +m: disable job control notification
167
        self.cmd( 'unset HISTFILE; stty -echo; set +m' )
168 89bf3103 Brandon Heller
169 736db20c cody burkard
    def mountPrivateDirs( self ):
170
        "mount private directories"
171 a38896c2 Bob Lantz
        # Avoid expanding a string into a list of chars
172
        assert not isinstance( self.privateDirs, basestring )
173 736db20c cody burkard
        for directory in self.privateDirs:
174
            if isinstance( directory, tuple ):
175
                # mount given private directory
176 a562ca1b Bob Lantz
                privateDir = directory[ 1 ] % self.__dict__
177 736db20c cody burkard
                mountPoint = directory[ 0 ]
178
                self.cmd( 'mkdir -p %s' % privateDir )
179
                self.cmd( 'mkdir -p %s' % mountPoint )
180
                self.cmd( 'mount --bind %s %s' %
181
                               ( privateDir, mountPoint ) )
182
            else:
183
                # mount temporary filesystem on directory
184 a562ca1b Bob Lantz
                self.cmd( 'mkdir -p %s' % directory )
185 736db20c cody burkard
                self.cmd( 'mount -n -t tmpfs tmpfs %s' % directory )
186
187 6a363f65 cody burkard
    def unmountPrivateDirs( self ):
188
        "mount private directories"
189
        for directory in self.privateDirs:
190
            if isinstance( directory, tuple ):
191
                self.cmd( 'umount ', directory[ 0 ] )
192
            else:
193
                self.cmd( 'umount ', directory )
194
195 c265deed Bob Lantz
    def _popen( self, cmd, **params ):
196
        """Internal method: spawn and return a process
197
            cmd: command to run (list)
198
            params: parameters to Popen()"""
199 b1ec912d Bob Lantz
        # Leave this is as an instance method for now
200
        assert self
201 c265deed Bob Lantz
        return Popen( cmd, **params )
202
203 80a8fa62 Bob Lantz
    def cleanup( self ):
204
        "Help python collect its garbage."
205 574d634f Bob Lantz
        # We used to do this, but it slows us down:
206 10be691b Bob Lantz
        # Intfs may end up in root NS
207 574d634f Bob Lantz
        # for intfName in self.intfNames():
208
        # if self.name in intfName:
209
        # quietRun( 'ip link del ' + intfName )
210 89bf3103 Brandon Heller
        self.shell = None
211
212
    # Subshell I/O, commands and control
213 163a6cf3 Bob Lantz
214 28f46c8d Bob Lantz
    def read( self, maxbytes=1024 ):
215 b78b99b6 Bob Lantz
        """Buffered read from node, potentially blocking.
216 28f46c8d Bob Lantz
           maxbytes: maximum number of bytes to return"""
217 ec7b211c Bob Lantz
        count = len( self.readbuf )
218 28f46c8d Bob Lantz
        if count < maxbytes:
219
            data = os.read( self.stdout.fileno(), maxbytes - count )
220 ec7b211c Bob Lantz
            self.readbuf += data
221 28f46c8d Bob Lantz
        if maxbytes >= len( self.readbuf ):
222 ec7b211c Bob Lantz
            result = self.readbuf
223
            self.readbuf = ''
224
        else:
225 28f46c8d Bob Lantz
            result = self.readbuf[ :maxbytes ]
226
            self.readbuf = self.readbuf[ maxbytes: ]
227 ec7b211c Bob Lantz
        return result
228
229
    def readline( self ):
230 b78b99b6 Bob Lantz
        """Buffered readline from node, potentially blocking.
231 ec7b211c Bob Lantz
           returns: line (minus newline) or None"""
232
        self.readbuf += self.read( 1024 )
233
        if '\n' not in self.readbuf:
234
            return None
235
        pos = self.readbuf.find( '\n' )
236 0bd5c651 Brandon Heller
        line = self.readbuf[ 0: pos ]
237 ec7b211c Bob Lantz
        self.readbuf = self.readbuf[ pos + 1: ]
238
        return line
239 80a8fa62 Bob Lantz
240
    def write( self, data ):
241
        """Write data to node.
242
           data: string"""
243
        os.write( self.stdin.fileno(), data )
244
245
    def terminate( self ):
246 6d72a3cb Bob Lantz
        "Send kill signal to Node and clean up after it."
247 6a363f65 cody burkard
        self.unmountPrivateDirs()
248 15146d90 Brian O'Connor
        if self.shell:
249 c265deed Bob Lantz
            if self.shell.poll() is None:
250
                os.killpg( self.shell.pid, signal.SIGHUP )
251 89bf3103 Brandon Heller
        self.cleanup()
252
253 b1ec912d Bob Lantz
    def stop( self, deleteIntfs=False ):
254
        """Stop node.
255
           deleteIntfs: delete interfaces? (False)"""
256
        if deleteIntfs:
257
            self.deleteIntfs()
258 89bf3103 Brandon Heller
        self.terminate()
259
260 f24e70a4 Bob Lantz
    def waitReadable( self, timeoutms=None ):
261
        """Wait until node's output is readable.
262 b78b99b6 Bob Lantz
           timeoutms: timeout in ms or None to wait indefinitely.
263
           returns: result of poll()"""
264 ec7b211c Bob Lantz
        if len( self.readbuf ) == 0:
265 b78b99b6 Bob Lantz
            return self.pollOut.poll( timeoutms )
266 89bf3103 Brandon Heller
267 121eb449 Bob Lantz
    def sendCmd( self, *args, **kwargs ):
268 80a8fa62 Bob Lantz
        """Send a command, followed by a command to echo a sentinel,
269 121eb449 Bob Lantz
           and return without waiting for the command to complete.
270
           args: command and arguments, or string
271 2e4dd134 Bob Lantz
           printPid: print command's PID? (False)"""
272 9db6cdc2 Bob Lantz
        assert self.shell and not self.waiting
273 2e4dd134 Bob Lantz
        printPid = kwargs.get( 'printPid', False )
274 318ae55e Bob Lantz
        # Allow sendCmd( [ list ] )
275 c273f490 Bob Lantz
        if len( args ) == 1 and isinstance( args[ 0 ], list ):
276 318ae55e Bob Lantz
            cmd = args[ 0 ]
277
        # Allow sendCmd( cmd, arg1, arg2... )
278
        elif len( args ) > 0:
279 121eb449 Bob Lantz
            cmd = args
280 318ae55e Bob Lantz
        # Convert to string
281 121eb449 Bob Lantz
        if not isinstance( cmd, str ):
282 a6bcad8f Bob Lantz
            cmd = ' '.join( [ str( c ) for c in cmd ] )
283 f24e70a4 Bob Lantz
        if not re.search( r'\w', cmd ):
284
            # Replace empty commands with something harmless
285
            cmd = 'echo -n'
286 d1b29d58 Bob Lantz
        self.lastCmd = cmd
287 ce167380 cody burkard
        # if a builtin command is backgrounded, it still yields a PID
288
        if len( cmd ) > 0 and cmd[ -1 ] == '&':
289
            # print ^A{pid}\n so monitor() can set lastPid
290
            cmd += ' printf "\\001%d\\012" $! '
291
        elif printPid and not isShellBuiltin( cmd ):
292
            cmd = 'mnexec -p ' + cmd
293 612b21cb Bob Lantz
        self.write( cmd + '\n' )
294 bcacfc05 Bob Lantz
        self.lastPid = None
295 89bf3103 Brandon Heller
        self.waiting = True
296
297 549f1ebc Bob Lantz
    def sendInt( self, intr=chr( 3 ) ):
298 bcacfc05 Bob Lantz
        "Interrupt running command."
299 c265deed Bob Lantz
        debug( 'sendInt: writing chr(%d)\n' % ord( intr ) )
300 549f1ebc Bob Lantz
        self.write( intr )
301 4065511a Bob Lantz
302 549f1ebc Bob Lantz
    def monitor( self, timeoutms=None, findPid=True ):
303 e4c82e52 Bob Lantz
        """Monitor and return the output of a command.
304 f24e70a4 Bob Lantz
           Set self.waiting to False if command has completed.
305 c265deed Bob Lantz
           timeoutms: timeout in ms or None to wait indefinitely
306
           findPid: look for PID from mnexec -p"""
307 b78b99b6 Bob Lantz
        ready = self.waitReadable( timeoutms )
308
        if not ready:
309
            return ''
310 80a8fa62 Bob Lantz
        data = self.read( 1024 )
311 c11d5773 cody burkard
        pidre = r'\[\d+\] \d+\r\n'
312 bcacfc05 Bob Lantz
        # Look for PID
313 e9013d76 Bob Lantz
        marker = chr( 1 ) + r'\d+\r\n'
314 549f1ebc Bob Lantz
        if findPid and chr( 1 ) in data:
315 c11d5773 cody burkard
            # suppress the job and PID of a backgrounded command
316
            if re.findall( pidre, data ):
317
                data = re.sub( pidre, '', data )
318 161e7997 Brian O'Connor
            # Marker can be read in chunks; continue until all of it is read
319
            while not re.findall( marker, data ):
320
                data += self.read( 1024 )
321 bcacfc05 Bob Lantz
            markers = re.findall( marker, data )
322
            if markers:
323
                self.lastPid = int( markers[ 0 ][ 1: ] )
324
                data = re.sub( marker, '', data )
325
        # Look for sentinel/EOF
326 4065511a Bob Lantz
        if len( data ) > 0 and data[ -1 ] == chr( 127 ):
327 89bf3103 Brandon Heller
            self.waiting = False
328 e4c82e52 Bob Lantz
            data = data[ :-1 ]
329 4065511a Bob Lantz
        elif chr( 127 ) in data:
330 bcacfc05 Bob Lantz
            self.waiting = False
331 e4c82e52 Bob Lantz
            data = data.replace( chr( 127 ), '' )
332
        return data
333 723d068c Brandon Heller
334 c265deed Bob Lantz
    def waitOutput( self, verbose=False, findPid=True ):
335 80a8fa62 Bob Lantz
        """Wait for a command to complete.
336 7c371cf3 Bob Lantz
           Completion is signaled by a sentinel character, ASCII(127)
337 80a8fa62 Bob Lantz
           appearing in the output stream.  Wait for the sentinel and return
338 efc9a01c Bob Lantz
           the output, including trailing newline.
339
           verbose: print output interactively"""
340
        log = info if verbose else debug
341 89bf3103 Brandon Heller
        output = ''
342 e4c82e52 Bob Lantz
        while self.waiting:
343 b1ec912d Bob Lantz
            data = self.monitor( findPid=findPid )
344 4065511a Bob Lantz
            output += data
345
            log( data )
346 89bf3103 Brandon Heller
        return output
347
348 121eb449 Bob Lantz
    def cmd( self, *args, **kwargs ):
349 80a8fa62 Bob Lantz
        """Send a command, wait for output, and return it.
350
           cmd: string"""
351 121eb449 Bob Lantz
        verbose = kwargs.get( 'verbose', False )
352 efc9a01c Bob Lantz
        log = info if verbose else debug
353 121eb449 Bob Lantz
        log( '*** %s : %s\n' % ( self.name, args ) )
354 9db6cdc2 Bob Lantz
        if self.shell:
355
            self.sendCmd( *args, **kwargs )
356
            return self.waitOutput( verbose )
357
        else:
358
            warn( '(%s exited - ignoring cmd%s)\n' % ( self, args ) )
359 89bf3103 Brandon Heller
360 121eb449 Bob Lantz
    def cmdPrint( self, *args):
361 80a8fa62 Bob Lantz
        """Call cmd and printing its output
362
           cmd: string"""
363 121eb449 Bob Lantz
        return self.cmd( *args, **{ 'verbose': True } )
364 89bf3103 Brandon Heller
365 089e8130 Bob Lantz
    def popen( self, *args, **kwargs ):
366
        """Return a Popen() object in our namespace
367
           args: Popen() args, single list, or string
368
           kwargs: Popen() keyword args"""
369
        defaults = { 'stdout': PIPE, 'stderr': PIPE,
370 5ca91f9c Bob Lantz
                     'mncmd':
371 e5a15ced Bob Lantz
                     [ 'mnexec', '-da', str( self.pid ) ] }
372 089e8130 Bob Lantz
        defaults.update( kwargs )
373
        if len( args ) == 1:
374 c273f490 Bob Lantz
            if isinstance( args[ 0 ], list ):
375 089e8130 Bob Lantz
                # popen([cmd, arg1, arg2...])
376
                cmd = args[ 0 ]
377 9a8bdfd7 Bob Lantz
            elif isinstance( args[ 0 ], basestring ):
378 089e8130 Bob Lantz
                # popen("cmd arg1 arg2...")
379
                cmd = args[ 0 ].split()
380 b9100834 Bob Lantz
            else:
381
                raise Exception( 'popen() requires a string or list' )
382 089e8130 Bob Lantz
        elif len( args ) > 0:
383
            # popen( cmd, arg1, arg2... )
384 06f7408c Bob Lantz
            cmd = list( args )
385 089e8130 Bob Lantz
        # Attach to our namespace  using mnexec -a
386 c265deed Bob Lantz
        cmd = defaults.pop( 'mncmd' ) + cmd
387 b9100834 Bob Lantz
        # Shell requires a string, not a list!
388
        if defaults.get( 'shell', False ):
389
            cmd = ' '.join( cmd )
390 c265deed Bob Lantz
        popen = self._popen( cmd, **defaults )
391
        return popen
392 089e8130 Bob Lantz
393
    def pexec( self, *args, **kwargs ):
394
        """Execute a command using popen
395
           returns: out, err, exitcode"""
396 c265deed Bob Lantz
        popen = self.popen( *args, stdin=PIPE, stdout=PIPE, stderr=PIPE,
397 7a3159c9 Bob Lantz
                            **kwargs )
398 c265deed Bob Lantz
        # Warning: this can fail with large numbers of fds!
399 089e8130 Bob Lantz
        out, err = popen.communicate()
400
        exitcode = popen.wait()
401
        return out, err, exitcode
402
403 89bf3103 Brandon Heller
    # Interface management, configuration, and routing
404 efc9a01c Bob Lantz
405
    # BL notes: This might be a bit redundant or over-complicated.
406
    # However, it does allow a bit of specialization, including
407
    # changing the canonical interface names. It's also tricky since
408
    # the real interfaces are created as veth pairs, so we can't
409
    # make a single interface at a time.
410
411 d44a5843 Bob Lantz
    def newPort( self ):
412
        "Return the next port number to allocate."
413
        if len( self.ports ) > 0:
414
            return max( self.ports.values() ) + 1
415 dd159b4a Bob Lantz
        return self.portBase
416 89bf3103 Brandon Heller
417 c265deed Bob Lantz
    def addIntf( self, intf, port=None, moveIntfFn=moveIntf ):
418 efc9a01c Bob Lantz
        """Add an interface.
419 a6bcad8f Bob Lantz
           intf: interface
420 c265deed Bob Lantz
           port: port number (optional, typically OpenFlow port number)
421
           moveIntfFn: function to move interface (optional)"""
422 121eb449 Bob Lantz
        if port is None:
423
            port = self.newPort()
424 efc9a01c Bob Lantz
        self.intfs[ port ] = intf
425
        self.ports[ intf ] = port
426 a6bcad8f Bob Lantz
        self.nameToIntf[ intf.name ] = intf
427 84a91a14 Bob Lantz
        debug( '\n' )
428 c265deed Bob Lantz
        debug( 'added intf %s (%d) to node %s\n' % (
429
                intf, port, self.name ) )
430 efc9a01c Bob Lantz
        if self.inNamespace:
431 84a91a14 Bob Lantz
            debug( 'moving', intf, 'into namespace for', self.name, '\n' )
432 c265deed Bob Lantz
            moveIntfFn( intf.name, self  )
433 efc9a01c Bob Lantz
434 a6bcad8f Bob Lantz
    def defaultIntf( self ):
435
        "Return interface for lowest port"
436
        ports = self.intfs.keys()
437
        if ports:
438
            return self.intfs[ min( ports ) ]
439 efc99154 Bob Lantz
        else:
440
            warn( '*** defaultIntf: warning:', self.name,
441 edf60032 Brandon Heller
                  'has no interfaces\n' )
442 efc9a01c Bob Lantz
443 b1ec912d Bob Lantz
    def intf( self, intf=None ):
444 bf208cde Brandon Heller
        """Return our interface object with given string name,
445
           default intf if name is falsy (None, empty string, etc).
446
           or the input intf arg.
447

448
        Having this fcn return its arg for Intf objects makes it
449
        easier to construct functions with flexible input args for
450
        interfaces (those that accept both string names and Intf objects).
451
        """
452 a6bcad8f Bob Lantz
        if not intf:
453
            return self.defaultIntf()
454 9a8bdfd7 Bob Lantz
        elif isinstance( intf, basestring):
455 a6bcad8f Bob Lantz
            return self.nameToIntf[ intf ]
456
        else:
457 bf208cde Brandon Heller
            return intf
458 efc9a01c Bob Lantz
459 fb2f6523 Bob Lantz
    def connectionsTo( self, node):
460 8856d284 Bob Lantz
        "Return [ intf1, intf2... ] for all intfs that connect self to node."
461 fb2f6523 Bob Lantz
        # We could optimize this if it is important
462
        connections = []
463 8856d284 Bob Lantz
        for intf in self.intfList():
464 a6bcad8f Bob Lantz
            link = intf.link
465 8856d284 Bob Lantz
            if link:
466
                node1, node2 = link.intf1.node, link.intf2.node
467
                if node1 == self and node2 == node:
468
                    connections += [ ( intf, link.intf2 ) ]
469
                elif node1 == node and node2 == self:
470
                    connections += [ ( intf, link.intf1 ) ]
471 fb2f6523 Bob Lantz
        return connections
472 35341142 Bob Lantz
473 10be691b Bob Lantz
    def deleteIntfs( self, checkName=True ):
474
        """Delete all of our interfaces.
475
           checkName: only delete interfaces that contain our name"""
476 086ef80e Bob Lantz
        # In theory the interfaces should go away after we shut down.
477
        # However, this takes time, so we're better off removing them
478
        # explicitly so that we won't get errors if we run before they
479 d44a5843 Bob Lantz
        # have been removed by the kernel. Unfortunately this is very slow,
480
        # at least with Linux kernels before 2.6.33
481 086ef80e Bob Lantz
        for intf in self.intfs.values():
482 10be691b Bob Lantz
            # Protect against deleting hardware interfaces
483
            if ( self.name in intf.name ) or ( not checkName ):
484
                intf.delete()
485
                info( '.' )
486 086ef80e Bob Lantz
487 a6bcad8f Bob Lantz
    # Routing support
488 376bcba4 Brandon Heller
489 80a8fa62 Bob Lantz
    def setARP( self, ip, mac ):
490
        """Add an ARP entry.
491 019bff82 Bob Lantz
           ip: IP address as string
492
           mac: MAC address as string"""
493 121eb449 Bob Lantz
        result = self.cmd( 'arp', '-s', ip, mac )
494 376bcba4 Brandon Heller
        return result
495
496 80a8fa62 Bob Lantz
    def setHostRoute( self, ip, intf ):
497
        """Add route to host.
498
           ip: IP address as dotted decimal
499
           intf: string, interface name"""
500 14ff3ad3 Bob Lantz
        return self.cmd( 'route add -host', ip, 'dev', intf )
501 89bf3103 Brandon Heller
502 84a91a14 Bob Lantz
    def setDefaultRoute( self, intf=None ):
503 80a8fa62 Bob Lantz
        """Set the default route to go through intf.
504 dd21df3c Bob Lantz
           intf: Intf or {dev <intfname> via <gw-ip> ...}"""
505
        # Note setParam won't call us if intf is none
506 9a8bdfd7 Bob Lantz
        if isinstance( intf, basestring ) and ' ' in intf:
507 dd21df3c Bob Lantz
            params = intf
508
        else:
509 12758046 Bob Lantz
            params = 'dev %s' % intf
510 c5d9e0e0 Bob Lantz
        # Do this in one line in case we're messing with the root namespace
511
        self.cmd( 'ip route del default; ip route add default', params )
512 89bf3103 Brandon Heller
513 84a91a14 Bob Lantz
    # Convenience and configuration methods
514 54d026f6 Bob Lantz
515 b684ff78 Bob Lantz
    def setMAC( self, mac, intf=None ):
516 a6bcad8f Bob Lantz
        """Set the MAC address for an interface.
517
           intf: intf or intf name
518
           mac: MAC address as string"""
519
        return self.intf( intf ).setMAC( mac )
520
521 a2486a6d Bob Lantz
    def setIP( self, ip, prefixLen=8, intf=None, **kwargs ):
522 a6bcad8f Bob Lantz
        """Set the IP address for an interface.
523 bf208cde Brandon Heller
           intf: intf or intf name
524 a6bcad8f Bob Lantz
           ip: IP address as a string
525 a2486a6d Bob Lantz
           prefixLen: prefix length, e.g. 8 for /8 or 16M addrs
526
           kwargs: any additional arguments for intf.setIP"""
527
        return self.intf( intf ).setIP( ip, prefixLen, **kwargs )
528 54d026f6 Bob Lantz
529 d44a5843 Bob Lantz
    def IP( self, intf=None ):
530
        "Return IP address of a node or specific interface."
531 b684ff78 Bob Lantz
        return self.intf( intf ).IP()
532 d44a5843 Bob Lantz
533
    def MAC( self, intf=None ):
534
        "Return MAC address of a node or specific interface."
535 0aefb0e0 Bob Lantz
        return self.intf( intf ).MAC()
536 54d026f6 Bob Lantz
537 a6bcad8f Bob Lantz
    def intfIsUp( self, intf=None ):
538 d44a5843 Bob Lantz
        "Check if an interface is up."
539 a6bcad8f Bob Lantz
        return self.intf( intf ).isUp()
540
541 84a91a14 Bob Lantz
    # The reason why we configure things in this way is so
542
    # That the parameters can be listed and documented in
543
    # the config method.
544
    # Dealing with subclasses and superclasses is slightly
545
    # annoying, but at least the information is there!
546
547
    def setParam( self, results, method, **param ):
548 216a4b7c Bob Lantz
        """Internal method: configure a *single* parameter
549
           results: dict of results to update
550
           method: config method name
551
           param: arg=value (ignore if value=None)
552
           value may also be list or dict"""
553 84a91a14 Bob Lantz
        name, value = param.items()[ 0 ]
554 a64f8c28 Bob Lantz
        if value is None:
555
            return
556 84a91a14 Bob Lantz
        f = getattr( self, method, None )
557 08d611f4 Cody Burkard
        if not f:
558 84a91a14 Bob Lantz
            return
559 c273f490 Bob Lantz
        if isinstance( value, list ):
560 84a91a14 Bob Lantz
            result = f( *value )
561 273c4e94 Bob Lantz
        elif isinstance( value, dict ):
562 84a91a14 Bob Lantz
            result = f( **value )
563 54d026f6 Bob Lantz
        else:
564 84a91a14 Bob Lantz
            result = f( value )
565
        results[ name ] = result
566 216a4b7c Bob Lantz
        return result
567 54d026f6 Bob Lantz
568 8856d284 Bob Lantz
    def config( self, mac=None, ip=None,
569
                defaultRoute=None, lo='up', **_params ):
570 84a91a14 Bob Lantz
        """Configure Node according to (optional) parameters:
571
           mac: MAC address for default interface
572
           ip: IP address for default interface
573
           ifconfig: arbitrary interface configuration
574
           Subclasses should override this method and call
575
           the parent class's config(**params)"""
576
        # If we were overriding this method, we would call
577
        # the superclass config method here as follows:
578 14ff3ad3 Bob Lantz
        # r = Parent.config( **_params )
579 84a91a14 Bob Lantz
        r = {}
580
        self.setParam( r, 'setMAC', mac=mac )
581
        self.setParam( r, 'setIP', ip=ip )
582 e5754ae9 Shaun Crampton
        self.setParam( r, 'setDefaultRoute', defaultRoute=defaultRoute )
583 8856d284 Bob Lantz
        # This should be examined
584
        self.cmd( 'ifconfig lo ' + lo )
585 84a91a14 Bob Lantz
        return r
586
587
    def configDefault( self, **moreParams ):
588
        "Configure with default parameters"
589
        self.params.update( moreParams )
590
        self.config( **self.params )
591
592 a6bcad8f Bob Lantz
    # This is here for backward compatibility
593
    def linkTo( self, node, link=Link ):
594
        """(Deprecated) Link to another node
595
           replace with Link( node1, node2)"""
596
        return link( self, node )
597 89bf3103 Brandon Heller
598
    # Other methods
599 a6bcad8f Bob Lantz
600 03dd914e Bob Lantz
    def intfList( self ):
601
        "List of our interfaces sorted by port number"
602
        return [ self.intfs[ p ] for p in sorted( self.intfs.iterkeys() ) ]
603
604 a6bcad8f Bob Lantz
    def intfNames( self ):
605 03dd914e Bob Lantz
        "The names of our interfaces sorted by port number"
606
        return [ str( i ) for i in self.intfList() ]
607 a6bcad8f Bob Lantz
608 8856d284 Bob Lantz
    def __repr__( self ):
609
        "More informative string representation"
610
        intfs = ( ','.join( [ '%s:%s' % ( i.name, i.IP() )
611 edf60032 Brandon Heller
                              for i in self.intfList() ] ) )
612 8856d284 Bob Lantz
        return '<%s %s: %s pid=%s> ' % (
613 c0095746 Brandon Heller
            self.__class__.__name__, self.name, intfs, self.pid )
614 8856d284 Bob Lantz
615 80a8fa62 Bob Lantz
    def __str__( self ):
616 8856d284 Bob Lantz
        "Abbreviated string representation"
617
        return self.name
618 89bf3103 Brandon Heller
619 14ff3ad3 Bob Lantz
    # Automatic class setup support
620
621
    isSetup = False
622
623
    @classmethod
624
    def checkSetup( cls ):
625
        "Make sure our class and superclasses are set up"
626
        while cls and not getattr( cls, 'isSetup', True ):
627
            cls.setup()
628
            cls.isSetup = True
629
            # Make pylint happy
630
            cls = getattr( type( cls ), '__base__', None )
631
632
    @classmethod
633
    def setup( cls ):
634
        "Make sure our class dependencies are available"
635
        pathCheck( 'mnexec', 'ifconfig', moduleName='Mininet')
636 89bf3103 Brandon Heller
637 80a8fa62 Bob Lantz
class Host( Node ):
638 216a4b7c Bob Lantz
    "A host is simply a Node"
639
    pass
640
641
class CPULimitedHost( Host ):
642 84a91a14 Bob Lantz
643
    "CPU limited host"
644
645 59542784 Bob Lantz
    def __init__( self, name, sched='cfs', **kwargs ):
646
        Host.__init__( self, name, **kwargs )
647 197b083f Bob Lantz
        # Initialize class if necessary
648
        if not CPULimitedHost.inited:
649
            CPULimitedHost.init()
650 84a91a14 Bob Lantz
        # Create a cgroup and move shell into it
651 197b083f Bob Lantz
        self.cgroup = 'cpu,cpuacct,cpuset:/' + self.name
652 8a622c3a Bob Lantz
        errFail( 'cgcreate -g ' + self.cgroup )
653 197b083f Bob Lantz
        # We don't add ourselves to a cpuset because you must
654
        # specify the cpu and memory placement first
655
        errFail( 'cgclassify -g cpu,cpuacct:/%s %s' % ( self.name, self.pid ) )
656 44af37bc Bob Lantz
        # BL: Setting the correct period/quota is tricky, particularly
657
        # for RT. RT allows very small quotas, but the overhead
658
        # seems to be high. CFS has a mininimum quota of 1 ms, but
659
        # still does better with larger period values.
660
        self.period_us = kwargs.get( 'period_us', 100000 )
661 59542784 Bob Lantz
        self.sched = sched
662 a562ca1b Bob Lantz
        if sched == 'rt':
663
            self.checkRtGroupSched()
664
            self.rtprio = 20
665 84a91a14 Bob Lantz
666
    def cgroupSet( self, param, value, resource='cpu' ):
667
        "Set a cgroup parameter and return its value"
668
        cmd = 'cgset -r %s.%s=%s /%s' % (
669
            resource, param, value, self.name )
670 8e3699ec Bob Lantz
        quietRun( cmd )
671 8a622c3a Bob Lantz
        nvalue = int( self.cgroupGet( param, resource ) )
672
        if nvalue != value:
673
            error( '*** error: cgroupSet: %s set to %s instead of %s\n'
674
                   % ( param, nvalue, value ) )
675
        return nvalue
676 84a91a14 Bob Lantz
677
    def cgroupGet( self, param, resource='cpu' ):
678 14ff3ad3 Bob Lantz
        "Return value of cgroup parameter"
679 84a91a14 Bob Lantz
        cmd = 'cgget -r %s.%s /%s' % (
680
            resource, param, self.name )
681 197b083f Bob Lantz
        return int( quietRun( cmd ).split()[ -1 ] )
682 84a91a14 Bob Lantz
683 28833d86 Bob Lantz
    def cgroupDel( self ):
684
        "Clean up our cgroup"
685
        # info( '*** deleting cgroup', self.cgroup, '\n' )
686 612b21cb Bob Lantz
        _out, _err, exitcode = errRun( 'cgdelete -r ' + self.cgroup )
687 7c6d645a Bob Lantz
        # Sometimes cgdelete returns a resource busy error but still
688
        # deletes the group; next attempt will give "no such file"
689
        return exitcode == 0  or ( 'no such file' in _err.lower() )
690 28833d86 Bob Lantz
691 089e8130 Bob Lantz
    def popen( self, *args, **kwargs ):
692
        """Return a Popen() object in node's namespace
693
           args: Popen() args, single list, or string
694
           kwargs: Popen() keyword args"""
695
        # Tell mnexec to execute command in our cgroup
696 e16c5fe9 cody burkard
        mncmd = [ 'mnexec', '-g', self.name,
697
                  '-da', str( self.pid ) ]
698 08d611f4 Cody Burkard
        # if our cgroup is not given any cpu time,
699
        # we cannot assign the RR Scheduler.
700 4f8aa1d8 Bob Lantz
        if self.sched == 'rt':
701
            if int( self.cgroupGet( 'rt_runtime_us', 'cpu' ) ) <= 0:
702
                mncmd += [ '-r', str( self.rtprio ) ]
703
            else:
704 b1ec912d Bob Lantz
                debug( '*** error: not enough cpu time available for %s.' %
705
                       self.name, 'Using cfs scheduler for subprocess\n' )
706 089e8130 Bob Lantz
        return Host.popen( self, *args, mncmd=mncmd, **kwargs )
707
708 28833d86 Bob Lantz
    def cleanup( self ):
709 59eeeadb Brian O'Connor
        "Clean up Node, then clean up our cgroup"
710
        super( CPULimitedHost, self ).cleanup()
711 7c6d645a Bob Lantz
        retry( retries=3, delaySecs=.1, fn=self.cgroupDel )
712 28833d86 Bob Lantz
713 820c3be7 Bob Lantz
    _rtGroupSched = False   # internal class var: Is CONFIG_RT_GROUP_SCHED set?
714 5a530af1 Bob Lantz
715 820c3be7 Bob Lantz
    @classmethod
716
    def checkRtGroupSched( cls ):
717
        "Check (Ubuntu,Debian) kernel config for CONFIG_RT_GROUP_SCHED for RT"
718
        if not cls._rtGroupSched:
719
            release = quietRun( 'uname -r' ).strip('\r\n')
720 b1ec912d Bob Lantz
            output = quietRun( 'grep CONFIG_RT_GROUP_SCHED /boot/config-%s' %
721
                               release )
722 820c3be7 Bob Lantz
            if output == '# CONFIG_RT_GROUP_SCHED is not set\n':
723 4d55ef11 Bob Lantz
                error( '\n*** error: please enable RT_GROUP_SCHED '
724 b1ec912d Bob Lantz
                       'in your kernel\n' )
725 820c3be7 Bob Lantz
                exit( 1 )
726
            cls._rtGroupSched = True
727
728 089e8130 Bob Lantz
    def chrt( self ):
729 84a91a14 Bob Lantz
        "Set RT scheduling priority"
730 089e8130 Bob Lantz
        quietRun( 'chrt -p %s %s' % ( self.rtprio, self.pid ) )
731 84a91a14 Bob Lantz
        result = quietRun( 'chrt -p %s' % self.pid )
732
        firstline = result.split( '\n' )[ 0 ]
733
        lastword = firstline.split( ' ' )[ -1 ]
734 8a622c3a Bob Lantz
        if lastword != 'SCHED_RR':
735
            error( '*** error: could not assign SCHED_RR to %s\n' % self.name )
736 84a91a14 Bob Lantz
        return lastword
737
738 8a622c3a Bob Lantz
    def rtInfo( self, f ):
739
        "Internal method: return parameters for RT bandwidth"
740
        pstr, qstr = 'rt_period_us', 'rt_runtime_us'
741
        # RT uses wall clock time for period and quota
742 04c1c098 cody burkard
        quota = int( self.period_us * f )
743 8a622c3a Bob Lantz
        return pstr, qstr, self.period_us, quota
744
745 820c3be7 Bob Lantz
    def cfsInfo( self, f ):
746 8a622c3a Bob Lantz
        "Internal method: return parameters for CFS bandwidth"
747
        pstr, qstr = 'cfs_period_us', 'cfs_quota_us'
748
        # CFS uses wall clock time for period and CPU time for quota.
749
        quota = int( self.period_us * f * numCores() )
750
        period = self.period_us
751
        if f > 0 and quota < 1000:
752
            debug( '(cfsInfo: increasing default period) ' )
753
            quota = 1000
754
            period = int( quota / f / numCores() )
755 820c3be7 Bob Lantz
        # Reset to unlimited on negative quota
756
        if quota < 0:
757
            quota = -1
758 8a622c3a Bob Lantz
        return pstr, qstr, period, quota
759
760 216a4b7c Bob Lantz
    # BL comment:
761 14ff3ad3 Bob Lantz
    # This may not be the right API,
762 216a4b7c Bob Lantz
    # since it doesn't specify CPU bandwidth in "absolute"
763
    # units the way link bandwidth is specified.
764
    # We should use MIPS or SPECINT or something instead.
765
    # Alternatively, we should change from system fraction
766
    # to CPU seconds per second, essentially assuming that
767
    # all CPUs are the same.
768 8a622c3a Bob Lantz
769 820c3be7 Bob Lantz
    def setCPUFrac( self, f, sched=None ):
770 216a4b7c Bob Lantz
        """Set overall CPU fraction for this host
771 820c3be7 Bob Lantz
           f: CPU bandwidth limit (positive fraction, or -1 for cfs unlimited)
772 216a4b7c Bob Lantz
           sched: 'rt' or 'cfs'
773 5a530af1 Bob Lantz
           Note 'cfs' requires CONFIG_CFS_BANDWIDTH,
774 820c3be7 Bob Lantz
           and 'rt' requires CONFIG_RT_GROUP_SCHED"""
775 216a4b7c Bob Lantz
        if not sched:
776
            sched = self.sched
777
        if sched == 'rt':
778 820c3be7 Bob Lantz
            if not f or f < 0:
779 b1ec912d Bob Lantz
                raise Exception( 'Please set a positive CPU fraction'
780
                                 ' for sched=rt\n' )
781 8a622c3a Bob Lantz
            pstr, qstr, period, quota = self.rtInfo( f )
782 216a4b7c Bob Lantz
        elif sched == 'cfs':
783 8a622c3a Bob Lantz
            pstr, qstr, period, quota = self.cfsInfo( f )
784 216a4b7c Bob Lantz
        else:
785
            return
786
        # Set cgroup's period and quota
787 08d611f4 Cody Burkard
        setPeriod = self.cgroupSet( pstr, period )
788
        setQuota = self.cgroupSet( qstr, quota )
789 216a4b7c Bob Lantz
        if sched == 'rt':
790
            # Set RT priority if necessary
791 08d611f4 Cody Burkard
            sched = self.chrt()
792
        info( '(%s %d/%dus) ' % ( sched, setQuota, setPeriod ) )
793 84a91a14 Bob Lantz
794 669e420c Bob Lantz
    def setCPUs( self, cores, mems=0 ):
795 197b083f Bob Lantz
        "Specify (real) cores that our cgroup can run on"
796 08d611f4 Cody Burkard
        if not cores:
797
            return
798 c273f490 Bob Lantz
        if isinstance( cores, list ):
799 197b083f Bob Lantz
            cores = ','.join( [ str( c ) for c in cores ] )
800
        self.cgroupSet( resource='cpuset', param='cpus',
801 669e420c Bob Lantz
                        value=cores )
802 197b083f Bob Lantz
        # Memory placement is probably not relevant, but we
803
        # must specify it anyway
804
        self.cgroupSet( resource='cpuset', param='mems',
805 669e420c Bob Lantz
                        value=mems)
806 197b083f Bob Lantz
        # We have to do this here after we've specified
807
        # cpus and mems
808
        errFail( 'cgclassify -g cpuset:/%s %s' % (
809 c0095746 Brandon Heller
                 self.name, self.pid ) )
810 197b083f Bob Lantz
811 08d611f4 Cody Burkard
    def config( self, cpu=-1, cores=None, **params ):
812 84a91a14 Bob Lantz
        """cpu: desired overall system CPU fraction
813 197b083f Bob Lantz
           cores: (real) core(s) this host can run on
814 84a91a14 Bob Lantz
           params: parameters for Node.config()"""
815
        r = Node.config( self, **params )
816 216a4b7c Bob Lantz
        # Was considering cpu={'cpu': cpu , 'sched': sched}, but
817
        # that seems redundant
818 84a91a14 Bob Lantz
        self.setParam( r, 'setCPUFrac', cpu=cpu )
819 197b083f Bob Lantz
        self.setParam( r, 'setCPUs', cores=cores )
820 84a91a14 Bob Lantz
        return r
821
822 197b083f Bob Lantz
    inited = False
823 89bf3103 Brandon Heller
824 197b083f Bob Lantz
    @classmethod
825
    def init( cls ):
826
        "Initialization for CPULimitedHost class"
827
        mountCgroups()
828
        cls.inited = True
829
830
831 84a91a14 Bob Lantz
# Some important things to note:
832
#
833 bf9c6ab7 Bob Lantz
# The "IP" address which setIP() assigns to the switch is not
834 84a91a14 Bob Lantz
# an "IP address for the switch" in the sense of IP routing.
835 bf9c6ab7 Bob Lantz
# Rather, it is the IP address for the control interface,
836
# on the control network, and it is only relevant to the
837
# controller. If you are running in the root namespace
838
# (which is the only way to run OVS at the moment), the
839
# control interface is the loopback interface, and you
840
# normally never want to change its IP address!
841 84a91a14 Bob Lantz
#
842
# In general, you NEVER want to attempt to use Linux's
843
# network stack (i.e. ifconfig) to "assign" an IP address or
844
# MAC address to a switch data port. Instead, you "assign"
845
# the IP and MAC addresses in the controller by specifying
846
# packets that you want to receive or send. The "MAC" address
847
# reported by ifconfig for a switch data port is essentially
848 bf9c6ab7 Bob Lantz
# meaningless. It is important to understand this if you
849
# want to create a functional router using OpenFlow.
850 89bf3103 Brandon Heller
851 80a8fa62 Bob Lantz
class Switch( Node ):
852 6d72a3cb Bob Lantz
    """A Switch is a Node that is running (or has execed?)
853 80a8fa62 Bob Lantz
       an OpenFlow switch."""
854 89bf3103 Brandon Heller
855 8a622c3a Bob Lantz
    portBase = 1  # Switches start with port 1 in OpenFlow
856 0d94548a Bob Lantz
    dpidLen = 16  # digits in dpid passed to switch
857 dd159b4a Bob Lantz
858 84a91a14 Bob Lantz
    def __init__( self, name, dpid=None, opts='', listenPort=None, **params):
859 0b5609f5 Bob Lantz
        """dpid: dpid hex string (or None to derive from name, e.g. s1 -> 1)
860 84a91a14 Bob Lantz
           opts: additional switch options
861
           listenPort: port to listen on for dpctl connections"""
862
        Node.__init__( self, name, **params )
863 0b5609f5 Bob Lantz
        self.dpid = self.defaultDpid( dpid )
864 121eb449 Bob Lantz
        self.opts = opts
865 ccca871a Brandon Heller
        self.listenPort = listenPort
866 8856d284 Bob Lantz
        if not self.inNamespace:
867 14c19260 Bob Lantz
            self.controlIntf = Intf( 'lo', self, port=0 )
868 84a91a14 Bob Lantz
869 0b5609f5 Bob Lantz
    def defaultDpid( self, dpid=None ):
870
        "Return correctly formatted dpid from dpid or switch name (s1 -> 1)"
871
        if dpid:
872
            # Remove any colons and make sure it's a good hex number
873
            dpid = dpid.translate( None, ':' )
874
            assert len( dpid ) <= self.dpidLen and int( dpid, 16 ) >= 0
875
        else:
876
            # Use hex of the first number in the switch name
877
            nums = re.findall( r'\d+', self.name )
878
            if nums:
879
                dpid = hex( int( nums[ 0 ] ) )[ 2: ]
880
            else:
881
                raise Exception( 'Unable to derive default datapath ID - '
882
                                 'please either specify a dpid or use a '
883
                                 'canonical switch name such as s23.' )
884 74c71bc8 Bob Lantz
        return '0' * ( self.dpidLen - len( dpid ) ) + dpid
885 121eb449 Bob Lantz
886 9802686b Bob Lantz
    def defaultIntf( self ):
887 8856d284 Bob Lantz
        "Return control interface"
888
        if self.controlIntf:
889
            return self.controlIntf
890
        else:
891
            return Node.defaultIntf( self )
892 121eb449 Bob Lantz
893
    def sendCmd( self, *cmd, **kwargs ):
894 80a8fa62 Bob Lantz
        """Send command to Node.
895
           cmd: string"""
896 121eb449 Bob Lantz
        kwargs.setdefault( 'printPid', False )
897 1bb4412f Brandon Heller
        if not self.execed:
898 121eb449 Bob Lantz
            return Node.sendCmd( self, *cmd, **kwargs )
899 1bb4412f Brandon Heller
        else:
900 6d72a3cb Bob Lantz
            error( '*** Error: %s has execed and cannot accept commands' %
901 2e089b5e Brandon Heller
                   self.name )
902 89bf3103 Brandon Heller
903 538a856c Bob Lantz
    def connected( self ):
904
        "Is the switch connected to a controller? (override this method)"
905 554abdd5 Bob Lantz
        # Assume that we are connected by default to whatever we need to
906
        # be connected to. This should be overridden by any OpenFlow
907
        # switch, but not by a standalone bridge.
908
        debug( 'Assuming', repr( self ), 'is connected to a controller\n' )
909 18aab5b7 Bob Lantz
        return True
910 538a856c Bob Lantz
911 0165d7bd Bob Lantz
    def stop( self, deleteIntfs=True ):
912
        """Stop switch
913
           deleteIntfs: delete interfaces? (True)"""
914
        if deleteIntfs:
915
            self.deleteIntfs()
916 6cb68f26 Bob Lantz
917 8856d284 Bob Lantz
    def __repr__( self ):
918
        "More informative string representation"
919
        intfs = ( ','.join( [ '%s:%s' % ( i.name, i.IP() )
920 edf60032 Brandon Heller
                              for i in self.intfList() ] ) )
921 8856d284 Bob Lantz
        return '<%s %s: %s pid=%s> ' % (
922 c0095746 Brandon Heller
            self.__class__.__name__, self.name, intfs, self.pid )
923 89bf3103 Brandon Heller
924 b1ec912d Bob Lantz
925 80a8fa62 Bob Lantz
class UserSwitch( Switch ):
926 d44a5843 Bob Lantz
    "User-space switch."
927 89bf3103 Brandon Heller
928 0d94548a Bob Lantz
    dpidLen = 12
929
930 aa554d98 Bob Lantz
    def __init__( self, name, dpopts='--no-slicing', **kwargs ):
931 80a8fa62 Bob Lantz
        """Init.
932 aa554d98 Bob Lantz
           name: name for the switch
933
           dpopts: additional arguments to ofdatapath (--no-slicing)"""
934 d44a5843 Bob Lantz
        Switch.__init__( self, name, **kwargs )
935 57fd19ef Bob Lantz
        pathCheck( 'ofdatapath', 'ofprotocol',
936 edf60032 Brandon Heller
                   moduleName='the OpenFlow reference user switch' +
937
                              '(openflow.org)' )
938 8856d284 Bob Lantz
        if self.listenPort:
939
            self.opts += ' --listen=ptcp:%i ' % self.listenPort
940 191df1cb Brian O'Connor
        else:
941
            self.opts += ' --listen=punix:/tmp/%s.listen' % self.name
942 804c4bbf Bob Lantz
        self.dpopts = dpopts
943 89bf3103 Brandon Heller
944 14ff3ad3 Bob Lantz
    @classmethod
945
    def setup( cls ):
946 b055728f Brandon Heller
        "Ensure any dependencies are loaded; if not, try to load them."
947 e900a16c Bob Lantz
        if not os.path.exists( '/dev/net/tun' ):
948
            moduleDeps( add=TUN )
949 b055728f Brandon Heller
950 8856d284 Bob Lantz
    def dpctl( self, *args ):
951
        "Run dpctl command"
952 0dd96ebc Andrew Ferguson
        listenAddr = None
953 8856d284 Bob Lantz
        if not self.listenPort:
954 191df1cb Brian O'Connor
            listenAddr = 'unix:/tmp/%s.listen' % self.name
955 0dd96ebc Andrew Ferguson
        else:
956
            listenAddr = 'tcp:127.0.0.1:%i' % self.listenPort
957 14c19260 Bob Lantz
        return self.cmd( 'dpctl ' + ' '.join( args ) +
958 0dd96ebc Andrew Ferguson
                         ' ' + listenAddr )
959 8856d284 Bob Lantz
960 538a856c Bob Lantz
    def connected( self ):
961
        "Is the switch connected to a controller?"
962 c75ff7ec cody burkard
        status = self.dpctl( 'status' )
963
        return ( 'remote.is-connected=true' in status and
964
                 'local.is-connected=true' in status )
965 538a856c Bob Lantz
966 3236d33b Andrew Ferguson
    @staticmethod
967
    def TCReapply( intf ):
968
        """Unfortunately user switch and Mininet are fighting
969
           over tc queuing disciplines. To resolve the conflict,
970
           we re-create the user switch's configuration, but as a
971
           leaf of the TCIntf-created configuration."""
972 c273f490 Bob Lantz
        if isinstance( intf, TCIntf ):
973 7a3159c9 Bob Lantz
            ifspeed = 10000000000  # 10 Gbps
974 3236d33b Andrew Ferguson
            minspeed = ifspeed * 0.001
975
976
            res = intf.config( **intf.params )
977 28454708 Andrew Ferguson
978 7a3159c9 Bob Lantz
            if res is None:  # link may not have TC parameters
979 28454708 Andrew Ferguson
                return
980 3236d33b Andrew Ferguson
981
            # Re-add qdisc, root, and default classes user switch created, but
982
            # with new parent, as setup by Mininet's TCIntf
983 28454708 Andrew Ferguson
            parent = res['parent']
984 3236d33b Andrew Ferguson
            intf.tc( "%s qdisc add dev %s " + parent +
985
                     " handle 1: htb default 0xfffe" )
986
            intf.tc( "%s class add dev %s classid 1:0xffff parent 1: htb rate "
987
                     + str(ifspeed) )
988
            intf.tc( "%s class add dev %s classid 1:0xfffe parent 1:0xffff " +
989
                     "htb rate " + str(minspeed) + " ceil " + str(ifspeed) )
990
991 80a8fa62 Bob Lantz
    def start( self, controllers ):
992
        """Start OpenFlow reference user datapath.
993 6d72a3cb Bob Lantz
           Log to /tmp/sN-{ofd,ofp}.log.
994 efc9a01c Bob Lantz
           controllers: list of controller objects"""
995 b69ef234 Bob Lantz
        # Add controllers
996
        clist = ','.join( [ 'tcp:%s:%d' % ( c.IP(), c.port )
997 edf60032 Brandon Heller
                            for c in controllers ] )
998 89bf3103 Brandon Heller
        ofdlog = '/tmp/' + self.name + '-ofd.log'
999
        ofplog = '/tmp/' + self.name + '-ofp.log'
1000 14c19260 Bob Lantz
        intfs = [ str( i ) for i in self.intfList() if not i.IP() ]
1001 efc9a01c Bob Lantz
        self.cmd( 'ofdatapath -i ' + ','.join( intfs ) +
1002 804c4bbf Bob Lantz
                  ' punix:/tmp/' + self.name + ' -d %s ' % self.dpid +
1003
                  self.dpopts +
1004 edf60032 Brandon Heller
                  ' 1> ' + ofdlog + ' 2> ' + ofdlog + ' &' )
1005 efc9a01c Bob Lantz
        self.cmd( 'ofprotocol unix:/tmp/' + self.name +
1006 edf60032 Brandon Heller
                  ' ' + clist +
1007
                  ' --fail=closed ' + self.opts +
1008
                  ' 1> ' + ofplog + ' 2>' + ofplog + ' &' )
1009 be13072f Brian O'Connor
        if "no-slicing" not in self.dpopts:
1010
            # Only TCReapply if slicing is enable
1011 7a3159c9 Bob Lantz
            sleep(1)  # Allow ofdatapath to start before re-arranging qdisc's
1012 be13072f Brian O'Connor
            for intf in self.intfList():
1013
                if not intf.IP():
1014
                    self.TCReapply( intf )
1015 89bf3103 Brandon Heller
1016 b57e5d93 Brian O'Connor
    def stop( self, deleteIntfs=True ):
1017 b1ec912d Bob Lantz
        """Stop OpenFlow reference user datapath.
1018
           deleteIntfs: delete interfaces? (True)"""
1019 80a8fa62 Bob Lantz
        self.cmd( 'kill %ofdatapath' )
1020
        self.cmd( 'kill %ofprotocol' )
1021 d754a7ce Bob Lantz
        super( UserSwitch, self ).stop( deleteIntfs )
1022 82b72072 Bob Lantz
1023 f7c2df25 Brandon Heller
1024 8a7d42db Bob Lantz
class OVSSwitch( Switch ):
1025
    "Open vSwitch switch. Depends on ovs-vsctl."
1026
1027 3e2eb713 Bob Lantz
    def __init__( self, name, failMode='secure', datapath='kernel',
1028 d4be9271 Bob Lantz
                  inband=False, protocols=None,
1029 a4e93368 Bob Lantz
                  reconnectms=1000, stp=False, batch=False, **params ):
1030 d4be9271 Bob Lantz
        """name: name for switch
1031 1bd0e927 Felician Nemeth
           failMode: controller loss behavior (secure|standalone)
1032 3e2eb713 Bob Lantz
           datapath: userspace or kernel mode (kernel|user)
1033 171e8151 Bob Lantz
           inband: use in-band control (False)
1034 127f35a9 Bob Lantz
           protocols: use specific OpenFlow version(s) (e.g. OpenFlow13)
1035 d4be9271 Bob Lantz
                      Unspecified (or old OVS version) uses OVS default
1036 a8cc243a Bob Lantz
           reconnectms: max reconnect timeout in ms (0/None for default)
1037 bdad3e8c Bob Lantz
           stp: enable STP (False, requires failMode=standalone)
1038 a4e93368 Bob Lantz
           batch: enable batch startup (False)"""
1039 84a91a14 Bob Lantz
        Switch.__init__( self, name, **params )
1040 92b601ab Bob Lantz
        self.failMode = failMode
1041 153d598d Murphy McCauley
        self.datapath = datapath
1042 3e2eb713 Bob Lantz
        self.inband = inband
1043 84ce84f5 Gregory Gee
        self.protocols = protocols
1044 d4be9271 Bob Lantz
        self.reconnectms = reconnectms
1045 a8cc243a Bob Lantz
        self.stp = stp
1046 05dbf82e Bob Lantz
        self._uuids = []  # controller UUIDs
1047 bdad3e8c Bob Lantz
        self.batch = batch
1048
        self.commands = []  # saved commands for batch startup
1049 14ff3ad3 Bob Lantz
1050
    @classmethod
1051
    def setup( cls ):
1052 8a7d42db Bob Lantz
        "Make sure Open vSwitch is installed and working"
1053 14ff3ad3 Bob Lantz
        pathCheck( 'ovs-vsctl',
1054 edf60032 Brandon Heller
                   moduleName='Open vSwitch (openvswitch.org)')
1055 28c2cdc2 Bob Lantz
        # This should no longer be needed, and it breaks
1056
        # with OVS 1.7 which has renamed the kernel module:
1057
        #  moduleDeps( subtract=OF_KMOD, add=OVS_KMOD )
1058 8a7d42db Bob Lantz
        out, err, exitcode = errRun( 'ovs-vsctl -t 1 show' )
1059
        if exitcode:
1060 14ff3ad3 Bob Lantz
            error( out + err +
1061 8a7d42db Bob Lantz
                   'ovs-vsctl exited with code %d\n' % exitcode +
1062
                   '*** Error connecting to ovs-db with ovs-vsctl\n'
1063
                   'Make sure that Open vSwitch is installed, '
1064
                   'that ovsdb-server is running, and that\n'
1065
                   '"ovs-vsctl show" works correctly.\n'
1066 14ff3ad3 Bob Lantz
                   'You may wish to try '
1067
                   '"service openvswitch-switch start".\n' )
1068 8a7d42db Bob Lantz
            exit( 1 )
1069 b1ec912d Bob Lantz
        version = quietRun( 'ovs-vsctl --version' )
1070 7a3159c9 Bob Lantz
        cls.OVSVersion = findall( r'\d+\.\d+', version )[ 0 ]
1071 f1e42ba5 Cody
1072
    @classmethod
1073
    def isOldOVS( cls ):
1074 b1ec912d Bob Lantz
        "Is OVS ersion < 1.10?"
1075 f1e42ba5 Cody
        return ( StrictVersion( cls.OVSVersion ) <
1076 7a3159c9 Bob Lantz
                 StrictVersion( '1.10' ) )
1077 8a7d42db Bob Lantz
1078 8856d284 Bob Lantz
    def dpctl( self, *args ):
1079 229f112f Bob Lantz
        "Run ovs-ofctl command"
1080
        return self.cmd( 'ovs-ofctl', args[ 0 ], self, *args[ 1: ] )
1081 8856d284 Bob Lantz
1082 9bda9848 Bob Lantz
    def vsctl( self, *args, **kwargs ):
1083 bdad3e8c Bob Lantz
        "Run ovs-vsctl command (or queue for later execution)"
1084
        if self.batch:
1085
            cmd = ' '.join( str( arg ).strip() for arg in args )
1086
            self.commands.append( cmd )
1087
        else:
1088
            return self.cmd( 'ovs-vsctl', *args, **kwargs )
1089 9bda9848 Bob Lantz
1090 1aec55d9 Bob Lantz
    @staticmethod
1091
    def TCReapply( intf ):
1092
        """Unfortunately OVS and Mininet are fighting
1093
           over tc queuing disciplines. As a quick hack/
1094
           workaround, we clear OVS's and reapply our own."""
1095 c273f490 Bob Lantz
        if isinstance( intf, TCIntf ):
1096 1aec55d9 Bob Lantz
            intf.config( **intf.params )
1097
1098 8856d284 Bob Lantz
    def attach( self, intf ):
1099
        "Connect a data port"
1100 9bda9848 Bob Lantz
        self.vsctl( 'add-port', self, intf )
1101 8856d284 Bob Lantz
        self.cmd( 'ifconfig', intf, 'up' )
1102 1aec55d9 Bob Lantz
        self.TCReapply( intf )
1103 8856d284 Bob Lantz
1104
    def detach( self, intf ):
1105
        "Disconnect a data port"
1106 9bda9848 Bob Lantz
        self.vsctl( 'del-port', self, intf )
1107 8856d284 Bob Lantz
1108 05dbf82e Bob Lantz
    def controllerUUIDs( self, update=False ):
1109
        """Return ovsdb UUIDs for our controllers
1110
           update: update cached value"""
1111
        if not self._uuids or update:
1112 bec34e72 Bob Lantz
            controllers = self.cmd( 'ovs-vsctl -- get Bridge', self,
1113 3ac5cafe Bob Lantz
                                    'Controller' ).strip()
1114 05dbf82e Bob Lantz
            if controllers.startswith( '[' ) and controllers.endswith( ']' ):
1115
                controllers = controllers[ 1 : -1 ]
1116
                if controllers:
1117 3ac5cafe Bob Lantz
                    self._uuids = [ c.strip()
1118
                                    for c in controllers.split( ',' ) ]
1119 05dbf82e Bob Lantz
        return self._uuids
1120 538a856c Bob Lantz
1121
    def connected( self ):
1122
        "Are we connected to at least one of our controllers?"
1123 05dbf82e Bob Lantz
        for uuid in self.controllerUUIDs():
1124 9bda9848 Bob Lantz
            if 'true' in self.vsctl( '-- get Controller',
1125
                                     uuid, 'is_connected' ):
1126 05dbf82e Bob Lantz
                return True
1127
        return self.failMode == 'standalone'
1128 538a856c Bob Lantz
1129 957fe1db Bob Lantz
    def intfOpts( self, intf ):
1130
        "Return OVS interface options for intf"
1131
        opts = ''
1132
        if not self.isOldOVS():
1133 bec34e72 Bob Lantz
            # ofport_request is not supported on old OVS
1134 957fe1db Bob Lantz
            opts += ' ofport_request=%s' % self.ports[ intf ]
1135
            # Patch ports don't work well with old OVS
1136
            if isinstance( intf, OVSIntf ):
1137
                intf1, intf2 = intf.link.intf1, intf.link.intf2
1138
                peer = intf1 if intf1 != intf else intf2
1139
                opts += ' type=patch options:peer=%s' % peer
1140
        return '' if not opts else ' -- set Interface %s' % intf + opts
1141
1142
    def bridgeOpts( self ):
1143
        "Return OVS bridge options"
1144 bec34e72 Bob Lantz
        opts = ( ' other_config:datapath-id=%s' % self.dpid +
1145
                 ' fail_mode=%s' % self.failMode )
1146 957fe1db Bob Lantz
        if not self.inband:
1147
            opts += ' other-config:disable-in-band=true'
1148
        if self.datapath == 'user':
1149 c11e9f33 Bob Lantz
            opts += ' datapath_type=netdev'
1150 957fe1db Bob Lantz
        if self.protocols and not self.isOldOVS():
1151 93be1d04 Bob Lantz
            opts += ' protocols=%s' % self.protocols
1152 957fe1db Bob Lantz
        if self.stp and self.failMode == 'standalone':
1153 63ae13fc Bob Lantz
            opts += ' stp_enable=true'
1154 957fe1db Bob Lantz
        return opts
1155 c069542c Bob Lantz
1156 8a7d42db Bob Lantz
    def start( self, controllers ):
1157
        "Start up a new OVS OpenFlow switch using ovs-vsctl"
1158 14ff3ad3 Bob Lantz
        if self.inNamespace:
1159 8856d284 Bob Lantz
            raise Exception(
1160 14ff3ad3 Bob Lantz
                'OVS kernel switch does not work in a namespace' )
1161 7a3159c9 Bob Lantz
        int( self.dpid, 16 )  # DPID must be a hex string
1162 bec34e72 Bob Lantz
        # Command to add interfaces
1163 957fe1db Bob Lantz
        intfs = ''.join( ' -- add-port %s %s' % ( self, intf ) +
1164
                         self.intfOpts( intf )
1165
                         for intf in self.intfList()
1166
                         if self.ports[ intf ] and not intf.IP() )
1167 bec34e72 Bob Lantz
        # Command to create controller entries
1168 3b4738c2 Bob Lantz
        clist = [ ( self.name + c.name, '%s:%s:%d' %
1169
                  ( c.protocol, c.IP(), c.port ) )
1170
                  for c in controllers ]
1171
        if self.listenPort:
1172
            clist.append( ( self.name + '-listen',
1173
                            'ptcp:%s' % self.listenPort ) )
1174 957fe1db Bob Lantz
        ccmd = '-- --id=@%s create Controller target=\\"%s\\"'
1175
        if self.reconnectms:
1176
            ccmd += ' max_backoff=%d' % self.reconnectms
1177
        cargs = ' '.join( ccmd % ( name, target )
1178 bdad3e8c Bob Lantz
                          for name, target in clist )
1179 bec34e72 Bob Lantz
        # Controller ID list
1180 957fe1db Bob Lantz
        cids = ','.join( '@%s' % name for name, _target in clist )
1181 bec34e72 Bob Lantz
        # Try to delete any existing bridges with the same name
1182 ba43451b Cody
        if not self.isOldOVS():
1183 957fe1db Bob Lantz
            cargs += ' -- --if-exists del-br %s' % self
1184 bec34e72 Bob Lantz
        # One ovs-vsctl command to rule them all!
1185
        self.vsctl( cargs +
1186
                    ' -- add-br %s' % self +
1187
                    ' -- set bridge %s controller=[%s]' % ( self, cids  ) +
1188
                    self.bridgeOpts() +
1189
                    intfs )
1190 05dbf82e Bob Lantz
        # If necessary, restore TC config overwritten by OVS
1191 bdad3e8c Bob Lantz
        if not self.batch:
1192
            for intf in self.intfList():
1193
                self.TCReapply( intf )
1194
1195
    # This should be ~ int( quietRun( 'getconf ARG_MAX' ) ),
1196
    # but the real limit seems to be much lower
1197
    argmax = 128000
1198
1199
    @classmethod
1200
    def batchStartup( cls, switches, run=errRun ):
1201
        """Batch startup for OVS
1202
           switches: switches to start up
1203
           run: function to run commands (errRun)"""
1204
        info( '...' )
1205
        cmds = 'ovs-vsctl'
1206
        for switch in switches:
1207
            if switch.isOldOVS():
1208
                # Ideally we'd optimize this also
1209
                run( 'ovs-vsctl del-br %s' % switch )
1210
            for cmd in switch.commands:
1211
                cmd = cmd.strip()
1212
                # Don't exceed ARG_MAX
1213
                if len( cmds ) + len( cmd ) >= cls.argmax:
1214
                    run( cmds, shell=True )
1215
                    cmds = 'ovs-vsctl'
1216
                cmds += ' ' + cmd
1217
                switch.cmds = []
1218
                switch.batch = False
1219
        if cmds:
1220
            run( cmds, shell=True )
1221
        # Reapply link config if necessary...
1222
        for switch in switches:
1223
            for intf in switch.intfs.itervalues():
1224
                if isinstance( intf, TCIntf ):
1225
                    intf.config( **intf.params )
1226
        return switches
1227 2e19ceb0 Bob Lantz
1228 b57e5d93 Brian O'Connor
    def stop( self, deleteIntfs=True ):
1229 b1ec912d Bob Lantz
        """Terminate OVS switch.
1230
           deleteIntfs: delete interfaces? (True)"""
1231 8856d284 Bob Lantz
        self.cmd( 'ovs-vsctl del-br', self )
1232 765d126e Bob Lantz
        if self.datapath == 'user':
1233
            self.cmd( 'ip link del', self )
1234 d754a7ce Bob Lantz
        super( OVSSwitch, self ).stop( deleteIntfs )
1235 8a7d42db Bob Lantz
1236 bdad3e8c Bob Lantz
    @classmethod
1237
    def batchShutdown( cls, switches, run=errRun ):
1238
        "Shut down a list of OVS switches"
1239
        delcmd = 'del-br %s'
1240 acdcf9b6 Bob Lantz
        if switches and not switches[ 0 ].isOldOVS():
1241 bdad3e8c Bob Lantz
            delcmd = '--if-exists ' + delcmd
1242
        # First, delete them all from ovsdb
1243
        run( 'ovs-vsctl ' +
1244
             ' -- '.join( delcmd % s for s in switches ) )
1245
        # Next, shut down all of the processes
1246
        pids = ' '.join( str( switch.pid ) for switch in switches )
1247
        run( 'kill -HUP ' + pids )
1248
        for switch in switches:
1249
            switch.shell = None
1250
        return switches
1251
1252 1b69ea13 Bob Lantz
1253 8a7d42db Bob Lantz
OVSKernelSwitch = OVSSwitch
1254
1255 84a91a14 Bob Lantz
1256 1b69ea13 Bob Lantz
class OVSBridge( OVSSwitch ):
1257
    "OVSBridge is an OVSSwitch in standalone/bridge mode"
1258 5a530af1 Bob Lantz
1259 79c944ae Bob Lantz
    def __init__( self, *args, **kwargs ):
1260
        """stp: enable Spanning Tree Protocol (False)
1261
           see OVSSwitch for other options"""
1262 1b69ea13 Bob Lantz
        kwargs.update( failMode='standalone' )
1263 79c944ae Bob Lantz
        OVSSwitch.__init__( self, *args, **kwargs )
1264 5a530af1 Bob Lantz
1265 1b69ea13 Bob Lantz
    def start( self, controllers ):
1266 79c944ae Bob Lantz
        "Start bridge, ignoring controllers argument"
1267 1b69ea13 Bob Lantz
        OVSSwitch.start( self, controllers=[] )
1268
1269 a8cc243a Bob Lantz
    def connected( self ):
1270
        "Are we forwarding yet?"
1271
        if self.stp:
1272
            status = self.dpctl( 'show' )
1273
            return 'STP_FORWARD' in status and not 'STP_LEARN' in status
1274
        else:
1275
            return True
1276
1277 1b69ea13 Bob Lantz
1278 b57e5d93 Brian O'Connor
class IVSSwitch( Switch ):
1279 554abdd5 Bob Lantz
    "Indigo Virtual Switch"
1280 27da832d Rich Lane
1281 f5164f86 Rich Lane
    def __init__( self, name, verbose=False, **kwargs ):
1282 27da832d Rich Lane
        Switch.__init__( self, name, **kwargs )
1283 163a66c6 Rich Lane
        self.verbose = verbose
1284 27da832d Rich Lane
1285
    @classmethod
1286
    def setup( cls ):
1287
        "Make sure IVS is installed"
1288
        pathCheck( 'ivs-ctl', 'ivs',
1289
                   moduleName="Indigo Virtual Switch (projectfloodlight.org)" )
1290
        out, err, exitcode = errRun( 'ivs-ctl show' )
1291
        if exitcode:
1292
            error( out + err +
1293
                   'ivs-ctl exited with code %d\n' % exitcode +
1294
                   '*** The openvswitch kernel module might '
1295
                   'not be loaded. Try modprobe openvswitch.\n' )
1296
            exit( 1 )
1297
1298 93cd5583 Rich Lane
    @classmethod
1299
    def batchShutdown( cls, switches ):
1300
        "Kill each IVS switch, to be waited on later in stop()"
1301
        for switch in switches:
1302 876e66e5 Rich Lane
            switch.cmd( 'kill %ivs' )
1303 bdad3e8c Bob Lantz
        return switches
1304 93cd5583 Rich Lane
1305 27da832d Rich Lane
    def start( self, controllers ):
1306
        "Start up a new IVS switch"
1307
        args = ['ivs']
1308
        args.extend( ['--name', self.name] )
1309
        args.extend( ['--dpid', self.dpid] )
1310 163a66c6 Rich Lane
        if self.verbose:
1311
            args.extend( ['--verbose'] )
1312 27da832d Rich Lane
        for intf in self.intfs.values():
1313
            if not intf.IP():
1314
                args.extend( ['-i', intf.name] )
1315
        for c in controllers:
1316
            args.extend( ['-c', '%s:%d' % (c.IP(), c.port)] )
1317 91261b27 Rich Lane
        if self.listenPort:
1318
            args.extend( ['--listen', '127.0.0.1:%i' % self.listenPort] )
1319 d4fabc04 Rich Lane
        args.append( self.opts )
1320 27da832d Rich Lane
1321 0a543602 Rich Lane
        logfile = '/tmp/ivs.%s.log' % self.name
1322
1323
        self.cmd( ' '.join(args) + ' >' + logfile + ' 2>&1 </dev/null &' )
1324 27da832d Rich Lane
1325 b57e5d93 Brian O'Connor
    def stop( self, deleteIntfs=True ):
1326 b1ec912d Bob Lantz
        """Terminate IVS switch.
1327
           deleteIntfs: delete interfaces? (True)"""
1328 0a543602 Rich Lane
        self.cmd( 'kill %ivs' )
1329 a7eb5576 Rich Lane
        self.cmd( 'wait' )
1330 d754a7ce Bob Lantz
        super( IVSSwitch, self ).stop( deleteIntfs )
1331 27da832d Rich Lane
1332
    def attach( self, intf ):
1333
        "Connect a data port"
1334
        self.cmd( 'ivs-ctl', 'add-port', '--datapath', self.name, intf )
1335
1336
    def detach( self, intf ):
1337
        "Disconnect a data port"
1338
        self.cmd( 'ivs-ctl', 'del-port', '--datapath', self.name, intf )
1339
1340
    def dpctl( self, *args ):
1341
        "Run dpctl command"
1342 91261b27 Rich Lane
        if not self.listenPort:
1343
            return "can't run dpctl without passive listening port"
1344 803c0a6e Rich Lane
        return self.cmd( 'ovs-ofctl ' + ' '.join( args ) +
1345 91261b27 Rich Lane
                         ' tcp:127.0.0.1:%i' % self.listenPort )
1346 27da832d Rich Lane
1347
1348 80a8fa62 Bob Lantz
class Controller( Node ):
1349 7c371cf3 Bob Lantz
    """A Controller is a Node that is running (or has execed?) an
1350 80a8fa62 Bob Lantz
       OpenFlow controller."""
1351 1bb4412f Brandon Heller
1352 57fd19ef Bob Lantz
    def __init__( self, name, inNamespace=False, command='controller',
1353 edf60032 Brandon Heller
                  cargs='-v ptcp:%d', cdir=None, ip="127.0.0.1",
1354 8df24304 Brian O'Connor
                  port=6653, protocol='tcp', **params ):
1355 57fd19ef Bob Lantz
        self.command = command
1356 1bb4412f Brandon Heller
        self.cargs = cargs
1357
        self.cdir = cdir
1358 17ba6a7c Bob Lantz
        # Accept 'ip:port' syntax as shorthand
1359
        if ':' in ip:
1360
            ip, port = ip.split( ':' )
1361
            port = int( port )
1362 84a91a14 Bob Lantz
        self.ip = ip
1363 60d9ead6 David Erickson
        self.port = port
1364 5cb4a542 Gregory Gee
        self.protocol = protocol
1365 086ef80e Bob Lantz
        Node.__init__( self, name, inNamespace=inNamespace,
1366 edf60032 Brandon Heller
                       ip=ip, **params  )
1367 ec969b7f Bob Lantz
        self.checkListening()
1368
1369
    def checkListening( self ):
1370
        "Make sure no controllers are running on our port"
1371 b453e006 Brandon Heller
        # Verify that Telnet is installed first:
1372 c8b85746 Bob Lantz
        out, _err, returnCode = errRun( "which telnet" )
1373 b453e006 Brandon Heller
        if 'telnet' not in out or returnCode != 0:
1374
            raise Exception( "Error running telnet to check for listening "
1375
                             "controllers; please check that it is "
1376
                             "installed." )
1377 ec969b7f Bob Lantz
        listening = self.cmd( "echo A | telnet -e A %s %d" %
1378
                              ( self.ip, self.port ) )
1379 b5580601 Bob Lantz
        if 'Connected' in listening:
1380 c34a000e backb1
            servers = self.cmd( 'netstat -natp' ).split( '\n' )
1381 ec969b7f Bob Lantz
            pstr = ':%d ' % self.port
1382 28c2cdc2 Bob Lantz
            clist = servers[ 0:1 ] + [ s for s in servers if pstr in s ]
1383 ec969b7f Bob Lantz
            raise Exception( "Please shut down the controller which is"
1384
                             " running on port %d:\n" % self.port +
1385 28c2cdc2 Bob Lantz
                             '\n'.join( clist ) )
1386 1bb4412f Brandon Heller
1387 80a8fa62 Bob Lantz
    def start( self ):
1388
        """Start <controller> <args> on controller.
1389
           Log to /tmp/cN.log"""
1390 57fd19ef Bob Lantz
        pathCheck( self.command )
1391 1bb4412f Brandon Heller
        cout = '/tmp/' + self.name + '.log'
1392
        if self.cdir is not None:
1393 efc9a01c Bob Lantz
            self.cmd( 'cd ' + self.cdir )
1394 57fd19ef Bob Lantz
        self.cmd( self.command + ' ' + self.cargs % self.port +
1395 c11d5773 cody burkard
                  ' 1>' + cout + ' 2>' + cout + ' &' )
1396 723d068c Brandon Heller
        self.execed = False
1397 89bf3103 Brandon Heller
1398 b1ec912d Bob Lantz
    def stop( self, *args, **kwargs ):
1399 80a8fa62 Bob Lantz
        "Stop controller."
1400 57fd19ef Bob Lantz
        self.cmd( 'kill %' + self.command )
1401 098bede0 Bob Lantz
        self.cmd( 'wait %' + self.command )
1402 b1ec912d Bob Lantz
        super( Controller, self ).stop( *args, **kwargs )
1403 89bf3103 Brandon Heller
1404 d44a5843 Bob Lantz
    def IP( self, intf=None ):
1405 80a8fa62 Bob Lantz
        "Return IP address of the Controller"
1406 a6bcad8f Bob Lantz
        if self.intfs:
1407
            ip = Node.IP( self, intf )
1408
        else:
1409 84a91a14 Bob Lantz
            ip = self.ip
1410 d44a5843 Bob Lantz
        return ip
1411 723d068c Brandon Heller
1412 8856d284 Bob Lantz
    def __repr__( self ):
1413
        "More informative string representation"
1414
        return '<%s %s: %s:%s pid=%s> ' % (
1415 c0095746 Brandon Heller
            self.__class__.__name__, self.name,
1416
            self.IP(), self.port, self.pid )
1417 7a3159c9 Bob Lantz
1418 5ac3cde2 Cody Burkard
    @classmethod
1419 b1ec912d Bob Lantz
    def isAvailable( cls ):
1420
        "Is controller available?"
1421 5ac3cde2 Cody Burkard
        return quietRun( 'which controller' )
1422 84a91a14 Bob Lantz
1423 7a3159c9 Bob Lantz
1424 9addfc13 Bob Lantz
class OVSController( Controller ):
1425
    "Open vSwitch controller"
1426
    def __init__( self, name, command='ovs-controller', **kwargs ):
1427 5ac3cde2 Cody Burkard
        if quietRun( 'which test-controller' ):
1428
            command = 'test-controller'
1429 9addfc13 Bob Lantz
        Controller.__init__( self, name, command=command, **kwargs )
1430 7a3159c9 Bob Lantz
1431 5ac3cde2 Cody Burkard
    @classmethod
1432 b1ec912d Bob Lantz
    def isAvailable( cls ):
1433
        return ( quietRun( 'which ovs-controller' ) or
1434 db3bffa9 Roan Huang
                 quietRun( 'which test-controller' ) or
1435
                 quietRun( 'which ovs-testcontroller' ) )
1436 80a8fa62 Bob Lantz
1437
class NOX( Controller ):
1438
    "Controller to run a NOX application."
1439
1440 2db4268b Bob Lantz
    def __init__( self, name, *noxArgs, **kwargs ):
1441 80a8fa62 Bob Lantz
        """Init.
1442
           name: name to give controller
1443 2db4268b Bob Lantz
           noxArgs: arguments (strings) to pass to NOX"""
1444 80a8fa62 Bob Lantz
        if not noxArgs:
1445 2db4268b Bob Lantz
            warn( 'warning: no NOX modules specified; '
1446
                  'running packetdump only\n' )
1447 80a8fa62 Bob Lantz
            noxArgs = [ 'packetdump' ]
1448 2db4268b Bob Lantz
        elif type( noxArgs ) not in ( list, tuple ):
1449 f32a5468 Brandon Heller
            noxArgs = [ noxArgs ]
1450 137ec305 Bob Lantz
1451 4f4f1dd2 Brandon Heller
        if 'NOX_CORE_DIR' not in os.environ:
1452 137ec305 Bob Lantz
            exit( 'exiting; please set missing NOX_CORE_DIR env var' )
1453 80a8fa62 Bob Lantz
        noxCoreDir = os.environ[ 'NOX_CORE_DIR' ]
1454 4f4f1dd2 Brandon Heller
1455 80a8fa62 Bob Lantz
        Controller.__init__( self, name,
1456 edf60032 Brandon Heller
                             command=noxCoreDir + '/nox_core',
1457
                             cargs='--libdir=/usr/local/lib -v -i ptcp:%s ' +
1458 2e089b5e Brandon Heller
                             ' '.join( noxArgs ),
1459 edf60032 Brandon Heller
                             cdir=noxCoreDir,
1460
                             **kwargs )
1461 80a8fa62 Bob Lantz
1462 c2be20f0 Bob Lantz
class Ryu( Controller ):
1463 686a9993 cody burkard
    "Controller to run Ryu application"
1464
    def __init__( self, name, *ryuArgs, **kwargs ):
1465
        """Init.
1466
        name: name to give controller.
1467
        ryuArgs: arguments and modules to pass to Ryu"""
1468
        homeDir = quietRun( 'printenv HOME' ).strip( '\r\n' )
1469
        ryuCoreDir = '%s/ryu/ryu/app/' % homeDir
1470
        if not ryuArgs:
1471
            warn( 'warning: no Ryu modules specified; '
1472
                  'running simple_switch only\n' )
1473
            ryuArgs = [ ryuCoreDir + 'simple_switch.py' ]
1474
        elif type( ryuArgs ) not in ( list, tuple ):
1475
            ryuArgs = [ ryuArgs ]
1476
1477
        Controller.__init__( self, name,
1478 7a3159c9 Bob Lantz
                             command='ryu-manager',
1479
                             cargs='--ofp-tcp-listen-port %s ' +
1480
                             ' '.join( ryuArgs ),
1481
                             cdir=ryuCoreDir,
1482
                             **kwargs )
1483 80a8fa62 Bob Lantz
1484 79c944ae Bob Lantz
1485 80a8fa62 Bob Lantz
class RemoteController( Controller ):
1486
    "Controller running outside of Mininet's control."
1487
1488 0eba655d Bob Lantz
    def __init__( self, name, ip='127.0.0.1',
1489 8df24304 Brian O'Connor
                  port=None, **kwargs):
1490 80a8fa62 Bob Lantz
        """Init.
1491
           name: name to give controller
1492 2aafefc2 Bob Lantz
           ip: the IP address where the remote controller is
1493 80a8fa62 Bob Lantz
           listening
1494
           port: the port where the remote controller is listening"""
1495 edf60032 Brandon Heller
        Controller.__init__( self, name, ip=ip, port=port, **kwargs )
1496 80a8fa62 Bob Lantz
1497
    def start( self ):
1498
        "Overridden to do nothing."
1499 60d9ead6 David Erickson
        return
1500
1501 80a8fa62 Bob Lantz
    def stop( self ):
1502
        "Overridden to do nothing."
1503 60d9ead6 David Erickson
        return
1504 2b35a2ca James Page
1505
    def checkListening( self ):
1506 54c51c02 Bob Lantz
        "Warn if remote controller is not accessible"
1507 8df24304 Brian O'Connor
        if self.port is not None:
1508
            self.isListening( self.ip, self.port )
1509
        else:
1510 d254d749 Brian O'Connor
            for port in 6653, 6633:
1511 8df24304 Brian O'Connor
                if self.isListening( self.ip, port ):
1512
                    self.port = port
1513
                    info( "Connecting to remote controller"
1514
                          " at %s:%d\n" % ( self.ip, self.port ))
1515
                    break
1516
1517
        if self.port is None:
1518
            self.port = 6653
1519
            warn( "Setting remote controller"
1520
                  " to %s:%d\n" % ( self.ip, self.port ))
1521
1522
    def isListening( self, ip, port ):
1523
        "Check if a remote controller is listening at a specific ip and port"
1524
        listening = self.cmd( "echo A | telnet -e A %s %d" % ( ip, port ) )
1525 b5580601 Bob Lantz
        if 'Connected' not in listening:
1526 54c51c02 Bob Lantz
            warn( "Unable to contact the remote controller"
1527 8df24304 Brian O'Connor
                  " at %s:%d\n" % ( ip, port ) )
1528
            return False
1529
        else:
1530
            return True
1531 5ac3cde2 Cody Burkard
1532 b1ec912d Bob Lantz
DefaultControllers = ( Controller, OVSController )
1533 39a3b73f Tomasz Buchert
1534 1b69ea13 Bob Lantz
def findController( controllers=DefaultControllers ):
1535
    "Return first available controller from list, if any"
1536
    for controller in controllers:
1537 5ac3cde2 Cody Burkard
        if controller.isAvailable():
1538 f51eddef Bob Lantz
            return controller
1539 1b69ea13 Bob Lantz
1540
def DefaultController( name, controllers=DefaultControllers, **kwargs ):
1541
    "Find a controller that is available and instantiate it"
1542 ccd3276d Bob Lantz
    controller = findController( controllers )
1543
    if not controller:
1544
        raise Exception( 'Could not find a default OpenFlow controller' )
1545 acdcf9b6 Bob Lantz
    return controller( name, **kwargs )
1546 6cb68f26 Bob Lantz
1547
def NullController( *_args, **_kwargs ):
1548
    "Nonexistent controller - simply returns None"
1549
    return None