Compare commits

..

11 Commits

Author SHA1 Message Date
lantz fc3152d724 Update INSTALL 2021-02-09 00:47:32 -08:00
lantz 9a3a3edf75 2.3.0rc1 (#1037) 2021-02-09 00:44:22 -08:00
lantz 377a4b5af5 Add net-tools dependency for fedora (#1039) 2021-02-09 00:17:34 -08:00
lantz 4a1dbac09b Basic fixes for install.sh -fnv for Fedora 33 (#1038)
Closes #1003
2021-02-08 23:33:40 -08:00
lantz 92fe3cc120 Make mn -w wait indefinitely and add --twait option (#1036)
Background: In Mininet 2.2, waitConnected() waits forever
by default. We are going to preserve this behavior for 2.3.

Therefore, the --wait/-w option will wait forever. This is used
in the tests to make sure that all switches have connected to
their controllers.

A new --twait/-t <int> option has been added for timed waits.

The API for Mininet(....waitConnected=False/True) is preserved,
but you can now pass in an integer wait time. False means
do not wait at all. True means wait forever. I have elected
for now to preserve None also meaning wait forever as it was
in 2.2, but note that you should probably use the boolean
True/False instead.
2021-02-08 23:22:36 -08:00
lantz 39ed456de2 Fixes for cluster.py (#1035)
- use decode() for python 3 compatibility
- try to identify non-loopback interface for controllers
-- avoid hardwired 'eth0'
- use remoteServer consistently in tests
- pass tests on ubuntu 20/python 3
- pass hybrid test with hybrid python 2 and python 3 mininet
2021-02-08 22:28:12 -08:00
lantz 5d4ec1ab9e catch IOError when writing .mininet_history (#1031)
write_history seems to raise a spurious IOError,
so we catch it.
2021-02-07 19:12:38 -08:00
lantz 6b90434b9c Allow reinstall with make install (#1033) 2021-02-07 18:42:29 -08:00
lantz c2fb4d2e8c Fix ryu (#1032)
* Force reinstall on 'make install'

This allows you to install a modified version with 'make install'.

* Simplify RyuController and update for current Ryu

Notes:

The Ryu() constructor has changed slightly. We still add
`--ofp-tcp-listen-port %s` to the end of `cargs` to make
Controller() happy.

`command` is now `ryu run` so it includes the `run` command
but can be specified explicitly as needed.

You should be able to run Ryu's simple_switch by using:

    mn --controller ryu

and pass alternate modules such as simple_switch_13:

    mn --controller ryu,ryu.app.simple_switch_13

Unfortunately simple_switch_stp seems like it may be broken
for Python 3.8, which is too bad because we'd like to test
the Torus topo with it.
2021-02-07 16:08:45 -08:00
lantz 7b240ce5b8 Minor fixes for miniedit (#1028)
- avoid deleting from collection we're iterating over
- fix variable name misspelling
2021-02-06 23:23:09 -08:00
lantz c7de350a4e update make doc for current doxygen/doxypy (#1027) 2021-02-06 19:42:15 -08:00
12 changed files with 2047 additions and 912 deletions
+6 -17
View File
@@ -2,7 +2,7 @@
Mininet Installation/Configuration Notes
----------------------------------------
Mininet 2.3.0b2
Mininet 2.3.0rc1
---
The supported installation methods for Mininet are 1) using a
@@ -137,9 +137,7 @@ like to contribute an installation script, we would welcome it!)
This takes about 4 minutes on our test system.
3.2. Native installation from source on Fedora 18+.
*This may be out of date.*
3.2. (Experimental) Native installation from source on Fedora:
As root execute the following operations:
@@ -147,18 +145,6 @@ like to contribute an installation script, we would welcome it!)
yum install git
* create an user account (e.g. mininet) and add it to the wheel group
useradd [...] mininet
usermod -a -G wheel mininet
* change the SElinux setting to permissive. It can be done
temporarily with:
setenforce 0
then login with the new account (e.g. mininet) and do the following:
* clone the Mininet repository
git clone git://github.com/mininet/mininet.git
@@ -177,7 +163,10 @@ like to contribute an installation script, we would welcome it!)
sudo mn --test pingall
4. Creating your own Mininet/OpenFlow tutorial VM
Note that `install.sh -fnv `may not install all dependencies on Fedora,
and many tests may still fail.
4. Creating your own Mininet/OpenFlow tutorial VM on Ubuntu/Debian
Creating your own Ubuntu Mininet VM for use with the OpenFlow tutorial
is easy! First, create a new Ubuntu VM. Next, run two commands in it:
+1 -1
View File
@@ -1,4 +1,4 @@
Mininet 2.3.0b2 License
Mininet 2.3.0rc1 License
Copyright (c) 2013-2020 Open Networking Foundation
Copyright (c) 2009-2012 Bob Lantz and The Board of Trustees of
+2
View File
@@ -56,6 +56,8 @@ install-manpages: $(MANPAGES)
install -D -t $(MANDIR) $(MANPAGES)
install: install-mnexec install-manpages
# This seems to work on all pip versions
$(PYTHON) -m pip uninstall mininet || true
$(PYTHON) -m pip install .
develop: $(MNEXEC) $(MANPAGES)
+6 -6
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.0b2
Mininet 2.3.0rc1
[![Build Status][1]](https://github.com/mininet/mininet/actions)
@@ -70,7 +70,7 @@ Mininet includes:
### Python 3 Support
- Mininet 2.3.0b2 supports Python 3 and Python 2!
- Mininet 2.3.0rc1 supports Python 3 and Python 2!
- You can install both the Python 3 and Python 2 versions of
Mininet side by side, but the most recent installation will
@@ -84,7 +84,7 @@ determine which Python version is used by default by `mn`.
- More information regarding Python 3 and Python 2 support
may be found in the release notes on http://docs.mininet.org.
### Other Enhancements and Information
- Support for Ubuntu 20.04 LTS (and 18.04 and 16.04)
@@ -117,7 +117,7 @@ Mininet mailing list, `mininet-discuss` at:
### Join Us
Thanks again to all of the Mininet contributors!
Thanks again to all of the Mininet contributors and users!
Mininet is an open source project and is currently hosted
at <https://github.com/mininet>. You are encouraged to download
@@ -129,10 +129,10 @@ hard work that Mininet continues to grow and improve.
### Enjoy Mininet
Have fun! We look forward to seeing what you will do with Mininet
Have fun! We look forward to seeing what you will do with Mininet
to change the networking world.
Bob Lantz
Bob Lantz,
on behalf of the Mininet Contributors
[1]: https://github.com/mininet/mininet/workflows/mininet-tests/badge.svg
+3
View File
@@ -290,6 +290,9 @@ class MininetRunner( object ):
help='prints the version and exits' )
opts.add_option( '--wait', '-w', action='store_true',
default=False, help='wait for switches to connect' )
opts.add_option( '--twait', '-t', action='store', type='int',
dest='wait',
help='timed wait (s) for switches to connect' )
opts.add_option( '--cluster', type='string', default=None,
metavar='server1,server2...',
help=( 'run on multiple servers (experimental!)' ) )
+1927 -821
View File
File diff suppressed because it is too large Load Diff
+57 -31
View File
@@ -89,7 +89,7 @@ from mininet.link import Link, Intf
from mininet.net import Mininet
from mininet.topo import LinearTopo
from mininet.topolib import TreeTopo
from mininet.util import quietRun, errRun
from mininet.util import quietRun, errRun, decode
from mininet.examples.clustercli import CLI
from mininet.log import setLogLevel, debug, info, error
from mininet.clean import addCleanupCallback
@@ -247,7 +247,7 @@ class RemoteMixin( object ):
result = ''
while True:
poll = popen.poll()
result += popen.stdout.read()
result += decode( popen.stdout.read() )
if poll is not None:
break
return result
@@ -440,13 +440,16 @@ class RemoteLink( Link ):
# When we receive the character '@', it means that our
# tunnel should be set up
debug( 'Waiting for tunnel to come up...\n' )
ch = tunnel.stdout.read( 1 )
ch = decode( tunnel.stdout.read( 1 ) )
if ch != '@':
raise Exception( 'makeTunnel:\n',
'Tunnel setup failed for',
'%s:%s' % ( node1, node1.dest ), 'to',
'%s:%s\n' % ( node2, node2.dest ),
'command was:', cmd, '\n' )
ch += decode( tunnel.stdout.read() )
cmd = ' '.join( cmd )
raise Exception( 'makeTunnel:\n'
'Tunnel setup failed for '
'%s:%s' % ( node1, node1.dest ) + ' to '
'%s:%s\n' % ( node2, node2.dest ) +
'command was: %s' % cmd + '\n' +
'result was: ' + ch )
# 3. Move interfaces if necessary
for node in node1, node2:
if not self.moveIntf( 'tap9', node ):
@@ -848,27 +851,37 @@ class MininetCluster( Mininet ):
if cfile:
config.setdefault( 'controlPath', cfile )
@staticmethod
def isLoopback( ipaddr ):
"Is ipaddr an IPv4 loopback address?"
return ipaddr.startswith( '127.' )
# pylint: disable=arguments-differ,signature-differs
def addController( self, *args, **kwargs ):
"Patch to update IP address to global IP address"
controller = Mininet.addController( self, *args, **kwargs )
loopback = '127.0.0.1'
controllerIP = controller.IP()
if ( not isinstance( controller, Controller ) or
controller.IP() != loopback ):
return None
not self.isLoopback( controller.IP() ) ):
return controller
# Find route to a different server IP address
serverIPs = [ ip for ip in self.serverIP.values()
if ip is not controller.IP() ]
if ip != controllerIP ]
if not serverIPs:
return None # 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()
for remoteIP in serverIPs:
# 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()
if intf != 'lo':
break
if intf == 'lo':
raise Exception( 'addController: could not find external '
'interface/IP for %s' % controller )
debug( 'adding', intf, 'to', controller )
Intf( intf, node=controller ).updateIP()
debug( controller, 'IP address updated to', controller.IP() )
@@ -883,7 +896,10 @@ class MininetCluster( Mininet ):
Mininet.buildFromTopo( self, *args, **kwargs )
def testNsTunnels( remote='ubuntu2', link=RemoteGRELink ):
# Default remote server for tests
remoteServer = 'ubuntu2'
def testNsTunnels( remote=remoteServer, link=RemoteGRELink ):
"Test tunnels between nodes in namespaces"
net = Mininet( host=RemoteHost, link=link, waitConnected=True )
h1 = net.addHost( 'h1')
@@ -898,14 +914,14 @@ def testNsTunnels( remote='ubuntu2', link=RemoteGRELink ):
# This shows how node options may be used to manage
# cluster placement using the net.add*() API
def testRemoteNet( remote='ubuntu2', link=RemoteGRELink ):
def testRemoteNet( remote=remoteServer, link=RemoteGRELink ):
"Test remote Node classes"
info( '*** Remote Node Test\n' )
net = Mininet( host=RemoteHost, switch=RemoteOVSSwitch, link=link,
net = Mininet( host=RemoteHost, switch=RemoteOVSSwitch,
link=link, controller=ClusterController,
waitConnected=True )
c0 = net.addController( 'c0' )
# Make sure controller knows its non-loopback address
Intf( 'eth0', node=c0 ).updateIP()
info( "*** Creating local h1\n" )
h1 = net.addHost( 'h1' )
info( "*** Creating remote h2\n" )
@@ -937,7 +953,7 @@ def testRemoteNet( remote='ubuntu2', link=RemoteGRELink ):
remoteHosts = [ 'h2' ]
remoteSwitches = [ 's2' ]
remoteServer = 'ubuntu2'
def HostPlacer( name, *args, **params ):
"Custom Host() constructor which places hosts on servers"
@@ -954,10 +970,21 @@ def SwitchPlacer( name, *args, **params ):
return RemoteOVSSwitch( name, *args, **params )
def ClusterController( *args, **kwargs):
"Custom Controller() constructor which updates its eth0 IP address"
"Custom Controller() constructor which updates its intf IP address"
intf = kwargs.pop( 'intf', '' )
controller = Controller( *args, **kwargs )
# Find out its IP address so that cluster switches can connect
Intf( 'eth0', node=controller ).updateIP()
if not intf:
output = controller.cmd(
r"ip a | egrep -o '\w+:\s\w+'" ).split( '\n' )
for line in output:
intf = line.split()[ -1 ]
if intf != 'lo':
break
if intf == 'lo':
raise Exception( 'Could not find non-loopback interface'
'for %s' % controller )
Intf( intf, node=controller ).updateIP()
return controller
def testRemoteTopo( link=RemoteGRELink ):
@@ -974,7 +1001,7 @@ def testRemoteTopo( link=RemoteGRELink ):
# do random switch placement rather than completely random
# host placement.
def testRemoteSwitches( remote='ubuntu2', link=RemoteGRELink ):
def testRemoteSwitches( remote=remoteServer, link=RemoteGRELink ):
"Test with local hosts and remote switches"
servers = [ 'localhost', remote]
topo = TreeTopo( depth=4, fanout=2 )
@@ -992,7 +1019,7 @@ def testRemoteSwitches( remote='ubuntu2', link=RemoteGRELink ):
# functions, for maximum ease of use. MininetCluster() also
# pre-flights and multiplexes server connections.
def testMininetCluster( remote='ubuntu2', link=RemoteGRELink ):
def testMininetCluster( remote=remoteServer, link=RemoteGRELink ):
"Test MininetCluster()"
servers = [ 'localhost', remote ]
topo = TreeTopo( depth=3, fanout=3 )
@@ -1002,7 +1029,7 @@ def testMininetCluster( remote='ubuntu2', link=RemoteGRELink ):
net.pingAll()
net.stop()
def signalTest( remote='ubuntu2'):
def signalTest( remote=remoteServer):
"Make sure hosts are robust to signals"
h = RemoteHost( 'h0', server=remote )
h.shell.send_signal( SIGINT )
@@ -1017,7 +1044,6 @@ def signalTest( remote='ubuntu2'):
if __name__ == '__main__':
setLogLevel( 'info' )
remoteServer = 'ubuntu2'
remoteLink = RemoteSSHLink
testRemoteTopo(link=remoteLink)
testNsTunnels( remote=remoteServer, link=remoteLink )
+4 -5
View File
@@ -2703,14 +2703,13 @@ class MiniEdit( Frame ):
tags = self.canvas.gettags(item)
if 'Controller' in tags:
# remove from switch controller lists
for serachwidget in self.widgetToItem:
name = serachwidget[ 'text' ]
tags = self.canvas.gettags( self.widgetToItem[ serachwidget ] )
for searchwidget in self.widgetToItem:
name = searchwidget[ 'text' ]
tags = self.canvas.gettags( self.widgetToItem[ searchwidget ] )
if 'Switch' in tags:
if widget['text'] in self.switchOpts[name]['controllers']:
self.switchOpts[name]['controllers'].remove(widget['text'])
for link in widget.links.values():
for link in tuple( widget.links.values() ):
# Delete from view and model
self.deleteItem( link )
del self.itemToWidget[ item ]
+9 -1
View File
@@ -89,7 +89,15 @@ class CLI( Cmd ):
if os.path.isfile( history_path ):
read_history_file( history_path )
set_history_length( 1000 )
atexit.register( lambda: write_history_file( history_path ) )
def writeHistory():
"Write out history file"
try:
write_history_file( history_path )
except IOError:
# Ignore probably spurious IOError
pass
atexit.register( writeHistory )
def run( self ):
"Run our cmdloop(), catching KeyboardInterrupt"
+9 -7
View File
@@ -109,7 +109,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.0b2"
VERSION = "2.3.0rc1"
class Mininet( object ):
"Network emulation with hosts spawned in network namespaces."
@@ -137,7 +137,9 @@ class Mininet( object ):
autoStaticArp: set all-pairs static MAC addrs?
autoPinCpus: pin hosts to (real) cores (requires CPULimitedHost)?
listenPort: base listening port to open; will be incremented for
each additional switch in the net if inNamespace=False"""
each additional switch in the net if inNamespace=False
waitConnected: wait for switches to Connect?
(False; True/None=wait indefinitely; time(s)=timed wait)"""
self.topo = topo
self.switch = switch
self.host = host
@@ -175,10 +177,9 @@ class Mininet( object ):
if topo and build:
self.build()
def waitConnected( self, timeout=5, delay=.5 ):
"""wait for each switch to connect to a controller,
up to 5 seconds
timeout: time to wait, or None to wait indefinitely
def waitConnected( self, timeout=None, delay=.5 ):
"""wait for each switch to connect to a controller
timeout: time to wait, or None or True to wait indefinitely
delay: seconds to sleep per iteration
returns: True if all switches are connected"""
info( '*** Waiting for switches to connect\n' )
@@ -192,7 +193,8 @@ class Mininet( object ):
if not remaining:
info( '\n' )
return True
if timeout is not None and time > timeout:
# Still allow None to preserve 2.2 behavior
if timeout not in ( None, True ) and time > timeout:
break
sleep( delay )
time += delay
+14 -20
View File
@@ -1505,32 +1505,26 @@ class NOX( Controller ):
Controller.__init__( self, name,
command=noxCoreDir + '/nox_core',
cargs='--libdir=/usr/local/lib -v -i ptcp:%s ' +
cargs='--libdir=/usr/local/lib -v '
'-i ptcp:%s ' +
' '.join( noxArgs ),
cdir=noxCoreDir,
**kwargs )
class Ryu( Controller ):
"Controller to run Ryu application"
def __init__( self, name, *ryuArgs, **kwargs ):
"Ryu OpenFlow Controller"
def __init__( self, name, ryuArgs='ryu.app.simple_switch',
command='ryu run', **kwargs ):
"""Init.
name: name to give controller.
ryuArgs: arguments and modules to pass to Ryu"""
homeDir = quietRun( 'printenv HOME' ).strip( '\r\n' )
ryuCoreDir = '%s/ryu/ryu/app/' % homeDir
if not ryuArgs:
warn( 'warning: no Ryu modules specified; '
'running simple_switch only\n' )
ryuArgs = [ ryuCoreDir + 'simple_switch.py' ]
elif not isinstance( ryuArgs, ( list, tuple ) ):
ryuArgs = [ ryuArgs ]
Controller.__init__( self, name,
command='ryu-manager',
cargs='--ofp-tcp-listen-port %s ' +
' '.join( ryuArgs ),
cdir=ryuCoreDir,
**kwargs )
name: name to give controller.
ryuArgs: modules to pass to Ryu (ryu.app.simple_switch)
command: comand to run Ryu ('ryu run')"""
if isinstance( ryuArgs, ( list, tuple ) ):
ryuArgs = ' '.join( ryuArgs )
cargs = kwargs.pop(
'cargs', ryuArgs + ' --ofp-tcp-listen-port %s' )
Controller.__init__( self, name, command=command,
cargs=cargs, **kwargs )
class RemoteController( Controller ):
+9 -3
View File
@@ -164,7 +164,9 @@ function mn_deps {
if [ "$DIST" = "Fedora" -o "$DIST" = "RedHatEnterpriseServer" ]; then
$install gcc make socat psmisc xterm openssh-clients iperf \
iproute telnet python-setuptools libcgroup-tools \
ethtool help2man pyflakes pylint python-pep8 python-pexpect
ethtool help2man net-tools
$install ${PYPKG}-pyflakes pylint ${PYPKG}-pep8-naming \
${PYPKG}-pexpect
elif [ "$DIST" = "SUSE LINUX" ]; then
$install gcc make socat psmisc xterm openssh iperf \
iproute telnet ${PYPKG}-setuptools libcgroup-tools \
@@ -205,10 +207,11 @@ function mn_deps {
# Install Mininet documentation dependencies
function mn_doc {
echo "Installing Mininet documentation dependencies"
$install doxygen doxypy texlive-fonts-recommended
$install doxygen texlive-fonts-recommended
if ! $install doxygen-latex; then
echo "doxygen-latex not needed"
fi
sudo pip install doxypy
}
# The following will cause a full OF install, covering:
@@ -407,7 +410,10 @@ function ovs {
echo "Installing Open vSwitch..."
if [ "$DIST" = "Fedora" -o "$DIST" = "RedHatEnterpriseServer" ]; then
$install openvswitch openvswitch-controller
$install openvswitch
if ! $install openvswitch-controller; then
echo "openvswitch-controller not installed"
fi
return
fi