1 "Utility functions for Mininet."
4 from mininet.log import output, info, error, warn, debug
7 from resource import getrlimit, setrlimit, RLIMIT_NPROC, RLIMIT_NOFILE
8 from select import poll, POLLIN, POLLHUP
9 from subprocess import call, check_call, Popen, PIPE, STDOUT
11 from fcntl import fcntl, F_GETFL, F_SETFL
12 from os import O_NONBLOCK
14 from functools import partial
17 # Python 2/3 compatibility
18 Python3 = sys.version_info[0] == 3
19 BaseString = str if Python3 else getattr( str, '__base__' )
20 Encoding = 'utf-8' if Python3 else None
22 "Decode a byte string if needed for Python 3"
23 return s.decode( Encoding ) if Python3 else s
25 "Encode a byte string if needed for Python 3"
26 return s.encode( Encoding ) if Python3 else s
28 # pylint: disable=import-error
30 import pexpect as oldpexpect
31 # pylint: enable=import-error
33 class Pexpect( object ):
34 "Custom pexpect that is compatible with str"
36 def spawn( *args, **kwargs):
37 "pexpect.spawn that is compatible with str"
38 if Python3 and 'encoding' not in kwargs:
39 kwargs.update( encoding='utf-8' )
40 return oldpexpect.spawn( *args, **kwargs )
42 def __getattr__( self, name ):
43 return getattr( oldpexpect, name )
49 # Command execution support
52 """Simple interface to subprocess.call()
53 cmd: list of command params"""
54 return call( cmd.split( ' ' ) )
57 """Simple interface to subprocess.check_call()
58 cmd: list of command params"""
59 return check_call( cmd.split( ' ' ) )
61 # pylint doesn't understand explicit type checking
62 # pylint: disable=maybe-no-member
64 def oldQuietRun( *cmd ):
65 """Run a command, routing stderr to stdout, and return the output.
66 cmd: list of command params"""
69 if isinstance( cmd, BaseString ):
70 cmd = cmd.split( ' ' )
71 popen = Popen( cmd, stdout=PIPE, stderr=STDOUT )
72 # We can't use Popen.communicate() because it uses
73 # select(), which can't handle
74 # high file descriptor numbers! poll() can, however.
77 readable.register( popen.stdout )
79 while readable.poll():
80 data = popen.stdout.read( 1024 )
85 if popen.returncode is not None:
90 # This is a bit complicated, but it enables us to
91 # monitor command output as it is happening
93 # pylint: disable=too-many-branches,too-many-statements
94 def errRun( *cmd, **kwargs ):
95 """Run a command and return stdout, stderr and return code
96 cmd: string or list of command and args
97 stderr: STDOUT to merge stderr with stdout
98 shell: run command using shell
99 echo: monitor output to console"""
100 # By default we separate stderr, don't run in a shell, and don't echo
101 stderr = kwargs.get( 'stderr', PIPE )
102 shell = kwargs.get( 'shell', False )
103 echo = kwargs.get( 'echo', False )
105 # cmd goes to stderr, output goes to stdout
109 # Allow passing in a list or a string
110 if isinstance( cmd, BaseString ) and not shell:
111 cmd = cmd.split( ' ' )
112 cmd = [ str( arg ) for arg in cmd ]
113 elif isinstance( cmd, list ) and shell:
114 cmd = " ".join( arg for arg in cmd )
115 debug( '*** errRun:', cmd, '\n' )
116 popen = Popen( cmd, stdout=PIPE, stderr=stderr, shell=shell )
117 # We use poll() because select() doesn't work with large fd numbers,
118 # and thus communicate() doesn't work either
121 poller.register( popen.stdout, POLLIN )
122 fdtofile = { popen.stdout.fileno(): popen.stdout }
123 outDone, errDone = False, True
125 fdtofile[ popen.stderr.fileno() ] = popen.stderr
126 poller.register( popen.stderr, POLLIN )
128 while not outDone or not errDone:
129 readable = poller.poll()
130 for fd, event in readable:
133 data = f.read( 1024 )
135 data = data.decode( Encoding )
138 if f == popen.stdout:
142 elif f == popen.stderr:
146 else: # POLLHUP or something unexpected
147 if f == popen.stdout:
149 elif f == popen.stderr:
151 poller.unregister( fd )
153 returncode = popen.wait()
154 # Python 3 complains if we don't explicitly close these
158 debug( out, err, returncode )
159 return out, err, returncode
160 # pylint: enable=too-many-branches
162 def errFail( *cmd, **kwargs ):
163 "Run a command using errRun and raise exception on nonzero exit"
164 out, err, ret = errRun( *cmd, **kwargs )
166 raise Exception( "errFail: %s failed with return code %s: %s"
167 % ( cmd, ret, err ) )
170 def quietRun( cmd, **kwargs ):
171 "Run a command and return merged stdout and stderr"
172 return errRun( cmd, stderr=STDOUT, **kwargs )[ 0 ]
174 def which(cmd, **kwargs ):
175 "Run a command and return merged stdout and stderr"
176 out, _, ret = errRun( ["which", cmd], stderr=STDOUT, **kwargs )
177 return out.rstrip() if ret == 0 else None
179 # pylint: enable=maybe-no-member
181 def isShellBuiltin( cmd ):
182 "Return True if cmd is a bash builtin."
183 if isShellBuiltin.builtIns is None:
184 isShellBuiltin.builtIns = set(quietRun( 'bash -c enable' ).split())
185 space = cmd.find( ' ' )
188 return cmd in isShellBuiltin.builtIns
190 isShellBuiltin.builtIns = None
192 # Interface management
194 # Interfaces are managed as strings which are simply the
195 # interface names, of the form 'nodeN-ethM'.
197 # To connect nodes, we create a pair of veth interfaces, and then place them
198 # in the pair of nodes that we want to communicate. We then update the node's
199 # list of interfaces and connectivity map.
201 # For the kernel datapath, switch interfaces
202 # live in the root namespace and thus do not have to be
205 def makeIntfPair( intf1, intf2, addr1=None, addr2=None, node1=None, node2=None,
206 deleteIntfs=True, runCmd=None ):
207 """Make a veth pair connnecting new interfaces intf1 and intf2
208 intf1: name for interface 1
209 intf2: name for interface 2
210 addr1: MAC address for interface 1 (optional)
211 addr2: MAC address for interface 2 (optional)
212 node1: home node for interface 1 (optional)
213 node2: home node for interface 2 (optional)
214 deleteIntfs: delete intfs before creating them
215 runCmd: function to run shell commands (quietRun)
216 raises Exception on failure"""
218 runCmd = quietRun if not node1 else node1.cmd
219 runCmd2 = quietRun if not node2 else node2.cmd
221 # Delete any old interfaces with the same names
222 runCmd( 'ip link del ' + intf1 )
223 runCmd2( 'ip link del ' + intf2 )
225 netns = 1 if not node2 else node2.pid
226 if addr1 is None and addr2 is None:
227 cmdOutput = runCmd( 'ip link add name %s '
228 'type veth peer name %s '
229 'netns %s' % ( intf1, intf2, netns ) )
231 cmdOutput = runCmd( 'ip link add name %s '
233 'type veth peer name %s '
236 ( intf1, addr1, intf2, addr2, netns ) )
238 raise Exception( "Error creating interface pair (%s,%s): %s " %
239 ( intf1, intf2, cmdOutput ) )
241 def retry( retries, delaySecs, fn, *args, **keywords ):
242 """Try something several times before giving up.
243 n: number of times to retry
244 delaySecs: wait this long between tries
246 args: args to apply to function call"""
248 while not fn( *args, **keywords ) and tries < retries:
252 error( "*** gave up after %i retries\n" % tries )
255 def moveIntfNoRetry( intf, dstNode, printError=False ):
256 """Move interface to node, without retrying.
257 intf: string, interface
258 dstNode: destination Node
259 printError: if true, print error"""
261 cmd = 'ip link set %s netns %s' % ( intf, dstNode.pid )
262 cmdOutput = quietRun( cmd )
263 # If ip link set does not produce any output, then we can assume
264 # that the link has been moved successfully.
267 error( '*** Error: moveIntf: ' + intf +
268 ' not successfully moved to ' + dstNode.name + ':\n',
273 def moveIntf( intf, dstNode, printError=True,
274 retries=3, delaySecs=0.001 ):
275 """Move interface to node, retrying on failure.
276 intf: string, interface
277 dstNode: destination Node
278 printError: if true, print error"""
279 retry( retries, delaySecs, moveIntfNoRetry, intf, dstNode,
280 printError=printError )
282 # Support for dumping network
284 def dumpNodeConnections( nodes ):
285 "Dump connections to/from nodes."
287 def dumpConnections( node ):
288 "Helper function: dump connections to node"
289 for intf in node.intfList():
290 output( ' %s:' % intf )
292 intfs = [ intf.link.intf1, intf.link.intf2 ]
300 dumpConnections( node )
303 def dumpNetConnections( net ):
304 "Dump connections in network"
305 nodes = net.controllers + net.switches + net.hosts
306 dumpNodeConnections( nodes )
308 def dumpPorts( switches ):
309 "dump interface to openflow port mappings for each switch"
310 for switch in switches:
311 output( '%s ' % switch.name )
312 for intf in switch.intfList():
313 port = switch.ports[ intf ]
314 output( '%s:%d ' % ( intf, port ) )
317 # IP and Mac address formatting and parsing
319 def _colonHex( val, bytecount ):
320 """Generate colon-hex string.
321 val: input as unsigned int
322 bytecount: number of bytes to convert
323 returns: chStr colon-hex string"""
325 for i in range( bytecount - 1, -1, -1 ):
326 piece = ( ( 0xff << ( i * 8 ) ) & val ) >> ( i * 8 )
327 pieces.append( '%02x' % piece )
328 chStr = ':'.join( pieces )
331 def macColonHex( mac ):
332 """Generate MAC colon-hex string from unsigned int.
333 mac: MAC address as unsigned int
334 returns: macStr MAC colon-hex string"""
335 return _colonHex( mac, 6 )
338 """Generate IP address string from an unsigned int.
339 ip: unsigned int of form w << 24 | x << 16 | y << 8 | z
340 returns: ip address string w.x.y.z"""
341 w = ( ip >> 24 ) & 0xff
342 x = ( ip >> 16 ) & 0xff
343 y = ( ip >> 8 ) & 0xff
345 return "%i.%i.%i.%i" % ( w, x, y, z )
347 def ipNum( w, x, y, z ):
348 """Generate unsigned int from components of IP address
349 returns: w << 24 | x << 16 | y << 8 | z"""
350 return ( w << 24 ) | ( x << 16 ) | ( y << 8 ) | z
352 def ipAdd( i, prefixLen=8, ipBaseNum=0x0a000000 ):
353 """Return IP address string from ints
354 i: int to be added to ipbase
355 prefixLen: optional IP prefix length
356 ipBaseNum: option base IP address as int
357 returns IP address as string"""
358 imax = 0xffffffff >> prefixLen
359 assert i <= imax, 'Not enough IP addresses in the subnet'
360 mask = 0xffffffff ^ imax
361 ipnum = ( ipBaseNum & mask ) + i
362 return ipStr( ipnum )
365 "Parse an IP address and return an unsigned int."
366 args = [ int( arg ) for arg in ip.split( '.' ) ]
368 args.insert( len(args) - 1, 0 )
369 return ipNum( *args )
371 def netParse( ipstr ):
372 """Parse an IP network specification, returning
373 address and prefix len as unsigned ints"""
376 ip, pf = ipstr.split( '/' )
377 prefixLen = int( pf )
378 #if no prefix is specified, set the prefix to 24
382 return ipParse( ip ), prefixLen
385 "Check if input string is an int"
393 "Check if input string is a float"
400 def makeNumeric( s ):
401 "Convert string to int or float if numeric."
404 elif checkFloat( s ):
411 def pmonitor(popens, timeoutms=500, readline=True,
413 """Monitor dict of hosts to popen objects
415 timeoutms: timeout for poll()
416 readline: return single line of output
417 yields: host, line/output (if any)
418 terminates: when all EOFs received"""
421 for host, popen in popens.items():
422 fd = popen.stdout.fileno()
423 fdToHost[ fd ] = host
424 poller.register( fd, POLLIN | POLLHUP )
425 flags = fcntl( fd, F_GETFL )
426 fcntl( fd, F_SETFL, flags | O_NONBLOCK )
428 fds = poller.poll( timeoutms )
430 for fd, event in fds:
431 host = fdToHost[ fd ]
432 popen = popens[ host ]
433 if event & POLLIN or event & POLLHUP:
437 line = decode( f.readline() if readline
438 else f.read( readmax ) )
445 poller.unregister( fd )
451 def sysctlTestAndSet( name, limit ):
452 "Helper function to set sysctl limits"
453 #convert non-directory names into directory names
455 name = '/proc/sys/' + name.replace( '.', '/' )
457 with open( name, 'r' ) as readFile:
458 oldLimit = readFile.readline()
459 if isinstance( limit, int ):
460 #compare integer limits before overriding
461 if int( oldLimit ) < limit:
462 with open( name, 'w' ) as writeFile:
463 writeFile.write( "%d" % limit )
465 #overwrite non-integer limits
466 with open( name, 'w' ) as writeFile:
467 writeFile.write( limit )
469 def rlimitTestAndSet( name, limit ):
470 "Helper function to set rlimits"
471 soft, hard = getrlimit( name )
473 hardLimit = hard if limit < hard else limit
474 setrlimit( name, ( limit, hardLimit ) )
477 "Fix ridiculously small resource limits."
478 debug( "*** Setting resource limits\n" )
480 rlimitTestAndSet( RLIMIT_NPROC, 8192 )
481 rlimitTestAndSet( RLIMIT_NOFILE, 16384 )
482 #Increase open file limit
483 sysctlTestAndSet( 'fs.file-max', 10000 )
484 #Increase network buffer space
485 sysctlTestAndSet( 'net.core.wmem_max', 16777216 )
486 sysctlTestAndSet( 'net.core.rmem_max', 16777216 )
487 sysctlTestAndSet( 'net.ipv4.tcp_rmem', '10240 87380 16777216' )
488 sysctlTestAndSet( 'net.ipv4.tcp_wmem', '10240 87380 16777216' )
489 sysctlTestAndSet( 'net.core.netdev_max_backlog', 5000 )
490 #Increase arp cache size
491 sysctlTestAndSet( 'net.ipv4.neigh.default.gc_thresh1', 4096 )
492 sysctlTestAndSet( 'net.ipv4.neigh.default.gc_thresh2', 8192 )
493 sysctlTestAndSet( 'net.ipv4.neigh.default.gc_thresh3', 16384 )
494 #Increase routing table size
495 sysctlTestAndSet( 'net.ipv4.route.max_size', 32768 )
496 #Increase number of PTYs for nodes
497 sysctlTestAndSet( 'kernel.pty.max', 20000 )
498 # pylint: disable=broad-except
500 warn( "*** Error setting resource limits. "
501 "Mininet's performance may be affected.\n" )
502 # pylint: enable=broad-except
506 "Make sure cgroups file system is mounted"
507 mounts = quietRun( 'grep cgroup /proc/mounts' )
508 cgdir = '/sys/fs/cgroup'
509 csdir = cgdir + '/cpuset'
510 if ('cgroup %s' % cgdir not in mounts and
511 'cgroups %s' % cgdir not in mounts):
512 raise Exception( "cgroups not mounted on " + cgdir )
513 if 'cpuset %s' % csdir not in mounts:
514 errRun( 'mkdir -p ' + csdir )
515 errRun( 'mount -t cgroup -ocpuset cpuset ' + csdir )
518 "To sort sanely/alphabetically: sorted( l, key=natural )"
520 "Convert text segment to int if necessary"
521 return int( s ) if s.isdigit() else s
522 return [ num( s ) for s in re.split( r'(\d+)', str( text ) ) ]
525 "Natural sort key function for sequences"
526 return [ natural( x ) for x in t ]
529 "Returns number of CPU cores based on /proc/cpuinfo"
530 if hasattr( numCores, 'ncores' ):
531 return numCores.ncores
533 numCores.ncores = int( quietRun('grep -c processor /proc/cpuinfo') )
536 return numCores.ncores
538 def irange(start, end):
539 """Inclusive range from start to end (vs. Python insanity.)
540 irange(1,5) -> 1, 2, 3, 4, 5"""
541 return range( start, end + 1 )
543 def custom( cls, **params ):
544 "Returns customized constructor for class cls."
545 # Note: we may wish to see if we can use functools.partial() here
546 # and in customConstructor
547 def customized( *args, **kwargs):
548 "Customized constructor"
549 kwargs = kwargs.copy()
550 kwargs.update( params )
551 return cls( *args, **kwargs )
552 customized.__name__ = 'custom(%s,%s)' % ( cls, params )
555 def splitArgs( argstr ):
556 """Split argument string into usable python arguments
557 argstr: argument string with format fn,arg2,kw1=arg3...
558 returns: fn, args, kwargs"""
559 split = argstr.split( ',' )
562 # Convert int and float args; removes the need for function
563 # to be flexible with input arg formats.
564 args = [ makeNumeric( s ) for s in params if '=' not in s ]
566 for s in [ p for p in params if '=' in p ]:
567 key, val = s.split( '=', 1 )
568 kwargs[ key ] = makeNumeric( val )
569 return fn, args, kwargs
571 def customClass( classes, argStr ):
572 """Return customized class based on argStr
573 The args and key/val pairs in argStr will be automatically applied
574 when the generated class is later used.
576 cname, args, kwargs = splitArgs( argStr )
577 cls = classes.get( cname, None )
579 raise Exception( "error: %s is unknown - please specify one of %s" %
580 ( cname, classes.keys() ) )
581 if not args and not kwargs:
584 return specialClass( cls, append=args, defaults=kwargs )
586 def specialClass( cls, prepend=None, append=None,
587 defaults=None, override=None ):
588 """Like functools.partial, but it returns a class
589 prepend: arguments to prepend to argument list
590 append: arguments to append to argument list
591 defaults: default values for keyword arguments
592 override: keyword arguments to override"""
606 class CustomClass( cls ):
607 "Customized subclass with preset args/params"
608 def __init__( self, *args, **params ):
609 newparams = defaults.copy()
610 newparams.update( params )
611 newparams.update( override )
612 cls.__init__( self, *( list( prepend ) + list( args ) +
616 CustomClass.__name__ = '%s%s' % ( cls.__name__, defaults )
620 def buildTopo( topos, topoStr ):
621 """Create topology from string with format (object, arg1, arg2,...).
622 input topos is a dict of topo names to constructors, possibly w/args.
624 topo, args, kwargs = splitArgs( topoStr )
625 if topo not in topos:
626 raise Exception( 'Invalid topo name %s' % topo )
627 return topos[ topo ]( *args, **kwargs )
630 """Ensure that we are running as root.
632 Probably we should only sudo when needed as per Big Switch's patch.
635 error( '*** Mininet must run as root.\n' )
639 def waitListening( client=None, server='127.0.0.1', port=80, timeout=None ):
640 """Wait until server is listening on port.
641 returns True if server is listening"""
642 runCmd = ( client.cmd if client else
643 partial( quietRun, shell=True ) )
644 if not runCmd( 'which telnet' ):
645 raise Exception('Could not find telnet' )
646 # pylint: disable=maybe-no-member
647 serverIP = server if isinstance( server, BaseString ) else server.IP()
648 cmd = ( 'echo A | telnet -e A %s %s' % ( serverIP, port ) )
650 result = runCmd( cmd )
651 while 'Connected' not in result:
652 if 'No route' in result:
653 rtable = runCmd( 'route' )
654 error( 'no route to %s:\n%s' % ( server, rtable ) )
656 if timeout and time >= timeout:
657 error( 'could not connect to %s on port %d\n' % ( server, port ) )
659 debug( 'waiting for', server, 'to listen on port', port, '\n' )
663 result = runCmd( cmd )