def tunnelX11(node, display=None): """Create an X11 tunnel from node:6000 to the root host display: display on root host (optional) returns: node $DISPLAY, Popen object for tunnel""" if display is None and 'DISPLAY' in environ: display = environ['DISPLAY'] if display is None: error("Error: Cannot connect to display\n") return None, None host, screen = display.split(':') # Unix sockets should work if not host or host == 'unix': # GDM3 doesn't put credentials in .Xauthority, # so allow root to just connect quietRun('xhost +si:localuser:root') return display, None else: # Create a tunnel for the TCP connection port = 6000 + int(float(screen)) connection = r'TCP\:%s\:%s' % (host, port) cmd = [ "socat", "TCP-LISTEN:%d,fork,reuseaddr" % port, "EXEC:'mnexec -a 1 socat STDIO %s'" % connection ] return 'localhost:' + screen, node.popen(cmd)
def run(self): "Run our cmdloop(), catching KeyboardInterrupt" while True: try: # Make sure no nodes are still waiting for node in self.mn.values(): while node.waiting: info('stopping', node, '\n') node.sendInt() node.waitOutput() if self.isatty(): quietRun('stty echo sane intr ^C') self.cmdloop() break except KeyboardInterrupt: # Output a message - unless it's also interrupted # pylint: disable=broad-except try: output('\nInterrupt\n') except Exception: pass # pylint: enable=broad-except except Exception as e: info("Exiting cmd loop due to error.\n") self.mn.stop() raise
def do_noecho(self, line): """Run an interactive command with echoing turned off. Usage: noecho [cmd args]""" if self.isatty(): quietRun('stty -echo') self.default(line) if self.isatty(): quietRun('stty echo')
def findUser(): "Try to return logged-in (usually non-root) user" return ( # If we're running sudo os.environ.get( 'SUDO_USER', False ) or # Logged-in user (if we have a tty) ( quietRun( 'who am i' ).split() or [ False ] )[ 0 ] or # Give up and return effective user quietRun( 'whoami' ).strip() )
def stop(self, wait=True): "Interrupt all hosts." consoles = self.consoles['hosts'].consoles for console in consoles: console.handleInt() if wait: for console in consoles: console.waitOutput() self.setOutputHook(None) # Shut down any iperfs that might still be running quietRun('killall -9 iperf')
def testRemoteNet( remote='ubuntu2' ): "Test remote Node classes" print '*** Remote Node Test' net = Mininet( host=RemoteHost, switch=RemoteOVSSwitch, link=RemoteLink ) c0 = net.addController( 'c0' ) # Make sure controller knows its non-loopback address Intf( 'eth0', node=c0 ).updateIP() print "*** Creating local h1" h1 = net.addHost( 'h1' ) print "*** Creating remote h2" h2 = net.addHost( 'h2', server=remote ) print "*** Creating local s1" s1 = net.addSwitch( 's1' ) print "*** Creating remote s2" s2 = net.addSwitch( 's2', server=remote ) print "*** Adding links" net.addLink( h1, s1 ) net.addLink( s1, s2 ) net.addLink( h2, s2 ) net.start() print 'Mininet is running on', quietRun( 'hostname' ).strip() for node in c0, h1, h2, s1, s2: print 'Node', node, 'is running on', node.cmd( 'hostname' ).strip() net.pingAll() CLI( net ) net.stop()
def runCpuLimitTest(self, cpu, duration=5): """run CPU limit test with 'while true' processes. cpu: desired CPU fraction of each host duration: test duration in seconds (integer) returns a single list of measured CPU fractions as floats. """ cores = int(quietRun('nproc')) pct = cpu * 100 info('*** Testing CPU %.0f%% bandwidth limit\n' % pct) hosts = self.hosts cores = int(quietRun('nproc')) # number of processes to run a while loop on per host num_procs = int(ceil(cores * cpu)) pids = {} for h in hosts: pids[h] = [] for _core in range(num_procs): h.cmd('while true; do a=1; done &') pids[h].append(h.cmd('echo $!').strip()) outputs = {} time = {} # get the initial cpu time for each host for host in hosts: outputs[host] = [] with open('/sys/fs/cgroup/cpuacct/%s/cpuacct.usage' % host, 'r') as f: time[host] = float(f.read()) for _ in range(duration): sleep(1) for host in hosts: with open('/sys/fs/cgroup/cpuacct/%s/cpuacct.usage' % host, 'r') as f: readTime = float(f.read()) outputs[host].append(((readTime - time[host]) / 1000000000) / cores * 100) time[host] = readTime for h, pids in pids.items(): for pid in pids: h.cmd('kill -9 %s' % pid) cpu_fractions = [] for _host, outputs in outputs.items(): for pct in outputs: cpu_fractions.append(pct) output('*** Results: %s\n' % cpu_fractions) return cpu_fractions
def setup(cls): "Check dependencies and warn about firewalling" pathCheck('brctl', moduleName='bridge-utils') # Disable Linux bridge firewalling so that traffic can flow! for table in 'arp', 'ip', 'ip6': cmd = 'sysctl net.bridge.bridge-nf-call-%stables' % table out = quietRun(cmd).strip() if out.endswith('1'): warn('Warning: Linux bridge may not work with', out, '\n')
def pathCheck(*args, **kwargs): "Make sure each program in *args can be found in $PATH." moduleName = kwargs.get('moduleName', 'it') for arg in args: if not quietRun('which ' + arg): error('Cannot find required executable %s.\n' % arg + 'Please make sure that %s is installed ' % moduleName + 'and available in your $PATH:\n(%s)\n' % environ['PATH']) exit(1)
def checkIntf( intf ): "Make sure intf exists and is not configured." config = quietRun( 'ifconfig %s 2>/dev/null' % intf, shell=True ) if not config: error( 'Error:', intf, 'does not exist!\n' ) exit( 1 ) ips = re.findall( r'\d+\.\d+\.\d+\.\d+', config ) if ips: error( 'Error:', intf, 'has an IP address,' 'and is probably in use!\n' ) exit( 1 )
def findServerIP( cls, server ): "Return our server's IP address" # First, check for an IP address ipmatch = cls._ipMatchRegex.findall( server ) if ipmatch: return ipmatch[ 0 ] # Otherwise, look up remote server output = quietRun( 'getent ahostsv4 %s' % server ) ips = cls._ipMatchRegex.findall( output ) ip = ips[ 0 ] if ips else None return ip
def cleanup( cls ): "Clean up" info( '*** Cleaning up cluster\n' ) for server, user in cls.serveruser.iteritems(): if server == 'localhost': # Handled by mininet.clean.cleanup() continue else: cmd = [ 'su', user, '-c', 'ssh %s@%s sudo mn -c' % ( user, server ) ] info( cmd, '\n' ) info( quietRun( cmd ) )
def limit(bw=10, cpu=.1): """Example/test of link and CPU bandwidth limits bw: interface bandwidth limit in Mbps cpu: cpu limit as fraction of overall CPU time""" intf = custom(TCIntf, bw=bw) myTopo = TreeTopo(depth=1, fanout=2) for sched in 'rt', 'cfs': info('*** Testing with', sched, 'bandwidth limiting\n') if sched == 'rt': release = quietRun('uname -r').strip('\r\n') output = quietRun('grep CONFIG_RT_GROUP_SCHED /boot/config-%s' % release) if output == '# CONFIG_RT_GROUP_SCHED is not set\n': info('*** RT Scheduler is not enabled in your kernel. ' 'Skipping this test\n') continue host = custom(CPULimitedHost, sched=sched, cpu=cpu) net = Mininet(topo=myTopo, intf=intf, host=host) net.start() testLinkLimit(net, bw=bw) net.runCpuLimitTest(cpu=cpu) net.stop()
def waitForNode(self, node): "Wait for a node to finish, and print its output." # Pollers nodePoller = poll() nodePoller.register(node.stdout) bothPoller = poll() bothPoller.register(self.stdin, POLLIN) bothPoller.register(node.stdout, POLLIN) if self.isatty(): # Buffer by character, so that interactive # commands sort of work quietRun('stty -isig -icanon min 1') while node.shell: try: bothPoller.poll() # XXX BL: this doesn't quite do what we want. if False and self.inputFile: key = self.inputFile.read(1) if key is not '': node.write(key) else: self.inputFile = None if isReadable(self.inPoller): key = self.stdin.read(1) node.write(key) if isReadable(nodePoller): data = node.monitor() output(data) if not node.waiting: quietRun('stty isig') break except KeyboardInterrupt: # There is an at least one race condition here, since # it's possible to interrupt ourselves after we've # read data but before it has been printed. node.sendInt()
class testNAT(unittest.TestCase): prompt = 'mininet>' @unittest.skipIf('0 received' in quietRun('ping -c 1 %s' % destIP), 'Destination IP is not reachable') def testNAT(self): "Attempt to ping an IP on the Internet and verify 0% packet loss" p = pexpect.spawn('python -m mininet.examples.nat') p.expect(self.prompt) p.sendline('h1 ping -c 1 %s' % destIP) p.expect('(\d+)% packet loss') percent = int(p.match.group(1)) if p.match else -1 p.expect(self.prompt) p.sendline('exit') p.wait() self.assertEqual(percent, 0)
def scratchNet( cname='controller', cargs='-v ptcp:' ): "Create network from scratch using Open vSwitch." info( "*** Creating nodes\n" ) controller = Node( 'c0', inNamespace=False ) switch = Node( 's0', inNamespace=False ) h0 = Node( 'h0' ) h1 = Node( 'h1' ) info( "*** Creating links\n" ) Link( h0, switch ) Link( h1, switch ) info( "*** Configuring hosts\n" ) h0.setIP( '192.168.123.1/24' ) h1.setIP( '192.168.123.2/24' ) info( str( h0 ) + '\n' ) info( str( h1 ) + '\n' ) info( "*** Starting network using Open vSwitch\n" ) controller.cmd( cname + ' ' + cargs + '&' ) switch.cmd( 'ovs-vsctl del-br dp0' ) switch.cmd( 'ovs-vsctl add-br dp0' ) for intf in switch.intfs.values(): print switch.cmd( 'ovs-vsctl add-port dp0 %s' % intf ) # Note: controller and switch are in root namespace, and we # can connect via loopback interface switch.cmd( 'ovs-vsctl set-controller dp0 tcp:127.0.0.1:6633' ) info( '*** Waiting for switch to connect to controller' ) while 'is_connected' not in quietRun( 'ovs-vsctl show' ): sleep( 1 ) info( '.' ) info( '\n' ) info( "*** Running test\n" ) h0.cmdPrint( 'ping -c1 ' + h1.IP() ) info( "*** Stopping network\n" ) controller.cmd( 'kill %' + cname ) switch.cmd( 'ovs-vsctl del-br dp0' ) switch.deleteIntfs() info( '\n' )
def exampleCustomTags(): """Simple example that exercises VLANStarTopo""" net = Mininet(topo=VLANStarTopo()) net.start() CLI(net) net.stop() if __name__ == '__main__': import sys from functools import partial from src.mininet.net import Mininet from src.mininet.cli import CLI from src.mininet.topo import SingleSwitchTopo from src.mininet.log import setLogLevel setLogLevel('info') if not quietRun('which vconfig'): error("Cannot find command 'vconfig'\nThe package", "'vlan' is required in Ubuntu or Debian,", "or 'vconfig' in Fedora\n") exit() if len(sys.argv) >= 2: exampleAllHosts(vlan=int(sys.argv[1])) else: exampleCustomTags()
def rmmod(mod): """Return output of lsmod. mod: module string""" return quietRun(['rmmod', mod])
def modprobe(mod): """Return output of modprobe mod: module string""" return quietRun(['modprobe', mod])
def lsmod(): "Return output of lsmod." return quietRun('lsmod')
def tsharkVersion(): "Return tshark version" versionStr = quietRun('tshark -v') versionMatch = re.findall(r'TShark \d+.\d+.\d+', versionStr)[0] return versionMatch.split()[1]
class testWalkthrough(unittest.TestCase): "Test Mininet walkthrough" prompt = 'mininet>' # PART 1 def testHelp(self): "Check the usage message" p = pexpect.spawn('mn -h') index = p.expect(['Usage: mn', pexpect.EOF]) self.assertEqual(index, 0) def testWireshark(self): "Use tshark to test the of dissector" # Satisfy pylint assert self if StrictVersion(tsharkVersion()) < StrictVersion('1.12.0'): tshark = pexpect.spawn('tshark -i lo -R of') else: tshark = pexpect.spawn('tshark -i lo -Y openflow_v1') tshark.expect(['Capturing on lo', "Capturing on 'Loopback'"]) mn = pexpect.spawn('mn --test pingall') mn.expect('0% dropped') tshark.expect(['74 Hello', '74 of_hello', '74 Type: OFPT_HELLO']) tshark.sendintr() def testBasic(self): "Test basic CLI commands (help, nodes, net, dump)" p = pexpect.spawn('mn') p.expect(self.prompt) # help command p.sendline('help') index = p.expect(['commands', self.prompt]) self.assertEqual(index, 0, 'No output for "help" command') # nodes command p.sendline('nodes') p.expect(r'([chs]\d ?){4}') nodes = p.match.group(0).split() self.assertEqual(len(nodes), 4, 'No nodes in "nodes" command') p.expect(self.prompt) # net command p.sendline('net') expected = [x for x in nodes] while len(expected) > 0: index = p.expect(expected) node = p.match.group(0) expected.remove(node) p.expect('\n') self.assertEqual(len(expected), 0, '"nodes" and "net" differ') p.expect(self.prompt) # dump command p.sendline('dump') expected = [r'<\w+ (%s)' % n for n in nodes] actual = [] for _ in nodes: index = p.expect(expected) node = p.match.group(1) actual.append(node) p.expect('\n') self.assertEqual(actual.sort(), nodes.sort(), '"nodes" and "dump" differ') p.expect(self.prompt) p.sendline('exit') p.wait() def testHostCommands(self): "Test ifconfig and ps on h1 and s1" p = pexpect.spawn('mn') p.expect(self.prompt) interfaces = ['h1-eth0', 's1-eth1', '[^-]eth0', 'lo', self.prompt] # h1 ifconfig p.sendline('h1 ifconfig -a') ifcount = 0 while True: index = p.expect(interfaces) if index == 0 or index == 3: ifcount += 1 elif index == 1: self.fail('s1 interface displayed in "h1 ifconfig"') elif index == 2: self.fail('eth0 displayed in "h1 ifconfig"') else: break self.assertEqual(ifcount, 2, 'Missing interfaces on h1') # s1 ifconfig p.sendline('s1 ifconfig -a') ifcount = 0 while True: index = p.expect(interfaces) if index == 0: self.fail('h1 interface displayed in "s1 ifconfig"') elif index == 1 or index == 2 or index == 3: ifcount += 1 else: break self.assertEqual(ifcount, 3, 'Missing interfaces on s1') # h1 ps p.sendline("h1 ps -a | egrep -v 'ps|grep'") p.expect(self.prompt) h1Output = p.before # s1 ps p.sendline("s1 ps -a | egrep -v 'ps|grep'") p.expect(self.prompt) s1Output = p.before # strip command from ps output h1Output = h1Output.split('\n', 1)[1] s1Output = s1Output.split('\n', 1)[1] self.assertEqual(h1Output, s1Output, 'h1 and s1 "ps" output differs') p.sendline('exit') p.wait() def testConnectivity(self): "Test ping and pingall" p = pexpect.spawn('mn') p.expect(self.prompt) p.sendline('h1 ping -c 1 h2') p.expect('1 packets transmitted, 1 received') p.expect(self.prompt) p.sendline('pingall') p.expect('0% dropped') p.expect(self.prompt) p.sendline('exit') p.wait() def testSimpleHTTP(self): "Start an HTTP server on h1 and wget from h2" p = pexpect.spawn('mn') p.expect(self.prompt) p.sendline('h1 python -m SimpleHTTPServer 80 &') p.expect(self.prompt) p.sendline(' h2 wget -O - h1') p.expect('200 OK') p.expect(self.prompt) p.sendline('h1 kill %python') p.expect(self.prompt) p.sendline('exit') p.wait() # PART 2 def testRegressionRun(self): "Test pingpair (0% drop) and iperf (bw > 0) regression tests" # test pingpair p = pexpect.spawn('mn --test pingpair') p.expect('0% dropped') p.expect(pexpect.EOF) # test iperf p = pexpect.spawn('mn --test iperf') p.expect(r"Results: \['([\d\.]+) .bits/sec',") bw = float(p.match.group(1)) self.assertTrue(bw > 0) p.expect(pexpect.EOF) def testTopoChange(self): "Test pingall on single,3 and linear,4 topos" # testing single,3 p = pexpect.spawn('mn --test pingall --topo single,3') p.expect(r'(\d+)/(\d+) received') received = int(p.match.group(1)) sent = int(p.match.group(2)) self.assertEqual(sent, 6, 'Wrong number of pings sent in single,3') self.assertEqual(sent, received, 'Dropped packets in single,3') p.expect(pexpect.EOF) # testing linear,4 p = pexpect.spawn('mn --test pingall --topo linear,4') p.expect(r'(\d+)/(\d+) received') received = int(p.match.group(1)) sent = int(p.match.group(2)) self.assertEqual(sent, 12, 'Wrong number of pings sent in linear,4') self.assertEqual(sent, received, 'Dropped packets in linear,4') p.expect(pexpect.EOF) def testLinkChange(self): "Test TCLink bw and delay" p = pexpect.spawn('mn --link tc,bw=10,delay=10ms') # test bw p.expect(self.prompt) p.sendline('iperf') p.expect(r"Results: \['([\d\.]+) Mbits/sec',") bw = float(p.match.group(1)) self.assertTrue(bw < 10.1, 'Bandwidth > 10 Mb/s') self.assertTrue(bw > 9.0, 'Bandwidth < 9 Mb/s') p.expect(self.prompt) # test delay p.sendline('h1 ping -c 4 h2') p.expect(r'rtt min/avg/max/mdev = ' r'([\d\.]+)/([\d\.]+)/([\d\.]+)/([\d\.]+) ms') delay = float(p.match.group(2)) self.assertTrue(delay > 40, 'Delay < 40ms') self.assertTrue(delay < 45, 'Delay > 40ms') p.expect(self.prompt) p.sendline('exit') p.wait() def testVerbosity(self): "Test debug and output verbosity" # test output p = pexpect.spawn('mn -v output') p.expect(self.prompt) self.assertEqual(len(p.before), 0, 'Too much output for "output"') p.sendline('exit') p.wait() # test debug p = pexpect.spawn('mn -v debug --test none') p.expect(pexpect.EOF) lines = p.before.split('\n') self.assertTrue(len(lines) > 70, "Debug output is too short") def testCustomTopo(self): "Start Mininet using a custom topo, then run pingall" # Satisfy pylint assert self custom = os.path.dirname(os.path.realpath(__file__)) custom = os.path.join(custom, '../../custom/topo-2sw-2host.py') custom = os.path.normpath(custom) p = pexpect.spawn('mn --custom %s --topo mytopo --test pingall' % custom) p.expect('0% dropped') p.expect(pexpect.EOF) def testStaticMAC(self): "Verify that MACs are set to easy to read numbers" p = pexpect.spawn('mn --mac') p.expect(self.prompt) for i in range(1, 3): p.sendline('h%d ifconfig' % i) p.expect('HWaddr 00:00:00:00:00:0%d' % i) p.expect(self.prompt) def testSwitches(self): "Run iperf test using user and ovsk switches" switches = ['user', 'ovsk'] for sw in switches: p = pexpect.spawn('mn --switch %s --test iperf' % sw) p.expect(r"Results: \['([\d\.]+) .bits/sec',") bw = float(p.match.group(1)) self.assertTrue(bw > 0) p.expect(pexpect.EOF) def testBenchmark(self): "Run benchmark and verify that it takes less than 2 seconds" p = pexpect.spawn('mn --test none') p.expect(r'completed in ([\d\.]+) seconds') time = float(p.match.group(1)) self.assertTrue(time < 2, 'Benchmark takes more than 2 seconds') def testOwnNamespace(self): "Test running user switch in its own namespace" p = pexpect.spawn('mn --innamespace --switch user') p.expect(self.prompt) interfaces = ['h1-eth0', 's1-eth1', '[^-]eth0', 'lo', self.prompt] p.sendline('s1 ifconfig -a') ifcount = 0 while True: index = p.expect(interfaces) if index == 1 or index == 3: ifcount += 1 elif index == 0: self.fail('h1 interface displayed in "s1 ifconfig"') elif index == 2: self.fail('eth0 displayed in "s1 ifconfig"') else: break self.assertEqual(ifcount, 2, 'Missing interfaces on s1') # verify that all hosts a reachable p.sendline('pingall') p.expect(r'(\d+)% dropped') dropped = int(p.match.group(1)) self.assertEqual(dropped, 0, 'pingall failed') p.expect(self.prompt) p.sendline('exit') p.wait() # PART 3 def testPythonInterpreter(self): "Test py and px by checking IP for h1 and adding h3" p = pexpect.spawn('mn') p.expect(self.prompt) # test host IP p.sendline('py h1.IP()') p.expect('10.0.0.1') p.expect(self.prompt) # test adding host p.sendline("px net.addHost('h3')") p.expect(self.prompt) p.sendline("px net.addLink(s1, h3)") p.expect(self.prompt) p.sendline('net') p.expect('h3') p.expect(self.prompt) p.sendline('py h3.MAC()') p.expect('([a-f0-9]{2}:?){6}') p.expect(self.prompt) p.sendline('exit') p.wait() def testLink(self): "Test link CLI command using ping" p = pexpect.spawn('mn') p.expect(self.prompt) p.sendline('link s1 h1 down') p.expect(self.prompt) p.sendline('h1 ping -c 1 h2') p.expect('unreachable') p.expect(self.prompt) p.sendline('link s1 h1 up') p.expect(self.prompt) p.sendline('h1 ping -c 1 h2') p.expect('0% packet loss') p.expect(self.prompt) p.sendline('exit') p.wait() @unittest.skipUnless( os.path.exists('/tmp/pox') or '1 received' in quietRun('ping -c 1 github.com'), 'Github is not reachable; cannot download Pox') def testRemoteController(self): "Test Mininet using Pox controller" # Satisfy pylint assert self if not os.path.exists('/tmp/pox'): p = pexpect.spawn( 'git clone https://github.com/noxrepo/pox.git /tmp/pox') p.expect(pexpect.EOF) pox = pexpect.spawn('/tmp/pox/pox.py forwarding.l2_learning') net = pexpect.spawn( 'mn --controller=remote,ip=127.0.0.1,port=6633 --test pingall') net.expect('0% dropped') net.expect(pexpect.EOF) pox.sendintr() pox.wait()
class OVSUser(OVSSwitch): "OVS User Switch convenience class" def __init__(self, *args, **kwargs): kwargs.update(datapath='user') OVSSwitch.__init__(self, *args, **kwargs) class testSwitchOVSUser(TestSwitchDpidAssignmentOVS): "Test dpid assignnment of OVS User Switch." switchClass = OVSUser @unittest.skipUnless(quietRun('which ivs-ctl'), 'IVS switch is not installed') class testSwitchIVS(TestSwitchDpidAssignmentOVS): "Test dpid assignment of IVS switch." switchClass = IVSSwitch @unittest.skipUnless(quietRun('which ofprotocol'), 'Reference user switch is not installed') class testSwitchUserspace(TestSwitchDpidAssignmentOVS): "Test dpid assignment of Userspace switch." switchClass = UserSwitch if __name__ == '__main__': setLogLevel('warning') unittest.main()
def linearBandwidthTest(lengths): "Check bandwidth at various lengths along a switch chain." results = {} switchCount = max(lengths) hostCount = switchCount + 1 switches = { 'reference user': UserSwitch, 'Open vSwitch kernel': OVSKernelSwitch } # UserSwitch is horribly slow with recent kernels. # We can reinstate it once its performance is fixed del switches['reference user'] topo = LinearTestTopo(hostCount) # Select TCP Reno output = quietRun('sysctl -w net.ipv4.tcp_congestion_control=reno') assert 'reno' in output for datapath in switches.keys(): print "*** testing", datapath, "datapath" Switch = switches[datapath] results[datapath] = [] link = partial(TCLink, delay='1ms') net = Mininet(topo=topo, switch=Switch, controller=Controller, waitConnected=True, link=link) net.start() print "*** testing basic connectivity" for n in lengths: net.ping([net.hosts[0], net.hosts[n]]) print "*** testing bandwidth" for n in lengths: src, dst = net.hosts[0], net.hosts[n] # Try to prime the pump to reduce PACKET_INs during test # since the reference controller is reactive src.cmd('telnet', dst.IP(), '5001') print "testing", src.name, "<->", dst.name, bandwidth = net.iperf([src, dst], seconds=10) print bandwidth flush() results[datapath] += [(n, bandwidth)] net.stop() for datapath in switches.keys(): print print "*** Linear network results for", datapath, "datapath:" print result = results[datapath] print "SwitchCount\tiperf Results" for switchCount, bandwidth in result: print switchCount, '\t\t', print bandwidth[0], 'server, ', bandwidth[1], 'client' print print