Statistics
| Branch: | Tag: | Revision:

mininet / mininet / node.py @ bec34e72

History | View | Annotate | Download (59.2 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
KernelSwitch: a switch using the kernel switch from the OpenFlow reference
27
    implementation.
28

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

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

35
NOXController: a controller node using NOX (noxrepo.org).
36

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

41 80be5642 Bob Lantz
Future enhancements:
42

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

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

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