Statistics
| Branch: | Tag: | Revision:

mininet / mininet / node.py @ d254d749

History | View | Annotate | Download (58.5 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 ec7b211c Bob Lantz
        """Buffered read from node, non-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
        """Buffered readline from node, non-blocking.
231
           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
           timeoutms: timeout in ms or None to wait indefinitely."""
263 ec7b211c Bob Lantz
        if len( self.readbuf ) == 0:
264 f24e70a4 Bob Lantz
            self.pollOut.poll( timeoutms )
265 89bf3103 Brandon Heller
266 121eb449 Bob Lantz
    def sendCmd( self, *args, **kwargs ):
267 80a8fa62 Bob Lantz
        """Send a command, followed by a command to echo a sentinel,
268 121eb449 Bob Lantz
           and return without waiting for the command to complete.
269
           args: command and arguments, or string
270 2e4dd134 Bob Lantz
           printPid: print command's PID? (False)"""
271 9db6cdc2 Bob Lantz
        assert self.shell and not self.waiting
272 2e4dd134 Bob Lantz
        printPid = kwargs.get( 'printPid', False )
273 318ae55e Bob Lantz
        # Allow sendCmd( [ list ] )
274 c273f490 Bob Lantz
        if len( args ) == 1 and isinstance( args[ 0 ], list ):
275 318ae55e Bob Lantz
            cmd = args[ 0 ]
276
        # Allow sendCmd( cmd, arg1, arg2... )
277
        elif len( args ) > 0:
278 121eb449 Bob Lantz
            cmd = args
279 318ae55e Bob Lantz
        # Convert to string
280 121eb449 Bob Lantz
        if not isinstance( cmd, str ):
281 a6bcad8f Bob Lantz
            cmd = ' '.join( [ str( c ) for c in cmd ] )
282 f24e70a4 Bob Lantz
        if not re.search( r'\w', cmd ):
283
            # Replace empty commands with something harmless
284
            cmd = 'echo -n'
285 d1b29d58 Bob Lantz
        self.lastCmd = cmd
286 ce167380 cody burkard
        # if a builtin command is backgrounded, it still yields a PID
287
        if len( cmd ) > 0 and cmd[ -1 ] == '&':
288
            # print ^A{pid}\n so monitor() can set lastPid
289
            cmd += ' printf "\\001%d\\012" $! '
290
        elif printPid and not isShellBuiltin( cmd ):
291
            cmd = 'mnexec -p ' + cmd
292 612b21cb Bob Lantz
        self.write( cmd + '\n' )
293 bcacfc05 Bob Lantz
        self.lastPid = None
294 89bf3103 Brandon Heller
        self.waiting = True
295
296 549f1ebc Bob Lantz
    def sendInt( self, intr=chr( 3 ) ):
297 bcacfc05 Bob Lantz
        "Interrupt running command."
298 c265deed Bob Lantz
        debug( 'sendInt: writing chr(%d)\n' % ord( intr ) )
299 549f1ebc Bob Lantz
        self.write( intr )
300 4065511a Bob Lantz
301 549f1ebc Bob Lantz
    def monitor( self, timeoutms=None, findPid=True ):
302 e4c82e52 Bob Lantz
        """Monitor and return the output of a command.
303 f24e70a4 Bob Lantz
           Set self.waiting to False if command has completed.
304 c265deed Bob Lantz
           timeoutms: timeout in ms or None to wait indefinitely
305
           findPid: look for PID from mnexec -p"""
306 f24e70a4 Bob Lantz
        self.waitReadable( timeoutms )
307 80a8fa62 Bob Lantz
        data = self.read( 1024 )
308 c11d5773 cody burkard
        pidre = r'\[\d+\] \d+\r\n'
309 bcacfc05 Bob Lantz
        # Look for PID
310 e9013d76 Bob Lantz
        marker = chr( 1 ) + r'\d+\r\n'
311 549f1ebc Bob Lantz
        if findPid and chr( 1 ) in data:
312 c11d5773 cody burkard
            # suppress the job and PID of a backgrounded command
313
            if re.findall( pidre, data ):
314
                data = re.sub( pidre, '', data )
315 161e7997 Brian O'Connor
            # Marker can be read in chunks; continue until all of it is read
316
            while not re.findall( marker, data ):
317
                data += self.read( 1024 )
318 bcacfc05 Bob Lantz
            markers = re.findall( marker, data )
319
            if markers:
320
                self.lastPid = int( markers[ 0 ][ 1: ] )
321
                data = re.sub( marker, '', data )
322
        # Look for sentinel/EOF
323 4065511a Bob Lantz
        if len( data ) > 0 and data[ -1 ] == chr( 127 ):
324 89bf3103 Brandon Heller
            self.waiting = False
325 e4c82e52 Bob Lantz
            data = data[ :-1 ]
326 4065511a Bob Lantz
        elif chr( 127 ) in data:
327 bcacfc05 Bob Lantz
            self.waiting = False
328 e4c82e52 Bob Lantz
            data = data.replace( chr( 127 ), '' )
329
        return data
330 723d068c Brandon Heller
331 c265deed Bob Lantz
    def waitOutput( self, verbose=False, findPid=True ):
332 80a8fa62 Bob Lantz
        """Wait for a command to complete.
333 7c371cf3 Bob Lantz
           Completion is signaled by a sentinel character, ASCII(127)
334 80a8fa62 Bob Lantz
           appearing in the output stream.  Wait for the sentinel and return
335 efc9a01c Bob Lantz
           the output, including trailing newline.
336
           verbose: print output interactively"""
337
        log = info if verbose else debug
338 89bf3103 Brandon Heller
        output = ''
339 e4c82e52 Bob Lantz
        while self.waiting:
340 b1ec912d Bob Lantz
            data = self.monitor( findPid=findPid )
341 4065511a Bob Lantz
            output += data
342
            log( data )
343 89bf3103 Brandon Heller
        return output
344
345 121eb449 Bob Lantz
    def cmd( self, *args, **kwargs ):
346 80a8fa62 Bob Lantz
        """Send a command, wait for output, and return it.
347
           cmd: string"""
348 121eb449 Bob Lantz
        verbose = kwargs.get( 'verbose', False )
349 efc9a01c Bob Lantz
        log = info if verbose else debug
350 121eb449 Bob Lantz
        log( '*** %s : %s\n' % ( self.name, args ) )
351 9db6cdc2 Bob Lantz
        if self.shell:
352
            self.sendCmd( *args, **kwargs )
353
            return self.waitOutput( verbose )
354
        else:
355
            warn( '(%s exited - ignoring cmd%s)\n' % ( self, args ) )
356 89bf3103 Brandon Heller
357 121eb449 Bob Lantz
    def cmdPrint( self, *args):
358 80a8fa62 Bob Lantz
        """Call cmd and printing its output
359
           cmd: string"""
360 121eb449 Bob Lantz
        return self.cmd( *args, **{ 'verbose': True } )
361 89bf3103 Brandon Heller
362 089e8130 Bob Lantz
    def popen( self, *args, **kwargs ):
363
        """Return a Popen() object in our namespace
364
           args: Popen() args, single list, or string
365
           kwargs: Popen() keyword args"""
366
        defaults = { 'stdout': PIPE, 'stderr': PIPE,
367 5ca91f9c Bob Lantz
                     'mncmd':
368 e5a15ced Bob Lantz
                     [ 'mnexec', '-da', str( self.pid ) ] }
369 089e8130 Bob Lantz
        defaults.update( kwargs )
370
        if len( args ) == 1:
371 c273f490 Bob Lantz
            if isinstance( args[ 0 ], list ):
372 089e8130 Bob Lantz
                # popen([cmd, arg1, arg2...])
373
                cmd = args[ 0 ]
374 9a8bdfd7 Bob Lantz
            elif isinstance( args[ 0 ], basestring ):
375 089e8130 Bob Lantz
                # popen("cmd arg1 arg2...")
376
                cmd = args[ 0 ].split()
377 b9100834 Bob Lantz
            else:
378
                raise Exception( 'popen() requires a string or list' )
379 089e8130 Bob Lantz
        elif len( args ) > 0:
380
            # popen( cmd, arg1, arg2... )
381 06f7408c Bob Lantz
            cmd = list( args )
382 089e8130 Bob Lantz
        # Attach to our namespace  using mnexec -a
383 c265deed Bob Lantz
        cmd = defaults.pop( 'mncmd' ) + cmd
384 b9100834 Bob Lantz
        # Shell requires a string, not a list!
385
        if defaults.get( 'shell', False ):
386
            cmd = ' '.join( cmd )
387 c265deed Bob Lantz
        popen = self._popen( cmd, **defaults )
388
        return popen
389 089e8130 Bob Lantz
390
    def pexec( self, *args, **kwargs ):
391
        """Execute a command using popen
392
           returns: out, err, exitcode"""
393 c265deed Bob Lantz
        popen = self.popen( *args, stdin=PIPE, stdout=PIPE, stderr=PIPE,
394 7a3159c9 Bob Lantz
                            **kwargs )
395 c265deed Bob Lantz
        # Warning: this can fail with large numbers of fds!
396 089e8130 Bob Lantz
        out, err = popen.communicate()
397
        exitcode = popen.wait()
398
        return out, err, exitcode
399
400 89bf3103 Brandon Heller
    # Interface management, configuration, and routing
401 efc9a01c Bob Lantz
402
    # BL notes: This might be a bit redundant or over-complicated.
403
    # However, it does allow a bit of specialization, including
404
    # changing the canonical interface names. It's also tricky since
405
    # the real interfaces are created as veth pairs, so we can't
406
    # make a single interface at a time.
407
408 d44a5843 Bob Lantz
    def newPort( self ):
409
        "Return the next port number to allocate."
410
        if len( self.ports ) > 0:
411
            return max( self.ports.values() ) + 1
412 dd159b4a Bob Lantz
        return self.portBase
413 89bf3103 Brandon Heller
414 c265deed Bob Lantz
    def addIntf( self, intf, port=None, moveIntfFn=moveIntf ):
415 efc9a01c Bob Lantz
        """Add an interface.
416 a6bcad8f Bob Lantz
           intf: interface
417 c265deed Bob Lantz
           port: port number (optional, typically OpenFlow port number)
418
           moveIntfFn: function to move interface (optional)"""
419 121eb449 Bob Lantz
        if port is None:
420
            port = self.newPort()
421 efc9a01c Bob Lantz
        self.intfs[ port ] = intf
422
        self.ports[ intf ] = port
423 a6bcad8f Bob Lantz
        self.nameToIntf[ intf.name ] = intf
424 84a91a14 Bob Lantz
        debug( '\n' )
425 c265deed Bob Lantz
        debug( 'added intf %s (%d) to node %s\n' % (
426
                intf, port, self.name ) )
427 efc9a01c Bob Lantz
        if self.inNamespace:
428 84a91a14 Bob Lantz
            debug( 'moving', intf, 'into namespace for', self.name, '\n' )
429 c265deed Bob Lantz
            moveIntfFn( intf.name, self  )
430 efc9a01c Bob Lantz
431 a6bcad8f Bob Lantz
    def defaultIntf( self ):
432
        "Return interface for lowest port"
433
        ports = self.intfs.keys()
434
        if ports:
435
            return self.intfs[ min( ports ) ]
436 efc99154 Bob Lantz
        else:
437
            warn( '*** defaultIntf: warning:', self.name,
438 edf60032 Brandon Heller
                  'has no interfaces\n' )
439 efc9a01c Bob Lantz
440 b1ec912d Bob Lantz
    def intf( self, intf=None ):
441 bf208cde Brandon Heller
        """Return our interface object with given string name,
442
           default intf if name is falsy (None, empty string, etc).
443
           or the input intf arg.
444

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