Statistics
| Branch: | Tag: | Revision:

mininet / mininet / node.py @ 58324bdc

History | View | Annotate | Download (51.4 KB)

1 7d4b7b7f Bob Lantz
"""
2
Node objects for Mininet.
3

4 6d72a3cb Bob Lantz
Nodes provide a simple abstraction for interacting with hosts, switches
5
and controllers. Local nodes are simply one or more processes on the local
6
machine.
7 7d4b7b7f Bob Lantz

8 6d72a3cb Bob Lantz
Node: superclass for all (primarily local) network nodes.
9 7d4b7b7f Bob Lantz

10 31b43002 Bob Lantz
Host: a virtual host. By default, a host is simply a shell; commands
11
    may be sent using Cmd (which waits for output), or using sendCmd(),
12
    which returns immediately, allowing subsequent monitoring using
13
    monitor(). Examples of how to run experiments using this
14
    functionality are provided in the examples/ directory.
15 7d4b7b7f Bob Lantz

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

19 af2f67d9 Cody Burkard
HostWithPrivateDirs: a virtual host that has user-specified private
20
    directories. These may be temporary directories stored as a tmpfs,
21
    or persistent directories that are mounted from another directory in
22
    the root filesystem.
23

24 7d4b7b7f Bob Lantz
Switch: superclass for switch nodes.
25

26
UserSwitch: a switch using the user-space switch from the OpenFlow
27
    reference implementation.
28

29
KernelSwitch: a switch using the kernel switch from the OpenFlow reference
30
    implementation.
31

32
OVSSwitch: a switch using the OpenVSwitch OpenFlow-compatible switch
33
    implementation (openvswitch.org).
34

35
Controller: superclass for OpenFlow controllers. The default controller
36
    is controller(8) from the reference implementation.
37

38
NOXController: a controller node using NOX (noxrepo.org).
39

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

44 80be5642 Bob Lantz
Future enhancements:
45

46
- Possibly make Node, Switch and Controller more abstract so that
47
  they can be used for both local and remote nodes
48

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

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