Compare commits
21 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 598d694058 | |||
| f92e3d3432 | |||
| ce4f1e0c92 | |||
| 31f44e1856 | |||
| 36d2b21187 | |||
| 1888001555 | |||
| fdc3156a91 | |||
| cf6da391fa | |||
| f170cc6f64 | |||
| fd96de6485 | |||
| 73f530b569 | |||
| c7a27b8876 | |||
| b7a6b8137f | |||
| d072e531c2 | |||
| 8139d7d1b4 | |||
| 273b14b771 | |||
| bfda33544a | |||
| 1f4525ed0a | |||
| 1969669f51 | |||
| c639f342e5 | |||
| 6e887d0788 |
@@ -2,7 +2,7 @@
|
||||
Mininet Installation/Configuration Notes
|
||||
----------------------------------------
|
||||
|
||||
Mininet 2.3.0d3
|
||||
Mininet 2.3.0d5
|
||||
---
|
||||
|
||||
The supported installation methods for Mininet are 1) using a
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
Mininet 2.3.0d3 License
|
||||
Mininet 2.3.0d5 License
|
||||
|
||||
Copyright (c) 2013-2018 Open Networking Laboratory
|
||||
Copyright (c) 2013-2019 Open Networking Laboratory
|
||||
Copyright (c) 2009-2012 Bob Lantz and The Board of Trustees of
|
||||
The Leland Stanford Junior University
|
||||
|
||||
|
||||
@@ -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.0d3
|
||||
Mininet 2.3.0d5
|
||||
|
||||
[![Build Status][1]](https://travis-ci.org/mininet/mininet)
|
||||
|
||||
|
||||
@@ -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 ) ]
|
||||
|
||||
@@ -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.0d3"
|
||||
VERSION = "2.3.0d5"
|
||||
|
||||
class Mininet( object ):
|
||||
"Network emulation with hosts spawned in network namespaces."
|
||||
|
||||
+12
-7
@@ -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
|
||||
|
||||
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()
|
||||
+2
-2
@@ -66,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
|
||||
@@ -107,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:
|
||||
|
||||
+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