Compare commits

..

33 Commits

Author SHA1 Message Date
Bob Lantz 31f44e1856 Change Link/TCLink to accept universal parameters
This also enables us to specify IP addresses for both sides
of a TCLink, which enables LinuxRouter to work with TCLink.

fixes #854
fixes #634
2019-03-06 15:01:27 -08:00
lantz 36d2b21187 Merge pull request #852 from gabay/patch/rename_init_to_build
Replaced example topologies __init__ with build
2019-02-06 13:22:44 -08:00
None 1888001555 fix kwargs names so they wont spark a warning 2019-01-17 13:06:53 -08:00
None fdc3156a91 Replaced example topologies __init__ with build 2019-01-17 12:01:09 -08:00
lantz cf6da391fa Merge pull request #847 from yousong/poll-eintr
cli: ignore EINTR caused by SIGWINCH when calling poll()
2018-12-20 11:36:42 -08:00
Yousong Zhou f170cc6f64 cli: ignore EINTR caused by SIGWINCH when calling poll()
This can happen when adjusting a tmux pane running mininet cli.  The
process will receive SIGWINCH causing the poll() call to raise
select.error

Below is a session dump with strace

	09:55:52.498106 poll([{fd=0, events=POLLIN}, {fd=28, events=POLLIN}], 2, -1) = ? ERESTART_RESTARTBLOCK (Interrupted by signal)
	09:55:52.606734 --- SIGWINCH {si_signo=SIGWINCH, si_code=SI_KERNEL} ---
	09:55:52.606770 rt_sigreturn({mask=[]}) = -1 EINTR (Interrupted system call)
	Traceback (most recent call last):
	  File "examples/topofac.py", line 527, in <module>
	    run()
	  File "examples/topofac.py", line 522, in run
	    CLI( net )
	  File "/home/yunion/git-repo/mininet/mininet/cli.py", line 68, in __init__
	    self.run()
	  File "/home/yunion/git-repo/mininet/mininet/cli.py", line 103, in run
	    self.cmdloop()
	  File "/home/yunion/.usr/lib/python2.7/cmd.py", line 142, in cmdloop
	    stop = self.onecmd(line)
	  File "/home/yunion/.usr/lib/python2.7/cmd.py", line 220, in onecmd
	    return self.default(line)
	  File "/home/yunion/git-repo/mininet/mininet/cli.py", line 422, in default
	    self.waitForNode( node )
	  File "/home/yunion/git-repo/mininet/mininet/cli.py", line 440, in waitForNode
	    bothPoller.poll()
	select.error: (4, 'Interrupted system call')
	09:55:52.622076 rt_sigaction(SIGINT, {SIG_DFL, [], SA_RESTORER, 0x7ff9a9b936d0}, {0x7ff9a9ef2230, [], SA_RESTORER, 0x7ff9a9b936d0}, 8) = 0
	09:55:52.631905 +++ exited with 1 +++
2018-12-14 02:46:08 +00:00
Bob Lantz fd96de6485 Add iproute back in for older dists 2018-12-07 17:08:41 -08:00
lantz 73f530b569 Merge pull request #841 from gabay/master
replaces isinstance(str) with isinstance(BaseString)
(note we define BaseString as str for python 3)
2018-11-15 06:40:28 -08:00
gabay c7a27b8876 replaces isinstance(str) with isinstance(BaseString) 2018-11-15 16:15:58 +02:00
Bob Lantz b7a6b8137f Fix undefined variable and code check. 2018-10-19 13:38:06 -07:00
lantz d072e531c2 Merge pull request #837 from theojepsen/master
intf.rename() also renames the intf name in the host
2018-10-11 15:51:48 -07:00
Bob Lantz 8139d7d1b4 Better IP address selection for controllers
Previously we looked for 'eth0', but linux has renamed interfaces.

Instead of looking by name, we now look for an interface which
is the appropriate gateway to a remote server address.

fixes #831
2018-10-11 15:46:57 -07:00
Theo 273b14b771 intf.rename() also renames the intf name in the host 2018-10-11 09:31:26 +02:00
Bob Lantz bfda33544a code check 2018-08-28 18:48:50 -07:00
Bob Lantz 1f4525ed0a Test for Node()/Host() pty leakage 2018-08-28 18:48:45 -07:00
Bob Lantz 1969669f51 pass code check 2018-08-23 13:06:16 -07:00
Bob Lantz c639f342e5 2.3.0d4 2018-08-23 11:43:45 -07:00
Bob Lantz 6e887d0788 Close pty to avoid leak 2018-08-23 11:43:43 -07:00
Bob Lantz 092863f113 2.3.0d3 2018-08-15 23:13:20 -07:00
Bob Lantz 5100a38fcd pmonitor() fixes
pmonitor() is hard to get right, but this seems like
a reasonable improvement:

1. non-blocking I/O so we can drain buffer with impunity
2. drain buffer after poll
3. unregister fd on POLLHUP

Note we may have a unicode splitting problem since we're
decoding chunks of bytes, but that is a problem for another
day I think.
2018-08-02 12:45:35 -07:00
Bob Lantz 3bcecd8e08 Fix popen() to work properly with shell=True 2018-08-02 06:56:18 -07:00
Bob Lantz 486676d617 2.3.0d2 2018-07-26 14:04:08 -07:00
lantz 9e5280b4d1 Python 3 Compatibility (Merge pull request #817 from lantz/py3-compat)
Changes for compatibility with Python 3.

The approach is to make as few changes as possible to maintain compatibility with both Python 2 and Python 3.

We use whatever python is installed, and also support a PYTHON environment variable for installing another version.

For simplicity, we provide mininet.util.pexpect which works out of the box with Python 3 utf-8 strings.

Thanks to @cuihantao for looking at this as well and also for changes to MiniEdit.

Closes #794
2018-07-26 14:03:34 -07:00
Bob Lantz b70eed69d3 Fix typo (50s -> 50ms) 2018-07-26 13:52:15 -07:00
Bob Lantz d96b35694a Use appropriate python version 2018-07-26 13:40:12 -07:00
Bob Lantz c7deeae11c Add sanity check for python mn version 2018-07-26 13:40:12 -07:00
Bob Lantz 323989953b Try to fix pylint errors ;-p 2018-07-26 13:40:12 -07:00
Bob Lantz 17f9756e5c sudo interferes with travis python 3 environment 2018-07-26 13:40:12 -07:00
Bob Lantz 71f3931f2f Remove redundancies in miniedit; pass code check 2018-07-26 13:40:12 -07:00
Bob Lantz 1e9ca5f7fc Fix missing quote in .travis.yml 2018-07-26 13:40:12 -07:00
Bob Lantz 8933c996cb Add python-tk/python3-tk 2018-07-26 13:40:06 -07:00
Bob Lantz afa83cd5ae Restore python 2 compatibility 2018-07-26 13:40:03 -07:00
Hantao Cui 3e81ea7455 miniedit.py changes for Python 3 2018-07-26 13:39:49 -07:00
20 changed files with 146 additions and 102 deletions
+15 -15
View File
@@ -1,35 +1,35 @@
language: python
sudo: required
python:
- "2.7
- "3.6"
matrix:
include:
- dist: trusty
python: 2.7
env: dist="14.04 LTS trusty"
- dist: trusty
python: 3.6
env: dist="14.04 LTS trusty"
# - dist: xenial
# env: dist="16.04 LTS xenial"
# Travis-CI only proposes 14.04 LTS Trusty and there is no plan to update to 16.04 xenial
# (c.f. https://github.com/travis-ci/travis-ci/issues/5821)
# It is useless to add a second job because it will run in the same Ubuntu version (14.04)
before_install:
- sudo apt-get update -qq
- sudo apt-get install -qq vlan
- sudo util/install.sh -n
- PYTHON=`which python` util/install.sh -n
install:
- bash -c "if [ `lsb_release -rs` == '14.04' ]; then make codecheck; fi"
- sudo util/install.sh -fnvw
- pip install pexpect || pip3 install pexpect
- util/install.sh -nfvw
script:
- sudo mn --test pingall
- sudo python mininet/test/runner.py -v -quick
- sudo python examples/test/runner.py -v -quick
- alias sudo="sudo env PATH=$PATH"
- export PYTHON=`which python`
- echo 'px import sys; print(sys.version_info)' | sudo $PYTHON bin/mn -v output
- sudo $PYTHON bin/mn --test pingall
- sudo $PYTHON mininet/test/runner.py -v -quick
- sudo $PYTHON examples/test/runner.py -v -quick
notifications:
email:
on_success: never
# More details: https://docs.travis-ci.com/user/notifications#Configuring-email-notifications
# More details: https://docs.travis-ci.com/user/notifications
+1 -1
View File
@@ -2,7 +2,7 @@
Mininet Installation/Configuration Notes
----------------------------------------
Mininet 2.3.0d1
Mininet 2.3.0d4
---
The supported installation methods for Mininet are 1) using a
+1 -1
View File
@@ -1,4 +1,4 @@
Mininet 2.3.0d1 License
Mininet 2.3.0d4 License
Copyright (c) 2013-2018 Open Networking Laboratory
Copyright (c) 2009-2012 Bob Lantz and The Board of Trustees of
+2 -2
View File
@@ -2,7 +2,7 @@ Mininet: Rapid Prototyping for Software Defined Networks
========================================================
*The best way to emulate almost any network on your laptop!*
Mininet 2.3.0d1
Mininet 2.3.0d4
[![Build Status][1]](https://travis-ci.org/mininet/mininet)
@@ -126,7 +126,7 @@ hard work that Mininet continues to grow and improve.
Best wishes, and we look forward to seeing what you can do with
Mininet to change the networking world!
Bob Lantz
Bob Lantz
Mininet Core Team
[1]: https://travis-ci.org/mininet/mininet.svg?branch=master
+1 -4
View File
@@ -13,12 +13,9 @@ from mininet.topo import Topo
class MyTopo( Topo ):
"Simple topology example."
def __init__( self ):
def build( self ):
"Create custom topo."
# Initialize topology
Topo.__init__( self )
# Add hosts and switches
leftHost = self.addHost( 'h1' )
rightHost = self.addHost( 'h2' )
+20 -5
View File
@@ -843,11 +843,26 @@ class MininetCluster( Mininet ):
def addController( self, *args, **kwargs ):
"Patch to update IP address to global IP address"
controller = Mininet.addController( self, *args, **kwargs )
# Update IP address for controller that may not be local
if ( isinstance( controller, Controller)
and controller.IP() == '127.0.0.1'
and ' eth0:' in controller.cmd( 'ip link show' ) ):
Intf( 'eth0', node=controller ).updateIP()
loopback = '127.0.0.1'
if ( not isinstance( controller, Controller ) or
controller.IP() != loopback ):
return
# Find route to a different server IP address
serverIPs = [ ip for ip in self.serverIP.values()
if ip is not controller.IP() ]
if not serverIPs:
return # no remote servers - loopback is fine
remoteIP = serverIPs[ 0 ]
# Route should contain 'dev <intfname>'
route = controller.cmd( 'ip route get', remoteIP,
r'| egrep -o "dev\s[^[:space:]]+"' )
if not route:
raise Exception('addController: no route from', controller,
'to', remoteIP )
intf = route.split()[ 1 ].strip()
debug( 'adding', intf, 'to', controller )
Intf( intf, node=controller ).updateIP()
debug( controller, 'IP address updated to', controller.IP() )
return controller
def buildFromTopo( self, *args, **kwargs ):
+2 -2
View File
@@ -30,7 +30,7 @@ class ClusterCLI( CLI ):
global nx, plt, graphviz_layout
if not nx:
try:
# pylint: disable=import-error
# pylint: disable=import-error,no-member
import networkx
nx = networkx # satisfy pylint
from matplotlib import pyplot
@@ -42,7 +42,7 @@ class ClusterCLI( CLI ):
graphviz_layout = nx.graphviz_layout
else:
graphviz_layout = nx.drawing.nx_agraph.graphviz_layout
# pylint: enable=import-error
# pylint: enable=import-error,no-member
except ImportError:
error( 'plot requires networkx, matplotlib and pygraphviz - '
'please install them and try again\n' )
+1 -2
View File
@@ -100,10 +100,9 @@ class MininetFacade( object ):
class ControlNetwork( Topo ):
"Control Network Topology"
def __init__( self, n, dataController=DataController, **kwargs ):
def build( self, n, dataController=DataController, **_kwargs ):
"""n: number of data network controller nodes
dataController: class for data network controllers"""
Topo.__init__( self, **kwargs )
# Connect everything to a single switch
cs0 = self.addSwitch( 'cs0' )
# Add hosts which will serve as data network controllers
+1 -5
View File
@@ -38,11 +38,7 @@ flush = sys.stdout.flush
class LinearTestTopo( Topo ):
"Topology for a string of N hosts and N-1 switches."
def __init__( self, N, **params ):
# Initialize topology
Topo.__init__( self, **params )
def build( self, N, **params ):
# Create switches and hosts
hosts = [ self.addHost( 'h%s' % h )
for h in irange( 1, N ) ]
+2 -2
View File
@@ -24,6 +24,7 @@ import sys
from optparse import OptionParser
from subprocess import call
# pylint: disable=import-error
if sys.version_info[0] == 2:
from Tkinter import ( Frame, Label, LabelFrame, Entry, OptionMenu,
Checkbutton, Menu, Toplevel, Button, BitmapImage,
@@ -32,11 +33,9 @@ if sys.version_info[0] == 2:
CENTER, RIGHT, LEFT, BOTH, TRUE, FALSE )
from ttk import Notebook
from tkMessageBox import showerror
from subprocess import call
import tkFont
import tkFileDialog
import tkSimpleDialog
import tkFileDialog
else:
from tkinter import ( Frame, Label, LabelFrame, Entry, OptionMenu,
Checkbutton, Menu, Toplevel, Button, BitmapImage,
@@ -48,6 +47,7 @@ else:
from tkinter import font as tkFont
from tkinter import simpledialog as tkSimpleDialog
from tkinter import filedialog as tkFileDialog
# pylint: enable=import-error
import re
import json
+1 -3
View File
@@ -21,9 +21,7 @@ def runMultiLink():
class simpleMultiLinkTopo( Topo ):
"Simple topology with multiple links"
def __init__( self, n, **kwargs ):
Topo.__init__( self, **kwargs )
def build( self, n, **_kwargs ):
h1, h2 = self.addHost( 'h1' ), self.addHost( 'h2' )
s1 = self.addSwitch( 's1' )
+1 -3
View File
@@ -27,9 +27,7 @@ from mininet.util import irange
class InternetTopo(Topo):
"Single switch connected to n hosts."
def __init__(self, n=2, **opts):
Topo.__init__(self, **opts)
def build(self, n=2, **_kwargs ):
# set up inet switch
inetSwitch = self.addSwitch('s0')
# add inet host
+9
View File
@@ -29,6 +29,8 @@ from subprocess import call
from cmd import Cmd
from os import isatty
from select import poll, POLLIN
import select
import errno
import sys
import time
import os
@@ -459,6 +461,13 @@ class CLI( Cmd ):
# it's possible to interrupt ourselves after we've
# read data but before it has been printed.
node.sendInt()
except select.error as e:
# pylint: disable=unpacking-non-sequence
errno_, errmsg = e.args
# pylint: enable=unpacking-non-sequence
if errno_ != errno.EINTR:
error( "select.error: %d, %s" % (errno_, errmsg) )
node.sendInt()
def precmd( self, line ):
"allow for comments in the cli"
+20 -21
View File
@@ -146,6 +146,9 @@ class Intf( object ):
def rename( self, newname ):
"Rename interface"
if self.node and self.name in self.node.nameToIntf:
# rename intf in node's nameToIntf
self.node.nameToIntf[newname] = self.node.nameToIntf.pop(self.name)
self.ifconfig( 'down' )
result = self.cmd( 'ip link set', self.name, 'name', newname )
self.name = newname
@@ -404,7 +407,7 @@ class Link( object ):
def __init__( self, node1, node2, port1=None, port2=None,
intfName1=None, intfName2=None, addr1=None, addr2=None,
intf=Intf, cls1=None, cls2=None, params1=None,
params2=None, fast=True ):
params2=None, fast=True, **params ):
"""Create veth link to another node, making two new interfaces.
node1: first node
node2: second node
@@ -414,18 +417,15 @@ class Link( object ):
cls1, cls2: optional interface-specific constructors
intfName1: node1 interface name (optional)
intfName2: node2 interface name (optional)
params1: parameters for interface 1
params2: parameters for interface 2"""
params1: parameters for interface 1 (optional)
params2: parameters for interface 2 (optional)
**params: additional parameters for both interfaces"""
# This is a bit awkward; it seems that having everything in
# params is more orthogonal, but being able to specify
# in-line arguments is more convenient! So we support both.
if params1 is None:
params1 = {}
if params2 is None:
params2 = {}
# Allow passing in params1=params2
if params2 is params1:
params2 = dict( params1 )
params1 = dict( params1 ) if params1 else {}
params2 = dict( params2 ) if params2 else {}
if port1 is not None:
params1[ 'port' ] = port1
if port2 is not None:
@@ -439,6 +439,10 @@ class Link( object ):
if not intfName2:
intfName2 = self.intfName( node2, params2[ 'port' ] )
# Update with remaining parameter list
params1.update( params )
params2.update( params )
self.fast = fast
if fast:
params1.setdefault( 'moveIntfFn', self._ignore )
@@ -460,6 +464,7 @@ class Link( object ):
# All we are is dust in the wind, and our two interfaces
self.intf1, self.intf2 = intf1, intf2
# pylint: enable=too-many-branches
@staticmethod
@@ -545,17 +550,11 @@ class OVSLink( Link ):
class TCLink( Link ):
"Link with symmetric TC interfaces configured via opts"
def __init__( self, node1, node2, port1=None, port2=None,
intfName1=None, intfName2=None,
addr1=None, addr2=None, **params ):
Link.__init__( self, node1, node2, port1=port1, port2=port2,
intfName1=intfName1, intfName2=intfName2,
cls1=TCIntf,
cls2=TCIntf,
addr1=addr1, addr2=addr2,
params1=params,
params2=params )
"Link with TC interfaces"
def __init__( self, *args, **kwargs):
kwargs.setdefault( 'cls1', TCIntf )
kwargs.setdefault( 'cls2', TCIntf )
Link.__init__( self, *args, **kwargs)
class TCULink( TCLink ):
+1 -1
View File
@@ -108,7 +108,7 @@ from mininet.util import ( quietRun, fixLimits, numCores, ensureRoot,
from mininet.term import cleanUpScreens, makeTerms
# Mininet version: should be consistent with README and LICENSE
VERSION = "2.3.0d1"
VERSION = "2.3.0d4"
class Mininet( object ):
"Network emulation with hosts spawned in network namespaces."
+17 -12
View File
@@ -89,7 +89,7 @@ class Node( object ):
self.inNamespace = params.get( 'inNamespace', inNamespace )
# Python 3 complains if we don't wait for shell exit
self.waitExited = params.get( 'waitExited', Python3 == True )
self.waitExited = params.get( 'waitExited', Python3 )
# Stash configuration parameters for future reference
self.params = params
@@ -107,6 +107,7 @@ class Node( object ):
self.readbuf = ''
# Start command interpreter shell
self.master, self.slave = None, None # pylint
self.startShell()
self.mountPrivateDirs()
@@ -145,12 +146,12 @@ class Node( object ):
# Spawn a shell subprocess in a pseudo-tty, to disable buffering
# in the subprocess and insulate it from signals (e.g. SIGINT)
# received by the parent
master, slave = pty.openpty()
self.shell = self._popen( cmd, stdin=slave, stdout=slave, stderr=slave,
close_fds=False )
self.master, self.slave = pty.openpty()
self.shell = self._popen( cmd, stdin=self.slave, stdout=self.slave,
stderr=self.slave, close_fds=False )
# XXX BL: This doesn't seem right, and we should also probably
# close our files when we exit...
self.stdin = os.fdopen( master, 'r' )
self.stdin = os.fdopen( self.master, 'r' )
self.stdout = self.stdin
self.pid = self.shell.pid
self.pollOut = select.poll()
@@ -217,9 +218,13 @@ class Node( object ):
# for intfName in self.intfNames():
# if self.name in intfName:
# quietRun( 'ip link del ' + intfName )
if self.waitExited and self.shell:
debug( 'waiting for', self.pid, 'to terminate\n' )
self.shell.wait()
if self.shell:
# Close ptys
self.stdin.close()
os.close(self.slave)
if self.waitExited:
debug( 'waiting for', self.pid, 'to terminate\n' )
self.shell.wait()
self.shell = None
# Subshell I/O, commands and control
@@ -383,23 +388,23 @@ class Node( object ):
'mncmd':
[ 'mnexec', '-da', str( self.pid ) ] }
defaults.update( kwargs )
shell = defaults.pop( 'shell', False )
if len( args ) == 1:
if isinstance( args[ 0 ], list ):
# popen([cmd, arg1, arg2...])
cmd = args[ 0 ]
elif isinstance( args[ 0 ], BaseString ):
# popen("cmd arg1 arg2...")
cmd = args[ 0 ].split()
cmd = [ args[ 0 ] ] if shell else args[ 0 ].split()
else:
raise Exception( 'popen() requires a string or list' )
elif len( args ) > 0:
# popen( cmd, arg1, arg2... )
cmd = list( args )
if shell:
cmd = [ os.environ[ 'SHELL' ], '-c' ] + [ ' '.join( cmd ) ]
# Attach to our namespace using mnexec -a
cmd = defaults.pop( 'mncmd' ) + cmd
# Shell requires a string, not a list!
if defaults.get( 'shell', False ):
cmd = ' '.join( cmd )
popen = self._popen( cmd, **defaults )
return popen
+31
View File
@@ -0,0 +1,31 @@
#!/usr/bin/env python
"""
Regression test for pty leak in Node()
"""
import unittest
from mininet.net import Mininet
from mininet.clean import cleanup
from mininet.topo import SingleSwitchTopo
class TestPtyLeak( unittest.TestCase ):
"Verify that there is no pty leakage"
@staticmethod
def testPtyLeak():
"Test for pty leakage"
net = Mininet( SingleSwitchTopo() )
net.start()
host = net[ 'h1' ]
for _ in range( 0, 10 ):
oldptys = host.slave, host.master
net.delHost( host )
host = net.addHost( 'h1' )
assert ( host.slave, host.master ) == oldptys
net.stop()
if __name__ == '__main__':
unittest.main()
cleanup()
+1 -1
View File
@@ -228,7 +228,7 @@ class testWalkthrough( unittest.TestCase ):
r'([\d\.]+)/([\d\.]+)/([\d\.]+)/([\d\.]+) ms' )
delay = float( p.match.group( 2 ) )
self.assertTrue( delay >= 40, 'Delay < 40ms' )
self.assertTrue( delay <= 50, 'Delay > 50s' )
self.assertTrue( delay <= 50, 'Delay > 50ms' )
p.expect( self.prompt )
p.sendline( 'exit' )
p.wait()
+17 -21
View File
@@ -25,7 +25,10 @@ def encode( s ):
"Encode a byte string if needed for Python 3"
return s.encode( Encoding ) if Python3 else s
try:
# pylint: disable=import-error
oldpexpect = None
import pexpect as oldpexpect
# pylint: enable=import-error
class Pexpect( object ):
"Custom pexpect that is compatible with str"
@@ -63,7 +66,7 @@ def oldQuietRun( *cmd ):
cmd: list of command params"""
if len( cmd ) == 1:
cmd = cmd[ 0 ]
if isinstance( cmd, str ):
if isinstance( cmd, BaseString ):
cmd = cmd.split( ' ' )
popen = Popen( cmd, stdout=PIPE, stderr=STDOUT )
# We can't use Popen.communicate() because it uses
@@ -104,7 +107,7 @@ def errRun( *cmd, **kwargs ):
if len( cmd ) == 1:
cmd = cmd[ 0 ]
# Allow passing in a list or a string
if isinstance( cmd, str ) and not shell:
if isinstance( cmd, BaseString ) and not shell:
cmd = cmd.split( ' ' )
cmd = [ str( arg ) for arg in cmd ]
elif isinstance( cmd, list ) and shell:
@@ -413,34 +416,27 @@ def pmonitor(popens, timeoutms=500, readline=True,
for host, popen in popens.items():
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 )
def readit( f ):
"Helper function - read line or data"
# Note this will block if readline is True
line = f.readline() if readline else f.read( readmax )
return decode( line )
poller.register( fd, POLLIN | POLLHUP )
flags = fcntl( fd, F_GETFL )
fcntl( fd, F_SETFL, flags | O_NONBLOCK )
while popens:
fds = poller.poll( timeoutms )
if fds:
for fd, event in fds:
host = fdToHost[ fd ]
popen = popens[ host ]
if event & POLLIN:
line = readit( popen.stdout )
yield host, line
if event & POLLHUP:
if event & POLLIN or event & POLLHUP:
while True:
# Drain buffer
line = readit( popen.stdout )
yield host, line
try:
f = popen.stdout
line = decode( f.readline() if readline
else f.read( readmax ) )
except IOError:
line = ''
if line == '':
break
yield host, line
if event & POLLHUP:
poller.unregister( fd )
del popens[ host ]
else:
+2 -1
View File
@@ -159,9 +159,10 @@ function mn_deps {
ethtool help2man python-pyflakes python3-pylint \
python-pep8 ${PYPKG}-pexpect ${PYPKG}-tk
else # Debian/Ubuntu
$install gcc make socat psmisc xterm ssh iperf iproute2 telnet \
$install gcc make socat psmisc xterm ssh iperf telnet \
cgroup-bin ethtool help2man pyflakes pylint pep8 \
${PYPKG}-setuptools ${PYPKG}-pexpect ${PYPKG}-tk
$install iproute2 || $install iproute
fi
echo "Installing Mininet core"