diff --git a/examples/popen.py b/examples/popen.py index d9ddd7a..332822f 100755 --- a/examples/popen.py +++ b/examples/popen.py @@ -1,30 +1,36 @@ #!/usr/bin/python """ -This example tests the Host.popen()/pexec() interface +This example monitors a number of hosts using host.popen() and +pmonitor() """ from mininet.net import Mininet from mininet.node import CPULimitedHost from mininet.topo import SingleSwitchTopo from mininet.log import setLogLevel -# from mininet.cli import CLI -from mininet.util import custom +from mininet.util import custom, pmonitor -def testpopen(sched='cfs'): - "Test popen() interface" - host = custom( CPULimitedHost, cpu=.2, sched=sched ) - net = Mininet( SingleSwitchTopo( 2 ), host=host ) +def monitorhosts( hosts=5, sched='cfs' ): + "Start a bunch of pings and monitor them using popen" + mytopo = SingleSwitchTopo( hosts ) + cpu = .5 / hosts + myhost = custom( CPULimitedHost, cpu=cpu, sched=sched ) + net = Mininet( topo=mytopo, host=myhost ) net.start() - h1 = net.get( 'h1' ) - # CLI(net) - out, err, code = h1.pexec( 'ifconfig' ) - print 'stdout:', out.strip() - print 'stderr:', err.strip() - print 'exit code:', code + # Start a bunch of pings + popens = {} + last = net.hosts[ -1 ] + for host in net.hosts: + popens[ host ] = host.popen( "ping -c5 %s" % last.IP() ) + last = host + # Monitor them and print output + for host, line in pmonitor( popens ): + if host: + print "<%s>: %s" % ( host.name, line.strip() ) + # Done net.stop() if __name__ == '__main__': setLogLevel( 'info' ) - testpopen('rt') - testpopen('cfs') + monitorhosts( hosts=5 ) diff --git a/mininet/util.py b/mininet/util.py index 6016cd3..e6ede1f 100644 --- a/mininet/util.py +++ b/mininet/util.py @@ -1,11 +1,14 @@ "Utility functions for Mininet." +from mininet.log import output, info, error + from time import sleep from resource import setrlimit, RLIMIT_NPROC, RLIMIT_NOFILE from select import poll, POLLIN from subprocess import call, check_call, Popen, PIPE, STDOUT -from mininet.log import output, info, error import re +from fcntl import fcntl, F_GETFL, F_SETFL +from os import O_NONBLOCK # Command execution support @@ -300,6 +303,49 @@ def makeNumeric( s ): else: return s +# Popen support + +def pmonitor(popens, timeoutms=500, readline=True, + readmax=1024 ): + """Monitor dict of hosts to popen objects + a line at a time + timeoutms: timeout for poll() + readline: return single line of output + yields: host, line/output (if any) + terminates: when all EOFs received""" + poller = poll() + fdToHost = {} + for host, popen in popens.iteritems(): + fd = popen.stdout.fileno() + fdToHost[ fd ] = host + poller.register( fd, POLLIN ) + if not readline: + # Use non-blocking reads + flags = fcntl( fd, F_GETFL ) + fcntl( fd, F_SETFL, flags | O_NONBLOCK ) + while True: + fds = poller.poll( timeoutms ) + if fds: + for fd, _event in fds: + host = fdToHost[ fd ] + popen = popens[ host ] + if readline: + # Attempt to read a line of output + # This blocks until we receive a newline! + line = popen.stdout.readline() + else: + line = popen.stdout.read( readmax ) + yield host, line + # Check for EOF + if not line: + popen.poll() + if popen.returncode is not None: + poller.unregister( fd ) + del popens[ host ] + if not popens: + return + else: + yield None, '' # Other stuff we use