Compare commits
33 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 31f44e1856 | |||
| 36d2b21187 | |||
| 1888001555 | |||
| fdc3156a91 | |||
| cf6da391fa | |||
| f170cc6f64 | |||
| fd96de6485 | |||
| 73f530b569 | |||
| c7a27b8876 | |||
| b7a6b8137f | |||
| d072e531c2 | |||
| 8139d7d1b4 | |||
| 273b14b771 | |||
| bfda33544a | |||
| 1f4525ed0a | |||
| 1969669f51 | |||
| c639f342e5 | |||
| 6e887d0788 | |||
| 092863f113 | |||
| 5100a38fcd | |||
| 3bcecd8e08 | |||
| 486676d617 | |||
| 9e5280b4d1 | |||
| b70eed69d3 | |||
| d96b35694a | |||
| c7deeae11c | |||
| 323989953b | |||
| 17f9756e5c | |||
| 71f3931f2f | |||
| 1e9ca5f7fc | |||
| 8933c996cb | |||
| afa83cd5ae | |||
| 3e81ea7455 |
+15
-15
@@ -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
|
||||
|
||||
@@ -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,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,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
|
||||
|
||||
@@ -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
@@ -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 ):
|
||||
|
||||
@@ -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' )
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 ) ]
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
@@ -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
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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
|
||||
|
||||
|
||||
Executable
+31
@@ -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()
|
||||
@@ -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
@@ -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
@@ -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"
|
||||
|
||||
Reference in New Issue
Block a user