Update and rename MantenerFIFO to MantenerFIFO.md
[vsorcdistro/.git] / mininet / mininet / util.py
1 "Utility functions for Mininet."
2
3
4 from mininet.log import output, info, error, warn, debug
5
6 from time import sleep
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
10 import re
11 from fcntl import fcntl, F_GETFL, F_SETFL
12 from os import O_NONBLOCK
13 import os
14 from functools import partial
15 import sys
16
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
21 def decode( s ):
22     "Decode a byte string if needed for Python 3"
23     return s.decode( Encoding ) if Python3 else s
24 def encode( s ):
25     "Encode a byte string if needed for Python 3"
26     return s.encode( Encoding ) if Python3 else s
27 try:
28     # pylint: disable=import-error
29     oldpexpect = None
30     import pexpect as oldpexpect
31     # pylint: enable=import-error
32
33     class Pexpect( object ):
34         "Custom pexpect that is compatible with str"
35         @staticmethod
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 )
41
42         def __getattr__( self, name ):
43             return getattr( oldpexpect, name )
44     pexpect = Pexpect()
45 except ImportError:
46     pass
47
48
49 # Command execution support
50
51 def run( cmd ):
52     """Simple interface to subprocess.call()
53        cmd: list of command params"""
54     return call( cmd.split( ' ' ) )
55
56 def checkRun( cmd ):
57     """Simple interface to subprocess.check_call()
58        cmd: list of command params"""
59     return check_call( cmd.split( ' ' ) )
60
61 # pylint doesn't understand explicit type checking
62 # pylint: disable=maybe-no-member
63
64 def oldQuietRun( *cmd ):
65     """Run a command, routing stderr to stdout, and return the output.
66        cmd: list of command params"""
67     if len( cmd ) == 1:
68         cmd = cmd[ 0 ]
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.
75     out = ''
76     readable = poll()
77     readable.register( popen.stdout )
78     while True:
79         while readable.poll():
80             data = popen.stdout.read( 1024 )
81             if len( data ) == 0:
82                 break
83             out += data
84         popen.poll()
85         if popen.returncode is not None:
86             break
87     return out
88
89
90 # This is a bit complicated, but it enables us to
91 # monitor command output as it is happening
92
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 )
104     if echo:
105         # cmd goes to stderr, output goes to stdout
106         info( cmd, '\n' )
107     if len( cmd ) == 1:
108         cmd = cmd[ 0 ]
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
119     out, err = '', ''
120     poller = poll()
121     poller.register( popen.stdout, POLLIN )
122     fdtofile = { popen.stdout.fileno(): popen.stdout }
123     outDone, errDone = False, True
124     if popen.stderr:
125         fdtofile[ popen.stderr.fileno() ] = popen.stderr
126         poller.register( popen.stderr, POLLIN )
127         errDone = False
128     while not outDone or not errDone:
129         readable = poller.poll()
130         for fd, event in readable:
131             f = fdtofile[ fd ]
132             if event & POLLIN:
133                 data = f.read( 1024 )
134                 if Python3:
135                     data = data.decode( Encoding )
136                 if echo:
137                     output( data )
138                 if f == popen.stdout:
139                     out += data
140                     if data == '':
141                         outDone = True
142                 elif f == popen.stderr:
143                     err += data
144                     if data == '':
145                         errDone = True
146             else:  # POLLHUP or something unexpected
147                 if f == popen.stdout:
148                     outDone = True
149                 elif f == popen.stderr:
150                     errDone = True
151                 poller.unregister( fd )
152
153     returncode = popen.wait()
154     # Python 3 complains if we don't explicitly close these
155     popen.stdout.close()
156     if stderr == PIPE:
157         popen.stderr.close()
158     debug( out, err, returncode )
159     return out, err, returncode
160 # pylint: enable=too-many-branches
161
162 def errFail( *cmd, **kwargs ):
163     "Run a command using errRun and raise exception on nonzero exit"
164     out, err, ret = errRun( *cmd, **kwargs )
165     if ret:
166         raise Exception( "errFail: %s failed with return code %s: %s"
167                          % ( cmd, ret, err ) )
168     return out, err, ret
169
170 def quietRun( cmd, **kwargs ):
171     "Run a command and return merged stdout and stderr"
172     return errRun( cmd, stderr=STDOUT, **kwargs )[ 0 ]
173
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
178
179 # pylint: enable=maybe-no-member
180
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( ' ' )
186     if space > 0:
187         cmd = cmd[ :space]
188     return cmd in isShellBuiltin.builtIns
189
190 isShellBuiltin.builtIns = None
191
192 # Interface management
193 #
194 # Interfaces are managed as strings which are simply the
195 # interface names, of the form 'nodeN-ethM'.
196 #
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.
200 #
201 # For the kernel datapath, switch interfaces
202 # live in the root namespace and thus do not have to be
203 # explicitly moved.
204
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"""
217     if not runCmd:
218         runCmd = quietRun if not node1 else node1.cmd
219         runCmd2 = quietRun if not node2 else node2.cmd
220     if deleteIntfs:
221         # Delete any old interfaces with the same names
222         runCmd( 'ip link del ' + intf1 )
223         runCmd2( 'ip link del ' + intf2 )
224     # Create new pair
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 ) )
230     else:
231         cmdOutput = runCmd( 'ip link add name %s '
232                             'address %s '
233                             'type veth peer name %s '
234                             'address %s '
235                             'netns %s' %
236                             (  intf1, addr1, intf2, addr2, netns ) )
237     if cmdOutput:
238         raise Exception( "Error creating interface pair (%s,%s): %s " %
239                          ( intf1, intf2, cmdOutput ) )
240
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
245        fn: function to call
246        args: args to apply to function call"""
247     tries = 0
248     while not fn( *args, **keywords ) and tries < retries:
249         sleep( delaySecs )
250         tries += 1
251     if tries >= retries:
252         error( "*** gave up after %i retries\n" % tries )
253         exit( 1 )
254
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"""
260     intf = str( intf )
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.
265     if cmdOutput:
266         if printError:
267             error( '*** Error: moveIntf: ' + intf +
268                    ' not successfully moved to ' + dstNode.name + ':\n',
269                    cmdOutput )
270         return False
271     return True
272
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 )
281
282 # Support for dumping network
283
284 def dumpNodeConnections( nodes ):
285     "Dump connections to/from nodes."
286
287     def dumpConnections( node ):
288         "Helper function: dump connections to node"
289         for intf in node.intfList():
290             output( ' %s:' % intf )
291             if intf.link:
292                 intfs = [ intf.link.intf1, intf.link.intf2 ]
293                 intfs.remove( intf )
294                 output( intfs[ 0 ] )
295             else:
296                 output( ' ' )
297
298     for node in nodes:
299         output( node.name )
300         dumpConnections( node )
301         output( '\n' )
302
303 def dumpNetConnections( net ):
304     "Dump connections in network"
305     nodes = net.controllers + net.switches + net.hosts
306     dumpNodeConnections( nodes )
307
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 ) )
315         output( '\n' )
316
317 # IP and Mac address formatting and parsing
318
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"""
324     pieces = []
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 )
329     return chStr
330
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 )
336
337 def ipStr( ip ):
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
344     z = ip & 0xff
345     return "%i.%i.%i.%i" % ( w, x, y, z )
346
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
351
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 )
363
364 def ipParse( ip ):
365     "Parse an IP address and return an unsigned int."
366     args = [ int( arg ) for arg in ip.split( '.' ) ]
367     while len(args) < 4:
368         args.insert( len(args) - 1, 0 )
369     return ipNum( *args )
370
371 def netParse( ipstr ):
372     """Parse an IP network specification, returning
373        address and prefix len as unsigned ints"""
374     prefixLen = 0
375     if '/' in ipstr:
376         ip, pf = ipstr.split( '/' )
377         prefixLen = int( pf )
378     #if no prefix is specified, set the prefix to 24
379     else:
380         ip = ipstr
381         prefixLen = 24
382     return ipParse( ip ), prefixLen
383
384 def checkInt( s ):
385     "Check if input string is an int"
386     try:
387         int( s )
388         return True
389     except ValueError:
390         return False
391
392 def checkFloat( s ):
393     "Check if input string is a float"
394     try:
395         float( s )
396         return True
397     except ValueError:
398         return False
399
400 def makeNumeric( s ):
401     "Convert string to int or float if numeric."
402     if checkInt( s ):
403         return int( s )
404     elif checkFloat( s ):
405         return float( s )
406     else:
407         return s
408
409 # Popen support
410
411 def pmonitor(popens, timeoutms=500, readline=True,
412              readmax=1024 ):
413     """Monitor dict of hosts to popen objects
414        a line at a time
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"""
419     poller = poll()
420     fdToHost = {}
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 )
427     while popens:
428         fds = poller.poll( timeoutms )
429         if fds:
430             for fd, event in fds:
431                 host = fdToHost[ fd ]
432                 popen = popens[ host ]
433                 if event & POLLIN or event & POLLHUP:
434                     while True:
435                         try:
436                             f = popen.stdout
437                             line = decode( f.readline() if readline
438                                            else f.read( readmax ) )
439                         except IOError:
440                             line = ''
441                         if line == '':
442                             break
443                         yield host, line
444                 if event & POLLHUP:
445                     poller.unregister( fd )
446                     del popens[ host ]
447         else:
448             yield None, ''
449
450 # Other stuff we use
451 def sysctlTestAndSet( name, limit ):
452     "Helper function to set sysctl limits"
453     #convert non-directory names into directory names
454     if '/' not in name:
455         name = '/proc/sys/' + name.replace( '.', '/' )
456     #read limit
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 )
464         else:
465             #overwrite non-integer limits
466             with open( name, 'w' ) as writeFile:
467                 writeFile.write( limit )
468
469 def rlimitTestAndSet( name, limit ):
470     "Helper function to set rlimits"
471     soft, hard = getrlimit( name )
472     if soft < limit:
473         hardLimit = hard if limit < hard else limit
474         setrlimit( name, ( limit, hardLimit ) )
475
476 def fixLimits():
477     "Fix ridiculously small resource limits."
478     debug( "*** Setting resource limits\n" )
479     try:
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
499     except Exception:
500         warn( "*** Error setting resource limits. "
501               "Mininet's performance may be affected.\n" )
502     # pylint: enable=broad-except
503
504
505 def mountCgroups():
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 )
516
517 def natural( text ):
518     "To sort sanely/alphabetically: sorted( l, key=natural )"
519     def num( s ):
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 ) ) ]
523
524 def naturalSeq( t ):
525     "Natural sort key function for sequences"
526     return [ natural( x ) for x in t ]
527
528 def numCores():
529     "Returns number of CPU cores based on /proc/cpuinfo"
530     if hasattr( numCores, 'ncores' ):
531         return numCores.ncores
532     try:
533         numCores.ncores = int( quietRun('grep -c processor /proc/cpuinfo') )
534     except ValueError:
535         return 0
536     return numCores.ncores
537
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 )
542
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 )
553     return customized
554
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( ',' )
560     fn = split[ 0 ]
561     params = split[ 1: ]
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 ]
565     kwargs = {}
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
570
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.
575     """
576     cname, args, kwargs = splitArgs( argStr )
577     cls = classes.get( cname, None )
578     if not cls:
579         raise Exception( "error: %s is unknown - please specify one of %s" %
580                          ( cname, classes.keys() ) )
581     if not args and not kwargs:
582         return cls
583
584     return specialClass( cls, append=args, defaults=kwargs )
585
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"""
593
594     if prepend is None:
595         prepend = []
596
597     if append is None:
598         append = []
599
600     if defaults is None:
601         defaults = {}
602
603     if override is None:
604         override = {}
605
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 ) +
613                                    list( append ) ),
614                           **newparams )
615
616     CustomClass.__name__ = '%s%s' % ( cls.__name__, defaults )
617     return CustomClass
618
619
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.
623     """
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 )
628
629 def ensureRoot():
630     """Ensure that we are running as root.
631
632     Probably we should only sudo when needed as per Big Switch's patch.
633     """
634     if os.getuid() != 0:
635         error( '*** Mininet must run as root.\n' )
636         exit( 1 )
637     return
638
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 ) )
649     time = 0
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 ) )
655             return False
656         if timeout and time >= timeout:
657             error( 'could not connect to %s on port %d\n' % ( server, port ) )
658             return False
659         debug( 'waiting for', server, 'to listen on port', port, '\n' )
660         info( '.' )
661         sleep( .5 )
662         time += .5
663         result = runCmd( cmd )
664     return True