Compare commits

..

21 Commits

Author SHA1 Message Date
Bob Lantz 598d694058 2.3.0d5 2019-03-13 21:56:32 -07:00
Bob Lantz f92e3d3432 2018 -> 2019 2019-03-13 21:52:13 -07:00
lantz ce4f1e0c92 Merge pull request #863 from mininet/devel/fix-tclink
Change Link/TCLink to accept universal parameters
2019-03-06 16:17:31 -08:00
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
17 changed files with 108 additions and 60 deletions
+1 -1
View File
@@ -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
+2 -2
View File
@@ -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
+1 -1
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.0d3
Mininet 2.3.0d5
[![Build Status][1]](https://travis-ci.org/mininet/mininet)
+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 ) ]
+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.0d3"
VERSION = "2.3.0d5"
class Mininet( object ):
"Network emulation with hosts spawned in network namespaces."
+12 -7
View File
@@ -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
+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()
+2 -2
View File
@@ -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
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"