Compare commits
39 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 547b91cb11 | |||
| 55f4eb7e94 | |||
| 4e507fb01f | |||
| 8102704726 | |||
| f4490069ca | |||
| 2ac4f92af3 | |||
| f98154a323 | |||
| 7b48460e9e | |||
| 3a0ef258d1 | |||
| 0d9a6796df | |||
| 08a59783f4 | |||
| e37afe996b | |||
| 6a387faa04 | |||
| 1a134cb4d2 | |||
| af4921adc5 | |||
| f94ee8ec97 | |||
| 356b024d6b | |||
| 2283bb01e6 | |||
| 3eef584c6b | |||
| 2e00a7de97 | |||
| e28348f6cd | |||
| 4b744d1fb0 | |||
| 2822998cad | |||
| 853815500d | |||
| 1a2925923f | |||
| cac884a85e | |||
| f314a6626a | |||
| bf83f4f343 | |||
| 1ef12e450a | |||
| 56fc6c3955 | |||
| 88cbf4ec42 | |||
| 26b3959968 | |||
| 966dd20b4b | |||
| 82998cac8b | |||
| 34b1f4161a | |||
| dfb297901f | |||
| 67236e9db3 | |||
| 8a00c3abf8 | |||
| 037f7f5921 |
@@ -1,6 +1,10 @@
|
||||
language: python
|
||||
sudo: required
|
||||
|
||||
python:
|
||||
- "2.7
|
||||
- "3.6"
|
||||
|
||||
matrix:
|
||||
include:
|
||||
- dist: trusty
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
Mininet 2.3.0d1 License
|
||||
|
||||
Copyright (c) 2013-2016 Open Networking Laboratory
|
||||
Copyright (c) 2013-2018 Open Networking Laboratory
|
||||
Copyright (c) 2009-2012 Bob Lantz and The Board of Trustees of
|
||||
The Leland Stanford Junior University
|
||||
|
||||
|
||||
@@ -2,14 +2,16 @@ MININET = mininet/*.py
|
||||
TEST = mininet/test/*.py
|
||||
EXAMPLES = mininet/examples/*.py
|
||||
MN = bin/mn
|
||||
PYMN = python -B bin/mn
|
||||
PYTHON ?= python
|
||||
PYMN = $(PYTHON) -B bin/mn
|
||||
BIN = $(MN)
|
||||
PYSRC = $(MININET) $(TEST) $(EXAMPLES) $(BIN)
|
||||
MNEXEC = mnexec
|
||||
MANPAGES = mn.1 mnexec.1
|
||||
P8IGN = E251,E201,E302,E202,E126,E127,E203,E226
|
||||
BINDIR = /usr/bin
|
||||
MANDIR = /usr/share/man/man1
|
||||
PREFIX ?= /usr
|
||||
BINDIR ?= $(PREFIX)/bin
|
||||
MANDIR ?= $(PREFIX)/share/man/man1
|
||||
DOCDIRS = doc/html doc/latex
|
||||
PDF = doc/latex/refman.pdf
|
||||
|
||||
@@ -46,16 +48,20 @@ slowtest: $(MININET)
|
||||
mnexec: mnexec.c $(MN) mininet/net.py
|
||||
cc $(CFLAGS) $(LDFLAGS) -DVERSION=\"`PYTHONPATH=. $(PYMN) --version`\" $< -o $@
|
||||
|
||||
install: $(MNEXEC) $(MANPAGES)
|
||||
install $(MNEXEC) $(BINDIR)
|
||||
install $(MANPAGES) $(MANDIR)
|
||||
python setup.py install
|
||||
install-mnexec: $(MNEXEC)
|
||||
install -D $(MNEXEC) $(BINDIR)/$(MNEXEC)
|
||||
|
||||
install-manpages: $(MANPAGES)
|
||||
install -D -t $(MANDIR) $(MANPAGES)
|
||||
|
||||
install: install-mnexec install-manpages
|
||||
$(PYTHON) setup.py install
|
||||
|
||||
develop: $(MNEXEC) $(MANPAGES)
|
||||
# Perhaps we should link these as well
|
||||
install $(MNEXEC) $(BINDIR)
|
||||
install $(MANPAGES) $(MANDIR)
|
||||
python setup.py develop
|
||||
$(PYTHON) setup.py develop
|
||||
|
||||
man: $(MANPAGES)
|
||||
|
||||
|
||||
@@ -29,11 +29,11 @@ from mininet.node import ( Host, CPULimitedHost, Controller, OVSController,
|
||||
DefaultController, NullController,
|
||||
UserSwitch, OVSSwitch, OVSBridge,
|
||||
IVSSwitch )
|
||||
from mininet.nodelib import LinuxBridge, Server
|
||||
from mininet.nodelib import LinuxBridge
|
||||
from mininet.link import Link, TCLink, TCULink, OVSLink
|
||||
from mininet.topo import ( SingleSwitchTopo, LinearTopo,
|
||||
SingleSwitchReversedTopo, MinimalTopo )
|
||||
import mininet.topolib
|
||||
from mininet.topolib import TreeTopo, TorusTopo
|
||||
from mininet.util import customClass, specialClass, splitArgs
|
||||
from mininet.util import buildTopo
|
||||
|
||||
@@ -53,8 +53,9 @@ TOPODEF = 'minimal'
|
||||
TOPOS = { 'minimal': MinimalTopo,
|
||||
'linear': LinearTopo,
|
||||
'reversed': SingleSwitchReversedTopo,
|
||||
'single': SingleSwitchTopo }
|
||||
TOPOS.update( mininet.topolib.topos)
|
||||
'single': SingleSwitchTopo,
|
||||
'tree': TreeTopo,
|
||||
'torus': TorusTopo }
|
||||
|
||||
SWITCHDEF = 'default'
|
||||
SWITCHES = { 'user': UserSwitch,
|
||||
@@ -68,7 +69,6 @@ SWITCHES = { 'user': UserSwitch,
|
||||
|
||||
HOSTDEF = 'proc'
|
||||
HOSTS = { 'proc': Host,
|
||||
'server': Server,
|
||||
'rt': specialClass( CPULimitedHost, defaults=dict( sched='rt' ) ),
|
||||
'cfs': specialClass( CPULimitedHost, defaults=dict( sched='cfs' ) ) }
|
||||
|
||||
@@ -186,8 +186,10 @@ class MininetRunner( object ):
|
||||
for fileName in files:
|
||||
customs = {}
|
||||
if os.path.isfile( fileName ):
|
||||
execfile( fileName, customs, customs )
|
||||
for name, val in customs.iteritems():
|
||||
# pylint: disable=exec-used
|
||||
exec( compile( open( fileName ).read(), fileName, 'exec' ),
|
||||
customs, customs )
|
||||
for name, val in customs.items():
|
||||
self.setCustom( name, val )
|
||||
else:
|
||||
raise Exception( 'could not find custom file: %s' % fileName )
|
||||
@@ -256,7 +258,7 @@ class MininetRunner( object ):
|
||||
opts.add_option( '--arp', action='store_true',
|
||||
default=False, help='set all-pairs ARP entries' )
|
||||
opts.add_option( '--verbosity', '-v', type='choice',
|
||||
choices=LEVELS.keys(), default = 'info',
|
||||
choices=list( LEVELS.keys() ), default = 'info',
|
||||
help = '|'.join( LEVELS.keys() ) )
|
||||
opts.add_option( '--innamespace', action='store_true',
|
||||
default=False, help='sw and ctrl in namespace?' )
|
||||
@@ -286,7 +288,7 @@ class MininetRunner( object ):
|
||||
metavar='server1,server2...',
|
||||
help=( 'run on multiple servers (experimental!)' ) )
|
||||
opts.add_option( '--placement', type='choice',
|
||||
choices=PLACEMENT.keys(), default='block',
|
||||
choices=list( PLACEMENT.keys() ), default='block',
|
||||
metavar='block|random',
|
||||
help=( 'node placement for --cluster '
|
||||
'(experimental!) ' ) )
|
||||
|
||||
+8
-12
@@ -126,7 +126,7 @@ class ClusterCleanup( object ):
|
||||
def cleanup( cls ):
|
||||
"Clean up"
|
||||
info( '*** Cleaning up cluster\n' )
|
||||
for server, user in cls.serveruser.iteritems():
|
||||
for server, user in cls.serveruser.items():
|
||||
if server == 'localhost':
|
||||
# Handled by mininet.clean.cleanup()
|
||||
continue
|
||||
@@ -157,7 +157,7 @@ class RemoteMixin( object ):
|
||||
'-o', 'ForwardAgent=yes', '-tt' ]
|
||||
|
||||
def __init__( self, name, server='localhost', user=None, serverIP=None,
|
||||
controlPath=True, splitInit=False, **kwargs):
|
||||
controlPath=False, splitInit=False, **kwargs):
|
||||
"""Instantiate a remote node
|
||||
name: name of remote node
|
||||
server: remote server (optional)
|
||||
@@ -213,7 +213,7 @@ class RemoteMixin( object ):
|
||||
def startShell( self, *args, **kwargs ):
|
||||
"Start a shell process for running commands"
|
||||
if self.isRemote:
|
||||
kwargs.update( mnopts='-cp' )
|
||||
kwargs.update( mnopts='-c' )
|
||||
super( RemoteMixin, self ).startShell( *args, **kwargs )
|
||||
# Optional split initialization
|
||||
self.sendCmd( 'echo $$' )
|
||||
@@ -386,8 +386,7 @@ class RemoteLink( Link ):
|
||||
if server1 == server2:
|
||||
# Link within same server
|
||||
return Link.makeIntfPair( intfname1, intfname2, addr1, addr2,
|
||||
node1, node2, deleteIntfs=deleteIntfs,
|
||||
runCmd=None )
|
||||
node1, node2, deleteIntfs=deleteIntfs )
|
||||
# Otherwise, make a tunnel
|
||||
self.tunnel = self.makeTunnel( node1, node2, intfname1, intfname2,
|
||||
addr1, addr2 )
|
||||
@@ -845,13 +844,10 @@ class MininetCluster( Mininet ):
|
||||
"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' ):
|
||||
links = controller.cmd( 'ip link show' )
|
||||
eth0 = re.findall( ' (.*eth0):', links )
|
||||
if not eth0:
|
||||
raise Exception( 'Cannot find IP address for controller eth0' )
|
||||
Intf( eth0[ 0 ], node=controller ).updateIP()
|
||||
if ( isinstance( controller, Controller)
|
||||
and controller.IP() == '127.0.0.1'
|
||||
and ' eth0:' in controller.cmd( 'ip link show' ) ):
|
||||
Intf( 'eth0', node=controller ).updateIP()
|
||||
return controller
|
||||
|
||||
def buildFromTopo( self, *args, **kwargs ):
|
||||
|
||||
@@ -49,7 +49,7 @@ class MininetFacade( object ):
|
||||
args: unnamed networks passed as arguments
|
||||
kwargs: named networks passed as arguments"""
|
||||
self.net = net
|
||||
self.nets = [ net ] + list( args ) + kwargs.values()
|
||||
self.nets = [ net ] + list( args ) + list( kwargs.values() )
|
||||
self.nameToNet = kwargs
|
||||
self.nameToNet['net'] = net
|
||||
|
||||
|
||||
+5
-4
@@ -31,9 +31,9 @@ rate includes buffering.
|
||||
from mininet.net import Mininet
|
||||
from mininet.node import CPULimitedHost
|
||||
from mininet.topolib import TreeTopo
|
||||
from mininet.util import custom, waitListening
|
||||
from mininet.util import custom, waitListening, decode
|
||||
from mininet.log import setLogLevel, info
|
||||
|
||||
from mininet.clean import cleanup
|
||||
|
||||
def bwtest( cpuLimits, period_us=100000, seconds=10 ):
|
||||
"""Example/test of link and CPU bandwidth limits
|
||||
@@ -55,7 +55,8 @@ def bwtest( cpuLimits, period_us=100000, seconds=10 ):
|
||||
net = Mininet( topo=topo, host=host )
|
||||
# pylint: disable=bare-except
|
||||
except:
|
||||
info( '*** Skipping scheduler %s\n' % sched )
|
||||
info( '*** Skipping scheduler %s and cleaning up\n' % sched )
|
||||
cleanup()
|
||||
break
|
||||
net.start()
|
||||
net.pingAll()
|
||||
@@ -70,7 +71,7 @@ def bwtest( cpuLimits, period_us=100000, seconds=10 ):
|
||||
# ignore empty result from waitListening/telnet
|
||||
popen.stdout.readline()
|
||||
client.cmd( 'iperf -yc -t %s -c %s' % ( seconds, server.IP() ) )
|
||||
result = popen.stdout.readline().split( ',' )
|
||||
result = decode( popen.stdout.readline() ).split( ',' )
|
||||
bps = float( result[ -1 ] )
|
||||
popen.terminate()
|
||||
net.stop()
|
||||
|
||||
@@ -1,86 +0,0 @@
|
||||
#!/usr/bin/python
|
||||
|
||||
"""
|
||||
fakecluster.py: a fake cluster for testing Mininet cluster edition!!!
|
||||
|
||||
We are going to self-host Mininet by creating a virtual cluster
|
||||
for cluster edition.
|
||||
|
||||
Note: ssh is kind of a mess - you end up having to do things
|
||||
like h1 sudo -E -u openflow ssh 10.2
|
||||
"""
|
||||
|
||||
from mininet.net import Mininet
|
||||
from mininet.nodelib import LinuxBridge, Server
|
||||
from mininet.cli import CLI
|
||||
from mininet.topo import Topo, SingleSwitchTopo
|
||||
from mininet.log import setLogLevel, warn
|
||||
from mininet.util import errRun, quietRun
|
||||
from mininet.link import Link
|
||||
|
||||
from functools import partial
|
||||
from sys import argv
|
||||
|
||||
class MininetServer( Server ):
|
||||
"A server (for nested Mininet) that runs ssh and ovs"
|
||||
|
||||
privateDirs = [ '/var/run/sshd', '/etc/openvswitch',
|
||||
'/var/run/openvswitch', '/var/log/openvswitch' ]
|
||||
|
||||
def __init__( self, *args, **kwargs ):
|
||||
"Turn on ovs by default"
|
||||
kwargs.setdefault( 'ovs', True )
|
||||
super( MininetServer, self ).__init__( *args, **kwargs )
|
||||
|
||||
def config( self, **kwargs ):
|
||||
"""Configure/start sshd and other stuff
|
||||
ovs: start Open vSwitch?"""
|
||||
self.ovs = kwargs.get( 'ovs' )
|
||||
super( MininetServer, self ).config( **kwargs )
|
||||
if self.ovs:
|
||||
self.service( 'openvswitch-switch start' )
|
||||
|
||||
def terminate( self, *args, **kwargs ):
|
||||
"Shut down services and terminate server"
|
||||
if self.ovs:
|
||||
self.service( 'openvswitch-switch stop' )
|
||||
super( MininetServer, self ).terminate( *args, **kwargs )
|
||||
|
||||
|
||||
class ServerLink( Link ):
|
||||
def intfName( self, node, n ):
|
||||
"Override to avoid destruction by cleanup!"
|
||||
# This is kind of ugly... for some reason 'eth0' fails so
|
||||
# we just use 'm1eth0'; however, this should nest reasonably.
|
||||
return ( node.name + 'eth' + repr( n ) if isinstance( node, Server )
|
||||
else node.name + '-eth' + repr( n ) )
|
||||
def makeIntfPair( self, *args, **kwargs ):
|
||||
"Override to use quietRun"
|
||||
kwargs.update( runCmd=quietRun )
|
||||
super( ServerLink, self ).makeIntfPair( *args, **kwargs )
|
||||
|
||||
class ClusterTopo( Topo ):
|
||||
"Cluster topology: m1..mN"
|
||||
def build( self, n ):
|
||||
ms1 = self.addSwitch( 'ms1' )
|
||||
for i in range( 1, n + 1 ):
|
||||
h = self.addHost( 'm%d' % i )
|
||||
self.addLink( h, ms1, cls=ServerLink )
|
||||
|
||||
|
||||
def test( serverCount ):
|
||||
"Test this setup"
|
||||
setLogLevel( 'info' )
|
||||
topo = ClusterTopo( serverCount )
|
||||
host = partial( MininetServer, ssh=True, ovs=True)
|
||||
net = Mininet( topo=topo, host=host, switch=LinuxBridge, ipBase='10.0/24' )
|
||||
MininetServer.updateHostsFiles( net.hosts )
|
||||
# addNAT().configDefault() also connects root namespace to Mininet
|
||||
net.addNAT().configDefault()
|
||||
net.start()
|
||||
CLI( net )
|
||||
net.stop()
|
||||
|
||||
if __name__ == '__main__':
|
||||
n = 8 if len( argv ) != 2 else int( argv[ 1 ] )
|
||||
test( n )
|
||||
+31
-16
@@ -20,24 +20,39 @@ OpenFlow icon from https://www.opennetworking.org/
|
||||
|
||||
MINIEDIT_VERSION = '2.2.0.1'
|
||||
|
||||
import sys
|
||||
from optparse import OptionParser
|
||||
# from Tkinter import *
|
||||
from Tkinter import ( Frame, Label, LabelFrame, Entry, OptionMenu, Checkbutton,
|
||||
Menu, Toplevel, Button, BitmapImage, PhotoImage, Canvas,
|
||||
Scrollbar, Wm, TclError, StringVar, IntVar,
|
||||
E, W, EW, NW, Y, VERTICAL, SOLID, CENTER,
|
||||
RIGHT, LEFT, BOTH, TRUE, FALSE )
|
||||
from ttk import Notebook
|
||||
from tkMessageBox import showerror
|
||||
from subprocess import call
|
||||
import tkFont
|
||||
import tkFileDialog
|
||||
import tkSimpleDialog
|
||||
|
||||
if sys.version_info[0] == 2:
|
||||
from Tkinter import ( Frame, Label, LabelFrame, Entry, OptionMenu,
|
||||
Checkbutton, Menu, Toplevel, Button, BitmapImage,
|
||||
PhotoImage, Canvas, Scrollbar, Wm, TclError,
|
||||
StringVar, IntVar, E, W, EW, NW, Y, VERTICAL, SOLID,
|
||||
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,
|
||||
PhotoImage, Canvas, Scrollbar, Wm, TclError,
|
||||
StringVar, IntVar, E, W, EW, NW, Y, VERTICAL, SOLID,
|
||||
CENTER, RIGHT, LEFT, BOTH, TRUE, FALSE )
|
||||
from tkinter.ttk import Notebook
|
||||
from tkinter.messagebox import showerror
|
||||
from tkinter import font as tkFont
|
||||
from tkinter import simpledialog as tkSimpleDialog
|
||||
from tkinter import filedialog as tkFileDialog
|
||||
|
||||
import re
|
||||
import json
|
||||
from distutils.version import StrictVersion
|
||||
import os
|
||||
import sys
|
||||
from functools import partial
|
||||
|
||||
if 'PYTHONPATH' in os.environ:
|
||||
@@ -1408,7 +1423,7 @@ class MiniEdit( Frame ):
|
||||
def convertJsonUnicode(self, text):
|
||||
"Some part of Mininet don't like Unicode"
|
||||
if isinstance(text, dict):
|
||||
return {self.convertJsonUnicode(key): self.convertJsonUnicode(value) for key, value in text.iteritems()}
|
||||
return {self.convertJsonUnicode(key): self.convertJsonUnicode(value) for key, value in text.items()}
|
||||
elif isinstance(text, list):
|
||||
return [self.convertJsonUnicode(element) for element in text]
|
||||
elif isinstance(text, unicode):
|
||||
@@ -1835,7 +1850,7 @@ class MiniEdit( Frame ):
|
||||
|
||||
# Save Links
|
||||
f.write(" info( '*** Add links\\n')\n")
|
||||
for key,linkDetail in self.links.iteritems():
|
||||
for key,linkDetail in self.links.items():
|
||||
tags = self.canvas.gettags(key)
|
||||
if 'data' in tags:
|
||||
optsExist = False
|
||||
@@ -2875,7 +2890,7 @@ class MiniEdit( Frame ):
|
||||
def buildLinks( self, net):
|
||||
# Make links
|
||||
info( "Getting Links.\n" )
|
||||
for key,link in self.links.iteritems():
|
||||
for key,link in self.links.items():
|
||||
tags = self.canvas.gettags(key)
|
||||
if 'data' in tags:
|
||||
src=link['src']
|
||||
@@ -3211,7 +3226,7 @@ class MiniEdit( Frame ):
|
||||
customs = {}
|
||||
if os.path.isfile( fileName ):
|
||||
execfile( fileName, customs, customs )
|
||||
for name, val in customs.iteritems():
|
||||
for name, val in customs.items():
|
||||
self.setCustom( name, val )
|
||||
else:
|
||||
raise Exception( 'could not find custom file: %s' % fileName )
|
||||
|
||||
@@ -45,7 +45,7 @@ def multiping( netsize, chunksize, seconds):
|
||||
|
||||
# Create network and identify subnets
|
||||
topo = SingleSwitchTopo( netsize )
|
||||
net = Mininet( topo=topo )
|
||||
net = Mininet( topo=topo, waitConnected=True )
|
||||
net.start()
|
||||
hosts = net.hosts
|
||||
subnets = chunks( hosts, chunksize )
|
||||
|
||||
@@ -9,6 +9,7 @@ monitoring them
|
||||
from mininet.topo import SingleSwitchTopo
|
||||
from mininet.net import Mininet
|
||||
from mininet.log import info, setLogLevel
|
||||
from mininet.util import decode
|
||||
|
||||
from time import time
|
||||
from select import poll, POLLIN
|
||||
@@ -19,7 +20,7 @@ def monitorFiles( outfiles, seconds, timeoutms ):
|
||||
"Monitor set of files and return [(host, line)...]"
|
||||
devnull = open( '/dev/null', 'w' )
|
||||
tails, fdToFile, fdToHost = {}, {}, {}
|
||||
for h, outfile in outfiles.iteritems():
|
||||
for h, outfile in outfiles.items():
|
||||
tail = Popen( [ 'tail', '-f', outfile ],
|
||||
stdout=PIPE, stderr=devnull )
|
||||
fd = tail.stdout.fileno()
|
||||
@@ -40,7 +41,7 @@ def monitorFiles( outfiles, seconds, timeoutms ):
|
||||
host = fdToHost[ fd ]
|
||||
# Wait for a line of output
|
||||
line = f.readline().strip()
|
||||
yield host, line
|
||||
yield host, decode( line )
|
||||
else:
|
||||
# If we timed out, return nothing
|
||||
yield None, ''
|
||||
|
||||
@@ -5,8 +5,9 @@ Tests for baresshd.py
|
||||
"""
|
||||
|
||||
import unittest
|
||||
import pexpect
|
||||
from mininet.util import pexpect
|
||||
from mininet.clean import cleanup, sh
|
||||
from sys import stdout
|
||||
|
||||
class testBareSSHD( unittest.TestCase ):
|
||||
|
||||
@@ -14,7 +15,9 @@ class testBareSSHD( unittest.TestCase ):
|
||||
|
||||
def connected( self ):
|
||||
"Log into ssh server, check banner, then exit"
|
||||
p = pexpect.spawn( 'ssh 10.0.0.1 -o StrictHostKeyChecking=no -i /tmp/ssh/test_rsa exit' )
|
||||
p = pexpect.spawn( 'ssh 10.0.0.1 -o ConnectTimeout=1 '
|
||||
'-o StrictHostKeyChecking=no '
|
||||
'-i /tmp/ssh/test_rsa exit' )
|
||||
while True:
|
||||
index = p.expect( self.opts )
|
||||
if index == 0:
|
||||
@@ -22,6 +25,7 @@ class testBareSSHD( unittest.TestCase ):
|
||||
else:
|
||||
return False
|
||||
|
||||
|
||||
def setUp( self ):
|
||||
# verify that sshd is not running
|
||||
self.assertFalse( self.connected() )
|
||||
@@ -55,7 +59,7 @@ class testBareSSHD( unittest.TestCase ):
|
||||
|
||||
def tearDown( self ):
|
||||
# kill the ssh process
|
||||
sh( "ps aux | grep 'ssh.*Banner' | awk '{ print $2 }' | xargs kill" )
|
||||
sh( "ps aux | grep ssh |grep Banner| awk '{ print $2 }' | xargs kill" )
|
||||
cleanup()
|
||||
# remove public key pair
|
||||
sh( 'rm -rf /tmp/ssh' )
|
||||
|
||||
@@ -5,7 +5,7 @@ Tests for bind.py
|
||||
"""
|
||||
|
||||
import unittest
|
||||
import pexpect
|
||||
from mininet.util import pexpect
|
||||
|
||||
class testBind( unittest.TestCase ):
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ A simple sanity check test for cluster edition
|
||||
'''
|
||||
|
||||
import unittest
|
||||
import pexpect
|
||||
from mininet.util import pexpect
|
||||
|
||||
class clusterSanityCheck( unittest.TestCase ):
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ Tests for controllers.py and controllers2.py
|
||||
"""
|
||||
|
||||
import unittest
|
||||
import pexpect
|
||||
from mininet.util import pexpect
|
||||
|
||||
class testControllers( unittest.TestCase ):
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ Test for controlnet.py
|
||||
"""
|
||||
|
||||
import unittest
|
||||
import pexpect
|
||||
from mininet.util import pexpect
|
||||
|
||||
class testControlNet( unittest.TestCase ):
|
||||
|
||||
|
||||
@@ -15,7 +15,7 @@ cfs 10% 1.29e+09
|
||||
"""
|
||||
|
||||
import unittest
|
||||
import pexpect
|
||||
from mininet.util import pexpect
|
||||
import sys
|
||||
|
||||
class testCPU( unittest.TestCase ):
|
||||
|
||||
@@ -5,7 +5,7 @@ Test for emptynet.py
|
||||
"""
|
||||
|
||||
import unittest
|
||||
import pexpect
|
||||
from mininet.util import pexpect
|
||||
|
||||
class testEmptyNet( unittest.TestCase ):
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ Test for hwintf.py
|
||||
import unittest
|
||||
import re
|
||||
|
||||
import pexpect
|
||||
from mininet.util import pexpect
|
||||
|
||||
from mininet.log import setLogLevel
|
||||
from mininet.node import Node
|
||||
|
||||
@@ -5,7 +5,7 @@ Test for intfOptions.py
|
||||
"""
|
||||
|
||||
import unittest
|
||||
import pexpect
|
||||
from mininet.util import pexpect
|
||||
import sys
|
||||
|
||||
class testIntfOptions( unittest.TestCase ):
|
||||
|
||||
@@ -5,7 +5,7 @@ Test for limit.py
|
||||
"""
|
||||
|
||||
import unittest
|
||||
import pexpect
|
||||
from mininet.util import pexpect
|
||||
import sys
|
||||
|
||||
class testLimit( unittest.TestCase ):
|
||||
|
||||
@@ -5,7 +5,7 @@ Test for linearbandwidth.py
|
||||
"""
|
||||
|
||||
import unittest
|
||||
import pexpect
|
||||
from mininet.util import pexpect
|
||||
import sys
|
||||
|
||||
class testLinearBandwidth( unittest.TestCase ):
|
||||
|
||||
@@ -5,7 +5,7 @@ Test for linuxrouter.py
|
||||
"""
|
||||
|
||||
import unittest
|
||||
import pexpect
|
||||
from mininet.util import pexpect
|
||||
from mininet.util import quietRun
|
||||
|
||||
class testLinuxRouter( unittest.TestCase ):
|
||||
|
||||
@@ -6,7 +6,7 @@ validates mininet interfaces against systems interfaces
|
||||
'''
|
||||
|
||||
import unittest
|
||||
import pexpect
|
||||
from mininet.util import pexpect
|
||||
|
||||
class testMultiLink( unittest.TestCase ):
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ Test for multiping.py
|
||||
"""
|
||||
|
||||
import unittest
|
||||
import pexpect
|
||||
from mininet.util import pexpect
|
||||
from collections import defaultdict
|
||||
|
||||
class testMultiPing( unittest.TestCase ):
|
||||
@@ -31,18 +31,20 @@ class testMultiPing( unittest.TestCase ):
|
||||
target = p.match.group(3)
|
||||
received = int( p.match.group(4) )
|
||||
if target == '10.0.0.200':
|
||||
self.assertEqual( received, 0 )
|
||||
self.assertEqual( received, 0, p.match.group(0) + '\n' +
|
||||
target + ' received %d != 0 packets' % received )
|
||||
else:
|
||||
self.assertEqual( received, 1 )
|
||||
self.assertEqual( received, 1, p.match.group(0) + '\n' +
|
||||
target + ' received %d != 1 packets' % received )
|
||||
try:
|
||||
pings[ name ].remove( target )
|
||||
except:
|
||||
pass
|
||||
else:
|
||||
break
|
||||
self.assertTrue( len( pings ) > 0 )
|
||||
self.assertTrue( len( pings ) > 0, 'too few pings' )
|
||||
for t in pings.values():
|
||||
self.assertEqual( len( t ), 0 )
|
||||
self.assertEqual( len( t ), 0, 'missed ping target(s): %s' % t )
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
|
||||
@@ -5,7 +5,7 @@ Test for multipoll.py
|
||||
"""
|
||||
|
||||
import unittest
|
||||
import pexpect
|
||||
from mininet.util import pexpect
|
||||
|
||||
class testMultiPoll( unittest.TestCase ):
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ Test for multitest.py
|
||||
"""
|
||||
|
||||
import unittest
|
||||
import pexpect
|
||||
from mininet.util import pexpect
|
||||
|
||||
class testMultiTest( unittest.TestCase ):
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ Test for nat.py
|
||||
"""
|
||||
|
||||
import unittest
|
||||
import pexpect
|
||||
from mininet.util import pexpect
|
||||
from mininet.util import quietRun
|
||||
|
||||
destIP = '8.8.8.8' # Google DNS
|
||||
|
||||
@@ -5,7 +5,7 @@ Test for natnet.py
|
||||
"""
|
||||
|
||||
import unittest
|
||||
import pexpect
|
||||
from mininet.util import pexpect
|
||||
from mininet.util import quietRun
|
||||
|
||||
class testNATNet( unittest.TestCase ):
|
||||
|
||||
@@ -5,7 +5,7 @@ Test for numberedports.py
|
||||
"""
|
||||
|
||||
import unittest
|
||||
import pexpect
|
||||
from mininet.util import pexpect
|
||||
from collections import defaultdict
|
||||
from mininet.node import OVSSwitch
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ Test for popen.py and popenpoll.py
|
||||
"""
|
||||
|
||||
import unittest
|
||||
import pexpect
|
||||
from mininet.util import pexpect
|
||||
|
||||
class testPopen( unittest.TestCase ):
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ Test for scratchnet.py
|
||||
"""
|
||||
|
||||
import unittest
|
||||
import pexpect
|
||||
from mininet.util import pexpect
|
||||
|
||||
class testScratchNet( unittest.TestCase ):
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ Test for simpleperf.py
|
||||
"""
|
||||
|
||||
import unittest
|
||||
import pexpect
|
||||
from mininet.util import pexpect
|
||||
import sys
|
||||
from mininet.log import setLogLevel
|
||||
|
||||
@@ -18,7 +18,7 @@ class testSimplePerf( unittest.TestCase ):
|
||||
"Run the example and verify iperf results"
|
||||
# 10 Mb/s, plus or minus 20% tolerance
|
||||
BW = 10
|
||||
TOLERANCE = .2
|
||||
TOLERANCE = .2
|
||||
p = pexpect.spawn( 'python -m mininet.examples.simpleperf testmode' )
|
||||
# check iperf results
|
||||
p.expect( "Results: \['10M', '([\d\.]+) .bits/sec", timeout=480 )
|
||||
|
||||
@@ -5,7 +5,7 @@ Test for sshd.py
|
||||
"""
|
||||
|
||||
import unittest
|
||||
import pexpect
|
||||
from mininet.util import pexpect
|
||||
from mininet.clean import sh
|
||||
|
||||
class testSSHD( unittest.TestCase ):
|
||||
|
||||
@@ -5,7 +5,7 @@ Test for tree1024.py
|
||||
"""
|
||||
|
||||
import unittest
|
||||
import pexpect
|
||||
from mininet.util import pexpect
|
||||
import sys
|
||||
|
||||
class testTree1024( unittest.TestCase ):
|
||||
|
||||
@@ -5,7 +5,7 @@ Test for treeping64.py
|
||||
"""
|
||||
|
||||
import unittest
|
||||
import pexpect
|
||||
from mininet.util import pexpect
|
||||
import sys
|
||||
|
||||
class testTreePing64( unittest.TestCase ):
|
||||
|
||||
@@ -5,7 +5,7 @@ Test for vlanhost.py
|
||||
"""
|
||||
|
||||
import unittest
|
||||
import pexpect
|
||||
from mininet.util import pexpect
|
||||
import sys
|
||||
from mininet.util import quietRun
|
||||
|
||||
|
||||
+8
-8
@@ -16,24 +16,25 @@ import time
|
||||
|
||||
from mininet.log import info
|
||||
from mininet.term import cleanUpScreens
|
||||
|
||||
from mininet.util import decode
|
||||
|
||||
def sh( cmd ):
|
||||
"Print a command and send it to the shell"
|
||||
info( cmd + '\n' )
|
||||
return Popen( [ '/bin/sh', '-c', cmd ], stdout=PIPE ).communicate()[ 0 ]
|
||||
result = Popen( [ '/bin/sh', '-c', cmd ], stdout=PIPE ).communicate()[ 0 ]
|
||||
return decode( result )
|
||||
|
||||
def killprocs( pattern ):
|
||||
"Reliably terminate processes matching a pattern (including args)"
|
||||
sh( 'pkill -9 -f %s' % pattern )
|
||||
# Make sure they are gone
|
||||
while True:
|
||||
try:
|
||||
pids = co( [ 'pgrep', '-f', pattern ] ).split( '\n' )
|
||||
pids = co( [ 'pgrep', '-f', pattern ] )
|
||||
except CalledProcessError:
|
||||
pids = []
|
||||
# Don't kill init
|
||||
pids = [ pid for pid in pids if pid and pid != '1' ]
|
||||
pids = ''
|
||||
if pids:
|
||||
sh( "kill -9 %s" % ' '.join( pids ) )
|
||||
sh( 'pkill -9 -f %s' % pattern )
|
||||
time.sleep( .5 )
|
||||
else:
|
||||
break
|
||||
@@ -76,7 +77,6 @@ class Cleanup( object ):
|
||||
for dp in dps:
|
||||
if dp:
|
||||
sh( 'dpctl deldp ' + dp )
|
||||
|
||||
info( "*** Removing OVS datapaths\n" )
|
||||
dps = sh("ovs-vsctl --timeout=1 list-br").strip().splitlines()
|
||||
if dps:
|
||||
|
||||
+5
-12
@@ -25,9 +25,7 @@ Link: basic link class for creating veth pairs
|
||||
"""
|
||||
|
||||
from mininet.log import info, error, debug
|
||||
from mininet.util import makeIntfPair, quietRun
|
||||
import mininet.node
|
||||
|
||||
from mininet.util import makeIntfPair
|
||||
import re
|
||||
|
||||
class Intf( object ):
|
||||
@@ -166,7 +164,7 @@ class Intf( object ):
|
||||
method: config method name
|
||||
param: arg=value (ignore if value=None)
|
||||
value may also be list or dict"""
|
||||
name, value = param.items()[ 0 ]
|
||||
name, value = list( param.items() )[ 0 ]
|
||||
f = getattr( self, method, None )
|
||||
if not f or value is None:
|
||||
return
|
||||
@@ -287,11 +285,7 @@ class TCIntf( Intf ):
|
||||
loss=None, max_queue_size=None ):
|
||||
"Internal method: return tc commands for delay and loss"
|
||||
cmds = []
|
||||
if delay and delay < 0:
|
||||
error( 'Negative delay', delay, '\n' )
|
||||
elif jitter and jitter < 0:
|
||||
error( 'Negative jitter', jitter, '\n' )
|
||||
elif loss and ( loss < 0 or loss > 100 ):
|
||||
if loss and ( loss < 0 or loss > 100 ):
|
||||
error( 'Bad loss percentage', loss, '%%\n' )
|
||||
else:
|
||||
# Delay/jitter/loss/max queue size
|
||||
@@ -481,8 +475,7 @@ class Link( object ):
|
||||
|
||||
@classmethod
|
||||
def makeIntfPair( cls, intfname1, intfname2, addr1=None, addr2=None,
|
||||
node1=None, node2=None, deleteIntfs=True,
|
||||
runCmd=quietRun ):
|
||||
node1=None, node2=None, deleteIntfs=True ):
|
||||
"""Create pair of interfaces
|
||||
intfname1: name for interface 1
|
||||
intfname2: name for interface 2
|
||||
@@ -495,7 +488,7 @@ class Link( object ):
|
||||
# Leave this as a class method for now
|
||||
assert cls
|
||||
return makeIntfPair( intfname1, intfname2, addr1, addr2, node1, node2,
|
||||
deleteIntfs=deleteIntfs, runCmd=runCmd )
|
||||
deleteIntfs=deleteIntfs )
|
||||
|
||||
def delete( self ):
|
||||
"Delete this link"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
"Module dependency utility functions for Mininet."
|
||||
|
||||
from mininet.util import quietRun
|
||||
from mininet.util import quietRun, BaseString
|
||||
from mininet.log import info, error, debug
|
||||
from os import environ
|
||||
|
||||
@@ -28,9 +28,9 @@ def moduleDeps( subtract=None, add=None ):
|
||||
add: string or list of module names to add, if not already loaded"""
|
||||
subtract = subtract if subtract is not None else []
|
||||
add = add if add is not None else []
|
||||
if isinstance( subtract, basestring ):
|
||||
if isinstance( subtract, BaseString ):
|
||||
subtract = [ subtract ]
|
||||
if isinstance( add, basestring ):
|
||||
if isinstance( add, BaseString ):
|
||||
add = [ add ]
|
||||
for mod in subtract:
|
||||
if mod in lsmod():
|
||||
|
||||
+10
-8
@@ -89,7 +89,7 @@ method may be called to shut down the network.
|
||||
import os
|
||||
import re
|
||||
import select
|
||||
from signal import SIGKILL
|
||||
import signal
|
||||
import random
|
||||
|
||||
from time import sleep
|
||||
@@ -104,7 +104,7 @@ from mininet.nodelib import NAT
|
||||
from mininet.link import Link, Intf
|
||||
from mininet.util import ( quietRun, fixLimits, numCores, ensureRoot,
|
||||
macColonHex, ipStr, ipParse, netParse, ipAdd,
|
||||
waitListening )
|
||||
waitListening, BaseString )
|
||||
from mininet.term import cleanUpScreens, makeTerms
|
||||
|
||||
# Mininet version: should be consistent with README and LICENSE
|
||||
@@ -190,7 +190,7 @@ class Mininet( object ):
|
||||
if not remaining:
|
||||
info( '\n' )
|
||||
return True
|
||||
if time > timeout and timeout is not None:
|
||||
if timeout is not None and time > timeout:
|
||||
break
|
||||
sleep( delay )
|
||||
time += delay
|
||||
@@ -383,8 +383,8 @@ class Mininet( object ):
|
||||
params: additional link params (optional)
|
||||
returns: link object"""
|
||||
# Accept node objects or names
|
||||
node1 = node1 if not isinstance( node1, basestring ) else self[ node1 ]
|
||||
node2 = node2 if not isinstance( node2, basestring ) else self[ node2 ]
|
||||
node1 = node1 if not isinstance( node1, BaseString ) else self[ node1 ]
|
||||
node2 = node2 if not isinstance( node2, BaseString ) else self[ node2 ]
|
||||
options = dict( params )
|
||||
# Port is optional
|
||||
if port1 is not None:
|
||||
@@ -524,7 +524,7 @@ class Mininet( object ):
|
||||
def stopXterms( self ):
|
||||
"Kill each xterm."
|
||||
for term in self.terms:
|
||||
term.send_signal( SIGKILL )
|
||||
os.kill( term.pid, signal.SIGKILL )
|
||||
cleanUpScreens()
|
||||
|
||||
def staticArp( self ):
|
||||
@@ -549,7 +549,8 @@ class Mininet( object ):
|
||||
switch.start( self.controllers )
|
||||
started = {}
|
||||
for swclass, switches in groupby(
|
||||
sorted( self.switches, key=type ), type ):
|
||||
sorted( self.switches,
|
||||
key=lambda s: str( type( s ) ) ), type ):
|
||||
switches = tuple( switches )
|
||||
if hasattr( swclass, 'batchStartup' ):
|
||||
success = swclass.batchStartup( switches )
|
||||
@@ -576,7 +577,8 @@ class Mininet( object ):
|
||||
info( '*** Stopping %i switches\n' % len( self.switches ) )
|
||||
stopped = {}
|
||||
for swclass, switches in groupby(
|
||||
sorted( self.switches, key=type ), type ):
|
||||
sorted( self.switches,
|
||||
key=lambda s: str( type( s ) ) ), type ):
|
||||
switches = tuple( switches )
|
||||
if hasattr( swclass, 'batchShutdown' ):
|
||||
success = swclass.batchShutdown( switches )
|
||||
|
||||
+63
-115
@@ -62,7 +62,8 @@ from time import sleep
|
||||
|
||||
from mininet.log import info, error, warn, debug
|
||||
from mininet.util import ( quietRun, errRun, errFail, moveIntf, isShellBuiltin,
|
||||
numCores, retry, mountCgroups )
|
||||
numCores, retry, mountCgroups, BaseString, decode,
|
||||
encode, Python3 )
|
||||
from mininet.moduledeps import moduleDeps, pathCheck, TUN
|
||||
from mininet.link import Link, Intf, TCIntf, OVSIntf
|
||||
from re import findall
|
||||
@@ -74,11 +75,10 @@ class Node( object ):
|
||||
|
||||
portBase = 0 # Nodes always start with eth0/port0, even in OF 1.0
|
||||
|
||||
def __init__( self, name, **params ):
|
||||
def __init__( self, name, inNamespace=True, **params ):
|
||||
"""name: name of node
|
||||
ns: private namespaces to use ['net','mnt']
|
||||
inNamespace: in network namespace?
|
||||
privateDirs: list of private directory strings or tuples
|
||||
overlayDirs: list of overlay directory strings or tuples
|
||||
params: Node parameters (see config() for details)"""
|
||||
|
||||
# Make sure class actually works
|
||||
@@ -86,14 +86,10 @@ class Node( object ):
|
||||
|
||||
self.name = params.get( 'name', name )
|
||||
self.privateDirs = params.get( 'privateDirs', [] )
|
||||
self.overlayDirs = params.get( 'overlayDirs', [] )
|
||||
self.inNamespace = params.get( 'inNamespace', inNamespace )
|
||||
|
||||
# Support old inNamespace param
|
||||
self.ns = params.get( 'ns', ( 'net', 'mnt' ) )
|
||||
inNamespace = params.get( 'inNamespace', True )
|
||||
if not inNamespace:
|
||||
self.ns = []
|
||||
self.inNamespace = 'net' in self.ns
|
||||
# Python 3 complains if we don't wait for shell exit
|
||||
self.waitExited = params.get( 'waitExited', Python3 == True )
|
||||
|
||||
# Stash configuration parameters for future reference
|
||||
self.params = params
|
||||
@@ -110,9 +106,8 @@ class Node( object ):
|
||||
self.waiting = False
|
||||
self.readbuf = ''
|
||||
|
||||
# Start command interpreter shell and mount any local dirs
|
||||
# Start command interpreter shell
|
||||
self.startShell()
|
||||
self.mountOverlayDirs()
|
||||
self.mountPrivateDirs()
|
||||
|
||||
# File descriptor to node mapping support
|
||||
@@ -129,33 +124,33 @@ class Node( object ):
|
||||
node = cls.outToNode.get( fd )
|
||||
return node or cls.inToNode.get( fd )
|
||||
|
||||
_marker = re.compile( chr( 1 ) + r'(\d+)\r\n' )
|
||||
|
||||
# Command support via shell process in namespace
|
||||
def startShell( self, mnopts=None ):
|
||||
"Start a shell process for running commands"
|
||||
if self.shell:
|
||||
error( "%s: shell is already running\n" % self.name )
|
||||
return
|
||||
# mnexec: (c)lose descriptors
|
||||
# (p)rint pid, and run in (n)etwork and (m)ount namespace
|
||||
opts = '-cdp' if mnopts is None else mnopts
|
||||
# Handle additional namespaces if specified
|
||||
nsmap = { 'pid': 'P', 'mnt': 'm', 'net': 'n', 'uts': 'u' }
|
||||
chars = [ nsmap.get( ns, '' ) for ns in self.ns ]
|
||||
opts += ''.join( chars )
|
||||
# mnexec: (c)lose descriptors, (d)etach from tty,
|
||||
# (p)rint pid, and run in (n)amespace
|
||||
opts = '-cd' if mnopts is None else mnopts
|
||||
if self.inNamespace:
|
||||
opts += 'n'
|
||||
# bash -i: force interactive
|
||||
# -s: pass $* to shell, and make process easy to find in ps
|
||||
# prompt is set to sentinel chr( 127 )
|
||||
cmd = [ 'mnexec', opts, 'env', 'PS1=' + chr( 127 ),
|
||||
'bash', '--norc', '-is', 'mininet:' + self.name ]
|
||||
'bash', '--norc', '--noediting',
|
||||
'-is', 'mininet:' + self.name ]
|
||||
|
||||
# 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.stdin = os.fdopen( master, 'rw' )
|
||||
# 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.stdout = self.stdin
|
||||
self.pid = self.shell.pid
|
||||
self.pollOut = select.poll()
|
||||
@@ -170,21 +165,19 @@ class Node( object ):
|
||||
self.lastPid = None
|
||||
self.readbuf = ''
|
||||
# Wait for prompt
|
||||
self.waiting = True
|
||||
self.waitOutput()
|
||||
if 'P' in opts:
|
||||
assert self.lastPid is not None
|
||||
self.pid = self.lastPid
|
||||
while True:
|
||||
data = self.read( 1024 )
|
||||
if data[ -1 ] == chr( 127 ):
|
||||
break
|
||||
self.pollOut.poll()
|
||||
self.waiting = False
|
||||
# +m: disable job control notification
|
||||
initcmd = 'unset HISTFILE; stty -echo; set +m'
|
||||
if 'uts' in self.ns:
|
||||
initcmd += '; hostname ' + self.name
|
||||
self.cmd( initcmd )
|
||||
self.cmd( 'unset HISTFILE; stty -echo; set +m' )
|
||||
|
||||
def mountPrivateDirs( self ):
|
||||
"Mount private directories"
|
||||
"mount private directories"
|
||||
# Avoid expanding a string into a list of chars
|
||||
assert not isinstance( self.privateDirs, basestring )
|
||||
assert not isinstance( self.privateDirs, BaseString )
|
||||
for directory in self.privateDirs:
|
||||
if isinstance( directory, tuple ):
|
||||
# mount given private directory
|
||||
@@ -200,68 +193,22 @@ class Node( object ):
|
||||
self.cmd( 'mount -n -t tmpfs tmpfs %s' % directory )
|
||||
|
||||
def unmountPrivateDirs( self ):
|
||||
"Unmount private and overlay directories"
|
||||
"mount private directories"
|
||||
for directory in self.privateDirs:
|
||||
if isinstance( directory, tuple ):
|
||||
self.cmd( 'umount ', directory[ 0 ] )
|
||||
else:
|
||||
self.cmd( 'umount ', directory )
|
||||
|
||||
# XXX We should make overlayDirs as consistent as possible
|
||||
# with privateDirs.
|
||||
|
||||
def _overlayFrom( self, entry ):
|
||||
"Helper function: return mountpaint, overlay, tmpfs from entry"
|
||||
if type( entry ) is str:
|
||||
# '/mountpoint'
|
||||
mountpoint, overlay = entry, None
|
||||
elif len( entry ) is 1:
|
||||
# [ '/mountpoint' ]
|
||||
mountpoint, overlay = entry[ 0 ], None
|
||||
else:
|
||||
# [ '/mountpoint', '/overlay' ]
|
||||
mountpoint, overlay = entry
|
||||
tmpfs = None if overlay else '/tmp/%s/%s' % ( self, mountpoint )
|
||||
return mountpoint, overlay, tmpfs
|
||||
|
||||
def mountOverlayDirs( self ):
|
||||
"""Mount overlay directories. Overlay directories are similar
|
||||
to private directories except they are copy-on-write copies
|
||||
of directories in the host file system.
|
||||
overlayDirs is of the form ((mountpoint,overlaydir), ...)
|
||||
much like privateDirs. If overlaydir doesn't exist, we
|
||||
mount a tmpfs at the specified mount point."""
|
||||
# Avoid expanding a string into a list of chars
|
||||
assert not isinstance( self.overlayDirs, basestring )
|
||||
for entry in self.overlayDirs:
|
||||
mountpoint, overlay, tmpfs = self._overlayFrom( entry )
|
||||
# Create tmpfs if overlay dir is not specified
|
||||
if not overlay:
|
||||
overlay = tmpfs
|
||||
self.cmd( 'mkdir -p', overlay )
|
||||
self.cmd( 'mount -t tmpfs tmpfs', overlay )
|
||||
# Mount overlay dir at mount point
|
||||
self.cmd( ( 'mount -t overlayfs -o upperdir=%s,lowerdir=%s'
|
||||
' overlayfs %s' ) % ( overlay, mountpoint, mountpoint ) )
|
||||
|
||||
def unmountOverlayDirs( self ):
|
||||
"Unmount overlay directories"
|
||||
for entry in self.overlayDirs:
|
||||
mountpoint, overlay, tmpfs = self._overlayFrom( entry )
|
||||
# Unfortunately these umounts can fail if the mount point
|
||||
# is in use, possibly leaving tmpfs garbage in the root
|
||||
# mount namespace / file system
|
||||
self.cmd( 'umount', mountpoint )
|
||||
if not overlay:
|
||||
self.cmd( 'umount', tmpfs )
|
||||
|
||||
def _popen( self, cmd, **params ):
|
||||
"""Internal method: spawn and return a process
|
||||
cmd: command to run (list)
|
||||
params: parameters to Popen()"""
|
||||
# Leave this is as an instance method for now
|
||||
assert self
|
||||
return Popen( cmd, **params )
|
||||
popen = Popen( cmd, **params )
|
||||
debug( '_popen', cmd, popen.pid )
|
||||
return popen
|
||||
|
||||
def cleanup( self ):
|
||||
"Help python collect its garbage."
|
||||
@@ -270,6 +217,9 @@ 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()
|
||||
self.shell = None
|
||||
|
||||
# Subshell I/O, commands and control
|
||||
@@ -279,7 +229,7 @@ class Node( object ):
|
||||
maxbytes: maximum number of bytes to return"""
|
||||
count = len( self.readbuf )
|
||||
if count < maxbytes:
|
||||
data = os.read( self.stdout.fileno(), maxbytes - count )
|
||||
data = decode( os.read( self.stdout.fileno(), maxbytes - count ) )
|
||||
self.readbuf += data
|
||||
if maxbytes >= len( self.readbuf ):
|
||||
result = self.readbuf
|
||||
@@ -303,15 +253,14 @@ class Node( object ):
|
||||
def write( self, data ):
|
||||
"""Write data to node.
|
||||
data: string"""
|
||||
os.write( self.stdin.fileno(), data )
|
||||
os.write( self.stdin.fileno(), encode( data ) )
|
||||
|
||||
def terminate( self ):
|
||||
"Send kill signal to Node and clean up after it."
|
||||
self.unmountPrivateDirs()
|
||||
self.unmountOverlayDirs()
|
||||
if self.shell:
|
||||
if self.shell.poll() is None:
|
||||
os.killpg( self.pid, signal.SIGHUP )
|
||||
os.killpg( self.shell.pid, signal.SIGHUP )
|
||||
self.cleanup()
|
||||
|
||||
def stop( self, deleteIntfs=False ):
|
||||
@@ -358,10 +307,10 @@ class Node( object ):
|
||||
self.lastPid = None
|
||||
self.waiting = True
|
||||
|
||||
def sendInt( self, signal=signal.SIGINT ):
|
||||
def sendInt( self, intr=chr( 3 ) ):
|
||||
"Interrupt running command."
|
||||
debug( "sending signal %d to pgrp %d" % ( signal, self.pid ) )
|
||||
os.killpg( self.pid, signal )
|
||||
debug( 'sendInt: writing chr(%d)\n' % ord( intr ) )
|
||||
self.write( intr )
|
||||
|
||||
def monitor( self, timeoutms=None, findPid=True ):
|
||||
"""Monitor and return the output of a command.
|
||||
@@ -374,18 +323,18 @@ class Node( object ):
|
||||
data = self.read( 1024 )
|
||||
pidre = r'\[\d+\] \d+\r\n'
|
||||
# Look for PID
|
||||
marker = chr( 1 ) + r'\d+\r\n'
|
||||
if findPid and chr( 1 ) in data:
|
||||
# suppress the job and PID of a backgrounded command
|
||||
if re.findall( pidre, data ):
|
||||
data = re.sub( pidre, '', data )
|
||||
# Marker can be read in chunks; continue until all of it is read
|
||||
while True:
|
||||
markers = self._marker.findall( data )
|
||||
if markers:
|
||||
self.lastPid = int( markers[ -1 ] )
|
||||
data = self._marker.sub( '', data )
|
||||
break
|
||||
while not re.findall( marker, data ):
|
||||
data += self.read( 1024 )
|
||||
markers = re.findall( marker, data )
|
||||
if markers:
|
||||
self.lastPid = int( markers[ 0 ][ 1: ] )
|
||||
data = re.sub( marker, '', data )
|
||||
# Look for sentinel/EOF
|
||||
if len( data ) > 0 and data[ -1 ] == chr( 127 ):
|
||||
self.waiting = False
|
||||
@@ -438,7 +387,7 @@ class Node( object ):
|
||||
if isinstance( args[ 0 ], list ):
|
||||
# popen([cmd, arg1, arg2...])
|
||||
cmd = args[ 0 ]
|
||||
elif isinstance( args[ 0 ], basestring ):
|
||||
elif isinstance( args[ 0 ], BaseString ):
|
||||
# popen("cmd arg1 arg2...")
|
||||
cmd = args[ 0 ].split()
|
||||
else:
|
||||
@@ -451,7 +400,6 @@ class Node( object ):
|
||||
# Shell requires a string, not a list!
|
||||
if defaults.get( 'shell', False ):
|
||||
cmd = ' '.join( cmd )
|
||||
debug( cmd, defaults )
|
||||
popen = self._popen( cmd, **defaults )
|
||||
return popen
|
||||
|
||||
@@ -463,7 +411,7 @@ class Node( object ):
|
||||
# Warning: this can fail with large numbers of fds!
|
||||
out, err = popen.communicate()
|
||||
exitcode = popen.wait()
|
||||
return out, err, exitcode
|
||||
return decode( out ), decode( err ), exitcode
|
||||
|
||||
# Interface management, configuration, and routing
|
||||
|
||||
@@ -525,7 +473,7 @@ class Node( object ):
|
||||
"""
|
||||
if not intf:
|
||||
return self.defaultIntf()
|
||||
elif isinstance( intf, basestring):
|
||||
elif isinstance( intf, BaseString):
|
||||
return self.nameToIntf[ intf ]
|
||||
else:
|
||||
return intf
|
||||
@@ -552,7 +500,7 @@ class Node( object ):
|
||||
# explicitly so that we won't get errors if we run before they
|
||||
# have been removed by the kernel. Unfortunately this is very slow,
|
||||
# at least with Linux kernels before 2.6.33
|
||||
for intf in self.intfs.values():
|
||||
for intf in list( self.intfs.values() ):
|
||||
# Protect against deleting hardware interfaces
|
||||
if ( self.name in intf.name ) or ( not checkName ):
|
||||
intf.delete()
|
||||
@@ -577,7 +525,7 @@ class Node( object ):
|
||||
"""Set the default route to go through intf.
|
||||
intf: Intf or {dev <intfname> via <gw-ip> ...}"""
|
||||
# Note setParam won't call us if intf is none
|
||||
if isinstance( intf, basestring ) and ' ' in intf:
|
||||
if isinstance( intf, BaseString ) and ' ' in intf:
|
||||
params = intf
|
||||
else:
|
||||
params = 'dev %s' % intf
|
||||
@@ -624,7 +572,7 @@ class Node( object ):
|
||||
method: config method name
|
||||
param: arg=value (ignore if value=None)
|
||||
value may also be list or dict"""
|
||||
name, value = param.items()[ 0 ]
|
||||
name, value = list( param.items() )[ 0 ]
|
||||
if value is None:
|
||||
return
|
||||
f = getattr( self, method, None )
|
||||
@@ -673,7 +621,7 @@ class Node( object ):
|
||||
|
||||
def intfList( self ):
|
||||
"List of our interfaces sorted by port number"
|
||||
return [ self.intfs[ p ] for p in sorted( self.intfs.iterkeys() ) ]
|
||||
return [ self.intfs[ p ] for p in sorted( self.intfs.keys() ) ]
|
||||
|
||||
def intfNames( self ):
|
||||
"The names of our interfaces sorted by port number"
|
||||
@@ -707,8 +655,6 @@ class Node( object ):
|
||||
def setup( cls ):
|
||||
"Make sure our class dependencies are available"
|
||||
pathCheck( 'mnexec', 'ifconfig', moduleName='Mininet')
|
||||
if '-m:' not in quietRun( 'mnexec -h' ):
|
||||
raise Exception( 'Please update mnexec (e.g. make install)' )
|
||||
|
||||
class Host( Node ):
|
||||
"A host is simply a Node"
|
||||
@@ -939,14 +885,14 @@ class Switch( Node ):
|
||||
self.dpid = self.defaultDpid( dpid )
|
||||
self.opts = opts
|
||||
self.listenPort = listenPort
|
||||
if 'net' not in self.ns:
|
||||
if not self.inNamespace:
|
||||
self.controlIntf = Intf( 'lo', self, port=0 )
|
||||
|
||||
def defaultDpid( self, dpid=None ):
|
||||
"Return correctly formatted dpid from dpid or switch name (s1 -> 1)"
|
||||
if dpid:
|
||||
# Remove any colons and make sure it's a good hex number
|
||||
dpid = dpid.translate( None, ':' )
|
||||
dpid = dpid.replace( ':', '' )
|
||||
assert len( dpid ) <= self.dpidLen and int( dpid, 16 ) >= 0
|
||||
else:
|
||||
# Use hex of the first number in the switch name
|
||||
@@ -954,6 +900,7 @@ class Switch( Node ):
|
||||
if nums:
|
||||
dpid = hex( int( nums[ 0 ] ) )[ 2: ]
|
||||
else:
|
||||
self.terminate() # Python 3.6 crash workaround
|
||||
raise Exception( 'Unable to derive default datapath ID - '
|
||||
'please either specify a dpid or use a '
|
||||
'canonical switch name such as s23.' )
|
||||
@@ -1145,9 +1092,10 @@ class OVSSwitch( Switch ):
|
||||
version = quietRun( 'ovs-vsctl --version' )
|
||||
cls.OVSVersion = findall( r'\d+\.\d+', version )[ 0 ]
|
||||
|
||||
def isOldOVS( self ):
|
||||
@classmethod
|
||||
def isOldOVS( cls ):
|
||||
"Is OVS ersion < 1.10?"
|
||||
return ( StrictVersion( self.OVSVersion ) <
|
||||
return ( StrictVersion( cls.OVSVersion ) <
|
||||
StrictVersion( '1.10' ) )
|
||||
|
||||
def dpctl( self, *args ):
|
||||
@@ -1233,7 +1181,7 @@ class OVSSwitch( Switch ):
|
||||
"Start up a new OVS OpenFlow switch using ovs-vsctl"
|
||||
if self.inNamespace:
|
||||
raise Exception(
|
||||
'OVS kernel switch does not work in a network namespace' )
|
||||
'OVS kernel switch does not work in a namespace' )
|
||||
int( self.dpid, 16 ) # DPID must be a hex string
|
||||
# Command to add interfaces
|
||||
intfs = ''.join( ' -- add-port %s %s' % ( self, intf ) +
|
||||
@@ -1296,7 +1244,7 @@ class OVSSwitch( Switch ):
|
||||
run( cmds, shell=True )
|
||||
# Reapply link config if necessary...
|
||||
for switch in switches:
|
||||
for intf in switch.intfs.itervalues():
|
||||
for intf in switch.intfs.values():
|
||||
if isinstance( intf, TCIntf ):
|
||||
intf.config( **intf.params )
|
||||
return switches
|
||||
@@ -1322,7 +1270,7 @@ class OVSSwitch( Switch ):
|
||||
pids = ' '.join( str( switch.pid ) for switch in switches )
|
||||
run( 'kill -HUP ' + pids )
|
||||
for switch in switches:
|
||||
switch.shell = None
|
||||
switch.terminate()
|
||||
return switches
|
||||
|
||||
|
||||
|
||||
+1
-86
@@ -4,14 +4,11 @@ Node Library for Mininet
|
||||
This contains additional Node types which you may find to be useful.
|
||||
"""
|
||||
|
||||
from mininet.node import Node, Host, Switch
|
||||
from mininet.node import Node, Switch
|
||||
from mininet.log import info, warn
|
||||
from mininet.moduledeps import pathCheck
|
||||
from mininet.util import quietRun
|
||||
|
||||
import re
|
||||
from tempfile import NamedTemporaryFile
|
||||
|
||||
|
||||
class LinuxBridge( Switch ):
|
||||
"Linux Bridge (with optional spanning tree)"
|
||||
@@ -146,85 +143,3 @@ class NAT( Node ):
|
||||
# Put the forwarding state back to what it was
|
||||
self.cmd( 'sysctl net.ipv4.ip_forward=%s' % self.forwardState )
|
||||
super( NAT, self ).terminate()
|
||||
|
||||
|
||||
class Server( Host ):
|
||||
"Run sshd in a net/mnt/pid/uts namespace, with private /etc/hosts"
|
||||
|
||||
ns = [ 'net', 'mnt', 'pid', 'uts' ]
|
||||
overlayDirs = [ '/etc', '/var/run', '/var/log' ]
|
||||
privateDirs = [ '/var/run/sshd', ]
|
||||
|
||||
def __init__( self, *args, **kwargs ):
|
||||
"""Add overlay dirs and private dirs, and change permissions
|
||||
ssh: run sshd? (True)"""
|
||||
kwargs.setdefault( 'inNamespace', True )
|
||||
kwargs.setdefault( 'ns', self.ns )
|
||||
kwargs.setdefault( 'privateDirs', self.privateDirs )
|
||||
kwargs.setdefault( 'overlayDirs', self.overlayDirs )
|
||||
kwargs.setdefault( 'ssh', True )
|
||||
super( Server, self ).__init__( *args, **kwargs )
|
||||
# Change permissions, mainly for ssh
|
||||
for pdir in self.privateDirs:
|
||||
self.cmd( 'chown root:root', pdir )
|
||||
self.cmd( 'chmod 755', pdir )
|
||||
|
||||
@staticmethod
|
||||
def updateHostsFiles( servers, tmpdirs=[ '/tmp', '/var/tmp' ] ):
|
||||
"""Update local hosts files on a list of servers
|
||||
servers: list of servers
|
||||
tmpdir: tmp dir shared between mn and servers"""
|
||||
# For large configurations it will be more efficient
|
||||
# to use a DNS server
|
||||
hosts = ( '# Mininet hosts file\n'
|
||||
'127.0.0.1 localhost %s\n' +
|
||||
''.join( '%s %s\n' % ( t.IP(), t )
|
||||
for t in servers ) )
|
||||
for s in servers:
|
||||
dirs = ( getattr( s, 'overlayDirs', [] ) +
|
||||
getattr( s, 'privateDirs', [] ) )
|
||||
if '/etc' in dirs:
|
||||
tmpdirs = [ d for d in tmpdirs if d not in dirs ]
|
||||
if tmpdirs:
|
||||
with NamedTemporaryFile( dir=tmpdirs[ 0 ] ) as tmpfile:
|
||||
tmpfile.write( hosts % s )
|
||||
tmpfile.flush()
|
||||
s.cmd( 'cp', tmpfile.name, '/etc/hosts' )
|
||||
else:
|
||||
warn( 'not updating hosts file on %s\n' % s )
|
||||
|
||||
def service( self, cmd ):
|
||||
"""Start or stop a service
|
||||
usage: service( 'ssh stop' )"""
|
||||
self.cmd( '/etc/init.d/%s' % cmd )
|
||||
|
||||
def motd( self ):
|
||||
"Return login message as a string"
|
||||
return 'Welcome to Mininet host %s at %s' % ( self, self.IP() )
|
||||
|
||||
def startSSH( self, motdPath='/var/run/motd.dynamic' ):
|
||||
"Update motd, Clear out utmp/wtmp/btmp, and start sshd"
|
||||
# Note: /var/run and /var/log must be overlays!
|
||||
assert ( '/var/run' in ( self.overlayDirs + self.privateDirs ) and
|
||||
'/var/log' in ( self.overlayDirs + self.privateDirs ) )
|
||||
self.cmd( "echo '%s' > %s" % ( self.motd(), motdPath ) )
|
||||
self.cmd( 'truncate -s0 /var/run/utmp /var/log/wtmp* /var/log/btmp*' )
|
||||
# sshd.pid should really be in /var/run/sshd instead of /var/run
|
||||
self.cmd( 'rm /var/run/sshd.pid' )
|
||||
self.cmd( '/etc/init.d/ssh start' )
|
||||
|
||||
def config( self, **kwargs ):
|
||||
"""Configure/start sshd and other stuff
|
||||
ssh: start sshd? (True )"""
|
||||
super( Server, self ).config( **kwargs )
|
||||
self.ssh = kwargs.get( 'ssh' )
|
||||
if self.ssh:
|
||||
self.startSSH()
|
||||
if 'uts' in self.ns:
|
||||
self.cmd( 'hostname', self )
|
||||
|
||||
def terminate( self, *args, **kwargs ):
|
||||
"Shut down services and terminate server"
|
||||
if self.ssh:
|
||||
self.service( 'ssh stop' )
|
||||
super( Server, self ).terminate( *args, **kwargs )
|
||||
|
||||
+13
-64
@@ -5,43 +5,13 @@ Utility functions to run a terminal (connected via socat(1)) on each host.
|
||||
Requires socat(1) and xterm(1).
|
||||
Optionally uses gnome-terminal.
|
||||
"""
|
||||
|
||||
from os import environ
|
||||
|
||||
from mininet.log import error
|
||||
from mininet.util import quietRun, errRun
|
||||
|
||||
from os import environ, getpid, path, setsid
|
||||
from subprocess import Popen, PIPE, STDOUT
|
||||
from tempfile import NamedTemporaryFile
|
||||
|
||||
def getAuthX11( display ):
|
||||
"Return X11 credentials for display"
|
||||
host, screen = display.split( ':' )
|
||||
host = host.split( '/' )[ 0 ]
|
||||
hostname = quietRun( 'hostname' ).strip()
|
||||
# First, try hostname:display
|
||||
if host == 'localhost':
|
||||
host = hostname
|
||||
result = quietRun( 'xauth list %s:%s' % ( host, screen ) )
|
||||
# Otherwise, try hostname/unix:display
|
||||
if not result:
|
||||
result = quietRun( 'xauth list %s/unix:%s' % ( host, screen ) )
|
||||
items = result.strip().split()
|
||||
if len( items ) != 3:
|
||||
raise Exception( "getAuthX11: could not fetch credentials for " +
|
||||
display )
|
||||
return items
|
||||
|
||||
# This is tricky with uts and pid namespaces
|
||||
# For uts namespaces, we create and use a private $XAUTHORITY
|
||||
# and add credentials for the node's hostname.
|
||||
# To enable pid namespaces to work, we proxy the X11
|
||||
# socket twice using socat - first with a shared socket in /tmp, and
|
||||
# second with a TCP listener in the host network namespace.
|
||||
# Note that this will fail if /tmp is not shared - we should
|
||||
# probably think about this some more. We could potentially
|
||||
# specify a globally shared directory somehow if /tmp is
|
||||
# private.
|
||||
|
||||
def tunnelX11( node, display=None ):
|
||||
def tunnelX11( node, display=None):
|
||||
"""Create an X11 tunnel from node:6000 to the root host
|
||||
display: display on root host (optional)
|
||||
returns: node $DISPLAY, Popen object for tunnel"""
|
||||
@@ -58,30 +28,11 @@ def tunnelX11( node, display=None ):
|
||||
quietRun( 'xhost +si:localuser:root' )
|
||||
return display, None
|
||||
else:
|
||||
hostname = quietRun( 'hostname' ).strip()
|
||||
port = 6000 + int( float( screen ) )
|
||||
if 'uts' in node.ns and ( hostname in display or
|
||||
'localhost' in display ):
|
||||
# Use private xauth file, and add credentials
|
||||
# for this hostname
|
||||
if not hasattr( node, 'xauthFile' ):
|
||||
node.xauthFile = NamedTemporaryFile()
|
||||
_display, proto, cookie = getAuthX11( display )
|
||||
creds = '%s %s %s' % ( '%s/unix:%s' % ( node.name, screen ),
|
||||
proto, cookie )
|
||||
node.cmd( 'export XAUTHORITY=' + node.xauthFile.name )
|
||||
node.cmd( 'xauth -f $XAUTHORITY add ' + creds )
|
||||
# Create a shared unix socket in /tmp
|
||||
# This can conflict if we are running nested Mininet
|
||||
# in a pid namespace, and it will also fail if /tmp is not
|
||||
# shared
|
||||
socket = '/tmp/mininet.x11.%d' % getpid()
|
||||
if not hasattr( tunnelX11, 'socket' ):
|
||||
cmd = ( 'socat unix-listen:%s,fork tcp:localhost:%d' %
|
||||
( socket, port ) ).split()
|
||||
tunnelX11.socket = Popen( cmd )
|
||||
# Create a tunnel for the TCP connection
|
||||
cmd = 'socat tcp-listen:%d,fork,reuseaddr unix:%s' % ( port, socket )
|
||||
port = 6000 + int( float( screen ) )
|
||||
connection = r'TCP\:%s\:%s' % ( host, port )
|
||||
cmd = [ "socat", "TCP-LISTEN:%d,fork,reuseaddr" % port,
|
||||
"EXEC:'mnexec -a 1 socat STDIO %s'" % connection ]
|
||||
return 'localhost:' + screen, node.popen( cmd )
|
||||
|
||||
def makeTerm( node, title='Node', term='xterm', display=None, cmd='bash'):
|
||||
@@ -94,8 +45,8 @@ def makeTerm( node, title='Node', term='xterm', display=None, cmd='bash'):
|
||||
if not node.inNamespace:
|
||||
title += ' (root)'
|
||||
cmds = {
|
||||
'xterm': [ 'xterm', '-title', title ],
|
||||
'gterm': [ 'gnome-terminal', '--title', title ]
|
||||
'xterm': [ 'xterm', '-title', title, '-display' ],
|
||||
'gterm': [ 'gnome-terminal', '--title', title, '--display' ]
|
||||
}
|
||||
if term not in cmds:
|
||||
error( 'invalid terminal type: %s' % term )
|
||||
@@ -103,10 +54,8 @@ def makeTerm( node, title='Node', term='xterm', display=None, cmd='bash'):
|
||||
display, tunnel = tunnelX11( node, display )
|
||||
if display is None:
|
||||
return []
|
||||
env = [ 'env', 'TERM=ansi', 'DISPLAY=%s' % display ]
|
||||
if hasattr( node, 'xauthFile' ):
|
||||
env += [ 'XAUTHORITY=%s' % node.xauthFile.name ]
|
||||
term = node.popen( env + cmds[ term ] + [ '-e', cmd ] )
|
||||
term = node.popen( cmds[ term ] +
|
||||
[ display, '-e', 'env TERM=ansi %s' % cmd ] )
|
||||
return [ tunnel, term ] if tunnel else [ term ]
|
||||
|
||||
def runX11( node, cmd ):
|
||||
@@ -119,7 +68,7 @@ def runX11( node, cmd ):
|
||||
|
||||
def cleanUpScreens():
|
||||
"Remove moldy socat X11 tunnels."
|
||||
errRun( "pkill -9 -f socat.*mininet" )
|
||||
errRun( "pkill -9 -f mnexec.*socat" )
|
||||
|
||||
def makeTerms( nodes, title='Node', term='xterm' ):
|
||||
"""Create terminals.
|
||||
|
||||
@@ -30,10 +30,10 @@ class TestSwitchDpidAssignmentOVS( unittest.TestCase ):
|
||||
def testDefaultDpid( self ):
|
||||
"""Verify that the default dpid is assigned using a valid provided
|
||||
canonical switchname if no dpid is passed in switch creation."""
|
||||
switch = Mininet( Topo(),
|
||||
self.switchClass,
|
||||
Host, Controller ).addSwitch( 's1' )
|
||||
net = Mininet( Topo(), self.switchClass, Host, Controller )
|
||||
switch = net.addSwitch( 's1' )
|
||||
self.assertEqual( switch.defaultDpid(), switch.dpid )
|
||||
net.stop()
|
||||
|
||||
def dpidFrom( self, num ):
|
||||
"Compute default dpid from number"
|
||||
@@ -44,31 +44,34 @@ class TestSwitchDpidAssignmentOVS( unittest.TestCase ):
|
||||
"""Verify that Switch dpid is the actual dpid assigned if dpid is
|
||||
passed in switch creation."""
|
||||
dpid = self.dpidFrom( 0xABCD )
|
||||
switch = Mininet( Topo(), self.switchClass,
|
||||
Host, Controller ).addSwitch(
|
||||
's1', dpid=dpid )
|
||||
net = Mininet( Topo(), self.switchClass, Host, Controller )
|
||||
switch = net.addSwitch( 's1', dpid=dpid )
|
||||
self.assertEqual( switch.dpid, dpid )
|
||||
net.stop()
|
||||
|
||||
def testDefaultDpidAssignmentFailure( self ):
|
||||
"""Verify that Default dpid assignment raises an Exception if the
|
||||
name of the switch does not contin a digit. Also verify the
|
||||
exception message."""
|
||||
net = Mininet( Topo(), self.switchClass, Host, Controller )
|
||||
with self.assertRaises( Exception ) as raises_cm:
|
||||
Mininet( Topo(), self.switchClass,
|
||||
Host, Controller ).addSwitch( 'A' )
|
||||
self.assertEqual(raises_cm.exception.message, 'Unable to derive '
|
||||
net.addSwitch( 'A' )
|
||||
self.assertTrue( 'Unable to derive '
|
||||
'default datapath ID - please either specify a dpid '
|
||||
'or use a canonical switch name such as s23.')
|
||||
'or use a canonical switch name such as s23.'
|
||||
in str( raises_cm.exception ) )
|
||||
net.stop()
|
||||
|
||||
def testDefaultDpidLen( self ):
|
||||
"""Verify that Default dpid length is 16 characters consisting of
|
||||
16 - len(hex of first string of contiguous digits passed in switch
|
||||
name) 0's followed by hex of first string of contiguous digits passed
|
||||
in switch name."""
|
||||
switch = Mininet( Topo(), self.switchClass,
|
||||
Host, Controller ).addSwitch( 's123' )
|
||||
|
||||
net = Mininet( Topo(), self.switchClass, Host, Controller )
|
||||
switch = net.addSwitch( 's123' )
|
||||
self.assertEqual( switch.dpid, self.dpidFrom( 123 ) )
|
||||
net.stop()
|
||||
|
||||
|
||||
class OVSUser( OVSSwitch):
|
||||
"OVS User Switch convenience class"
|
||||
@@ -95,3 +98,4 @@ class testSwitchUserspace( TestSwitchDpidAssignmentOVS ):
|
||||
if __name__ == '__main__':
|
||||
setLogLevel( 'warning' )
|
||||
unittest.main()
|
||||
cleanup()
|
||||
|
||||
@@ -7,13 +7,13 @@ TODO: missing xterm test
|
||||
"""
|
||||
|
||||
import unittest
|
||||
import pexpect
|
||||
import os
|
||||
import re
|
||||
from mininet.util import quietRun
|
||||
from mininet.util import quietRun, pexpect
|
||||
from distutils.version import StrictVersion
|
||||
from time import sleep
|
||||
|
||||
|
||||
def tsharkVersion():
|
||||
"Return tshark version"
|
||||
versionStr = quietRun( 'tshark -v' )
|
||||
@@ -95,7 +95,8 @@ class testWalkthrough( unittest.TestCase ):
|
||||
p = pexpect.spawn( 'mn' )
|
||||
p.expect( self.prompt )
|
||||
# Third pattern is a local interface beginning with 'eth' or 'en'
|
||||
interfaces = [ 'h1-eth0', 's1-eth1', r'[^-](eth|en)\w*\d', 'lo',
|
||||
interfaces = [ r'h1-eth0[:\s]', r's1-eth1[:\s]',
|
||||
r'[^-](eth|en)\w*\d[:\s]', r'lo[:\s]',
|
||||
self.prompt ]
|
||||
# h1 ifconfig
|
||||
p.sendline( 'h1 ifconfig -a' )
|
||||
@@ -122,7 +123,7 @@ class testWalkthrough( unittest.TestCase ):
|
||||
ifcount += 1
|
||||
else:
|
||||
break
|
||||
self.assertTrue( ifcount >= 3, 'Missing interfaces on s1')
|
||||
self.assertTrue( ifcount <= 3, 'Missing interfaces on s1')
|
||||
# h1 ps
|
||||
p.sendline( "h1 ps -a | egrep -v 'ps|grep'" )
|
||||
p.expect( self.prompt )
|
||||
@@ -156,9 +157,13 @@ class testWalkthrough( unittest.TestCase ):
|
||||
|
||||
def testSimpleHTTP( self ):
|
||||
"Start an HTTP server on h1 and wget from h2"
|
||||
if 'Python 2' in quietRun( 'python --version' ):
|
||||
httpserver = 'SimpleHTTPServer'
|
||||
else:
|
||||
httpserver = 'http.server'
|
||||
p = pexpect.spawn( 'mn' )
|
||||
p.expect( self.prompt )
|
||||
p.sendline( 'h1 python -m SimpleHTTPServer 80 &' )
|
||||
p.sendline( 'h1 python -m %s 80 &' % httpserver )
|
||||
# The walkthrough doesn't specify a delay here, and
|
||||
# we also don't read the output (also a possible problem),
|
||||
# but for now let's wait a couple of seconds to make
|
||||
@@ -222,8 +227,8 @@ class testWalkthrough( unittest.TestCase ):
|
||||
p.expect( r'rtt min/avg/max/mdev = '
|
||||
r'([\d\.]+)/([\d\.]+)/([\d\.]+)/([\d\.]+) ms' )
|
||||
delay = float( p.match.group( 2 ) )
|
||||
self.assertTrue( delay > 40, 'Delay < 40ms' )
|
||||
self.assertTrue( delay < 45, 'Delay > 40ms' )
|
||||
self.assertTrue( delay >= 40, 'Delay < 40ms' )
|
||||
self.assertTrue( delay <= 50, 'Delay > 50s' )
|
||||
p.expect( self.prompt )
|
||||
p.sendline( 'exit' )
|
||||
p.wait()
|
||||
@@ -260,7 +265,7 @@ class testWalkthrough( unittest.TestCase ):
|
||||
p.expect( self.prompt )
|
||||
for i in range( 1, 3 ):
|
||||
p.sendline( 'h%d ifconfig' % i )
|
||||
p.expect( 'HWaddr 00:00:00:00:00:0%d' % i )
|
||||
p.expect( r'\s00:00:00:00:00:0%d\s' % i )
|
||||
p.expect( self.prompt )
|
||||
p.sendline( 'exit' )
|
||||
p.expect( pexpect.EOF )
|
||||
@@ -286,7 +291,9 @@ class testWalkthrough( unittest.TestCase ):
|
||||
"Test running user switch in its own namespace"
|
||||
p = pexpect.spawn( 'mn --innamespace --switch user' )
|
||||
p.expect( self.prompt )
|
||||
interfaces = [ 'h1-eth0', 's1-eth1', '[^-]eth0', 'lo', self.prompt ]
|
||||
interfaces = [ r'h1-eth0[:\s]', r's1-eth1[:\s]',
|
||||
r'[^-](eth|en)\w*\d[:\s]', r'lo[:\s]',
|
||||
self.prompt ]
|
||||
p.sendline( 's1 ifconfig -a' )
|
||||
ifcount = 0
|
||||
while True:
|
||||
|
||||
+4
-4
@@ -56,13 +56,13 @@ class MultiGraph( object ):
|
||||
return self.node.items() if data else self.node.keys()
|
||||
|
||||
def edges_iter( self, data=False, keys=False ):
|
||||
"Iterator: return graph edges"
|
||||
for src, entry in self.edge.iteritems():
|
||||
for dst, keys in entry.iteritems():
|
||||
"Iterator: return graph edges, optionally with data and keys"
|
||||
for src, entry in self.edge.items():
|
||||
for dst, entrykeys in entry.items():
|
||||
if src > dst:
|
||||
# Skip duplicate edges
|
||||
continue
|
||||
for k, attrs in keys.iteritems():
|
||||
for k, attrs in entrykeys.items():
|
||||
if data:
|
||||
if keys:
|
||||
yield( src, dst, k, attrs )
|
||||
|
||||
+10
-33
@@ -45,11 +45,10 @@ class TorusTopo( Topo ):
|
||||
without STP turned on! It can be used with STP, e.g.:
|
||||
# mn --topo torus,3,3 --switch lxbr,stp=1 --test pingall"""
|
||||
|
||||
def build( self, x, y, n=1, wrap=True ):
|
||||
"""x: number of switches per row
|
||||
y: number of rows
|
||||
n: number of hosts per switch
|
||||
wrap: torus rather than grid? (True)"""
|
||||
def build( self, x, y, n=1 ):
|
||||
"""x: dimension of torus in x-direction
|
||||
y: dimension of torus in y-direction
|
||||
n: number of hosts per switch"""
|
||||
if x < 3 or y < 3:
|
||||
raise Exception( 'Please use 3x3 or greater for compatibility '
|
||||
'with 2.1' )
|
||||
@@ -73,33 +72,11 @@ class TorusTopo( Topo ):
|
||||
self.addLink( host, switch )
|
||||
# Connect switches
|
||||
for i in range( 0, x ):
|
||||
for j in range( 0, y):
|
||||
sw = switches[ i, j ]
|
||||
right = switches[ ( i + 1 ) % x, j ]
|
||||
down = switches[ i, ( j + 1 ) % y ]
|
||||
if wrap or i + 1 < x:
|
||||
self.addLink( sw, right )
|
||||
if wrap or j + 1 < y:
|
||||
self.addLink( sw, down )
|
||||
|
||||
|
||||
class GridTopo( TorusTopo ):
|
||||
"""2-D Grid topology
|
||||
WARNING: this topology has LOOPS and WILL NOT WORK
|
||||
with the default controller or any Ethernet bridge
|
||||
without STP turned on! It can be used with STP, e.g.:
|
||||
# mn --topo grid,3,3 --switch lxbr,stp=1 --test pingall"""
|
||||
|
||||
def build( self, x, y, n=1, wrap=False ):
|
||||
"""x: number of switches per row
|
||||
y: number of rows
|
||||
n: number of hosts per switch
|
||||
wrap: torus rather than grid (False)"""
|
||||
super( GridTopo, self ).build( x, y, n, wrap )
|
||||
|
||||
|
||||
topos = { 'tree': TreeTopo,
|
||||
'torus': TorusTopo,
|
||||
'grid': GridTopo }
|
||||
for j in range( 0, y ):
|
||||
sw1 = switches[ i, j ]
|
||||
sw2 = switches[ i, ( j + 1 ) % y ]
|
||||
sw3 = switches[ ( i + 1 ) % x, j ]
|
||||
self.addLink( sw1, sw2 )
|
||||
self.addLink( sw1, sw3 )
|
||||
|
||||
# pylint: enable=arguments-differ
|
||||
|
||||
+70
-49
@@ -12,6 +12,36 @@ from fcntl import fcntl, F_GETFL, F_SETFL
|
||||
from os import O_NONBLOCK
|
||||
import os
|
||||
from functools import partial
|
||||
import sys
|
||||
|
||||
# Python 2/3 compatibility
|
||||
Python3 = sys.version_info[0] == 3
|
||||
BaseString = str if Python3 else getattr( str, '__base__' )
|
||||
Encoding = 'utf-8' if Python3 else None
|
||||
def decode( s ):
|
||||
"Decode a byte string if needed for Python 3"
|
||||
return s.decode( Encoding ) if Python3 else s
|
||||
def encode( s ):
|
||||
"Encode a byte string if needed for Python 3"
|
||||
return s.encode( Encoding ) if Python3 else s
|
||||
try:
|
||||
import pexpect as oldpexpect
|
||||
|
||||
class Pexpect( object ):
|
||||
"Custom pexpect that is compatible with str"
|
||||
@staticmethod
|
||||
def spawn( *args, **kwargs):
|
||||
"pexpect.spawn that is compatible with str"
|
||||
if Python3 and 'encoding' not in kwargs:
|
||||
kwargs.update( encoding='utf-8' )
|
||||
return oldpexpect.spawn( *args, **kwargs )
|
||||
|
||||
def __getattr__( self, name ):
|
||||
return getattr( oldpexpect, name )
|
||||
pexpect = Pexpect()
|
||||
except ImportError:
|
||||
pass
|
||||
|
||||
|
||||
# Command execution support
|
||||
|
||||
@@ -57,7 +87,7 @@ def oldQuietRun( *cmd ):
|
||||
# This is a bit complicated, but it enables us to
|
||||
# monitor command output as it is happening
|
||||
|
||||
# pylint: disable=too-many-branches
|
||||
# pylint: disable=too-many-branches,too-many-statements
|
||||
def errRun( *cmd, **kwargs ):
|
||||
"""Run a command and return stdout, stderr and return code
|
||||
cmd: string or list of command and args
|
||||
@@ -98,6 +128,8 @@ def errRun( *cmd, **kwargs ):
|
||||
f = fdtofile[ fd ]
|
||||
if event & POLLIN:
|
||||
data = f.read( 1024 )
|
||||
if Python3:
|
||||
data = data.decode( Encoding )
|
||||
if echo:
|
||||
output( data )
|
||||
if f == popen.stdout:
|
||||
@@ -116,6 +148,10 @@ def errRun( *cmd, **kwargs ):
|
||||
poller.unregister( fd )
|
||||
|
||||
returncode = popen.wait()
|
||||
# Python 3 complains if we don't explicitly close these
|
||||
popen.stdout.close()
|
||||
if stderr == PIPE:
|
||||
popen.stderr.close()
|
||||
debug( out, err, returncode )
|
||||
return out, err, returncode
|
||||
# pylint: enable=too-many-branches
|
||||
@@ -178,19 +214,18 @@ def makeIntfPair( intf1, intf2, addr1=None, addr2=None, node1=None, node2=None,
|
||||
runCmd( 'ip link del ' + intf1 )
|
||||
runCmd2( 'ip link del ' + intf2 )
|
||||
# Create new pair
|
||||
ns1 = 1 if not node1 else node1.pid
|
||||
ns2 = 1 if not node2 else node2.pid
|
||||
netns = 1 if not node2 else node2.pid
|
||||
if addr1 is None and addr2 is None:
|
||||
cmdOutput = runCmd( 'ip link add name %s netns %s '
|
||||
cmdOutput = runCmd( 'ip link add name %s '
|
||||
'type veth peer name %s '
|
||||
'netns %s' % ( intf1, ns1, intf2, ns2 ) )
|
||||
'netns %s' % ( intf1, intf2, netns ) )
|
||||
else:
|
||||
cmdOutput = runCmd( 'ip link add name %s '
|
||||
'address %s netns %s '
|
||||
'address %s '
|
||||
'type veth peer name %s '
|
||||
'address %s '
|
||||
'netns %s' %
|
||||
( intf1, addr1, ns1, intf2, addr2, ns2 ) )
|
||||
( intf1, addr1, intf2, addr2, netns ) )
|
||||
if cmdOutput:
|
||||
raise Exception( "Error creating interface pair (%s,%s): %s " %
|
||||
( intf1, intf2, cmdOutput ) )
|
||||
@@ -375,7 +410,7 @@ def pmonitor(popens, timeoutms=500, readline=True,
|
||||
terminates: when all EOFs received"""
|
||||
poller = poll()
|
||||
fdToHost = {}
|
||||
for host, popen in popens.iteritems():
|
||||
for host, popen in popens.items():
|
||||
fd = popen.stdout.fileno()
|
||||
fdToHost[ fd ] = host
|
||||
poller.register( fd, POLLIN )
|
||||
@@ -383,6 +418,13 @@ def pmonitor(popens, timeoutms=500, readline=True,
|
||||
# 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 )
|
||||
|
||||
while popens:
|
||||
fds = poller.poll( timeoutms )
|
||||
if fds:
|
||||
@@ -390,15 +432,15 @@ def pmonitor(popens, timeoutms=500, readline=True,
|
||||
host = fdToHost[ fd ]
|
||||
popen = popens[ host ]
|
||||
if event & POLLIN:
|
||||
if readline:
|
||||
# Attempt to read a line of output
|
||||
# This blocks until we receive a newline!
|
||||
line = popen.stdout.readline()
|
||||
else:
|
||||
line = popen.stdout.read( readmax )
|
||||
line = readit( popen.stdout )
|
||||
yield host, line
|
||||
# Check for EOF
|
||||
elif event & POLLHUP:
|
||||
if event & POLLHUP:
|
||||
while True:
|
||||
# Drain buffer
|
||||
line = readit( popen.stdout )
|
||||
yield host, line
|
||||
if line == '':
|
||||
break
|
||||
poller.unregister( fd )
|
||||
del popens[ host ]
|
||||
else:
|
||||
@@ -461,7 +503,7 @@ def fixLimits():
|
||||
|
||||
def mountCgroups():
|
||||
"Make sure cgroups file system is mounted"
|
||||
mounts = quietRun( 'cat /proc/mounts' )
|
||||
mounts = quietRun( 'grep cgroup /proc/mounts' )
|
||||
cgdir = '/sys/fs/cgroup'
|
||||
csdir = cgdir + '/cpuset'
|
||||
if ('cgroup %s' % cgdir not in mounts and
|
||||
@@ -509,42 +551,21 @@ def custom( cls, **params ):
|
||||
customized.__name__ = 'custom(%s,%s)' % ( cls, params )
|
||||
return customized
|
||||
|
||||
|
||||
|
||||
def parseArgs( argstr ):
|
||||
"""Parse argument string
|
||||
returns args, kwargs"""
|
||||
# One step at a time: support param=[a,b,c]
|
||||
paramre = r'\[[^\]]*\]|[^,\[\]]+'
|
||||
paramre = r'\w+=\[[^\]]*\]|\w+\[^,\[\]]+' + paramre
|
||||
params = re.findall( paramre, argstr )
|
||||
# Parse lists
|
||||
for i, arg in enumerate( params ):
|
||||
if arg.startswith( '[' ) and arg.endswith( ']' ):
|
||||
arg = arg[ 1 : -1 ]
|
||||
print "recurse on", arg
|
||||
params[ i ] = parseArgs( args )
|
||||
def splitArgs( argstr ):
|
||||
"""Split argument string into usable python arguments
|
||||
argstr: argument string with format fn,arg2,kw1=arg3...
|
||||
returns: fn, args, kwargs"""
|
||||
split = argstr.split( ',' )
|
||||
fn = split[ 0 ]
|
||||
params = split[ 1: ]
|
||||
# Convert int and float args; removes the need for function
|
||||
# to be flexible with input arg formats.
|
||||
args = [ makeNumeric( s ) for s in params if '=' not in s ]
|
||||
kwargs = {}
|
||||
for s in [ p for p in params if '=' in p ]:
|
||||
key, arg = s.split( '=', 1 )
|
||||
if arg.startswith( '[' ) and arg.endswith( ']' ):
|
||||
arg = arg[ 1 : -1 ]
|
||||
print 'recurse on', arg
|
||||
arg = parseArgs( arg )[ 0 ]
|
||||
else:
|
||||
arg = makeNumeric( arg )
|
||||
kwargs[ key ] = arg
|
||||
return args, kwargs
|
||||
|
||||
def splitArgs( argstr ):
|
||||
"""Split argument string into usable python arguments
|
||||
argstr: argument string with format fn,arg2,kw1=arg3...
|
||||
returns: fn, args, kwargs"""
|
||||
args, kwargs = parseArgs( argstr )
|
||||
return args[ 0 ], args[ 1: ], kwargs
|
||||
key, val = s.split( '=', 1 )
|
||||
kwargs[ key ] = makeNumeric( val )
|
||||
return fn, args, kwargs
|
||||
|
||||
def customClass( classes, argStr ):
|
||||
"""Return customized class based on argStr
|
||||
@@ -622,7 +643,7 @@ def waitListening( client=None, server='127.0.0.1', port=80, timeout=None ):
|
||||
if not runCmd( 'which telnet' ):
|
||||
raise Exception('Could not find telnet' )
|
||||
# pylint: disable=maybe-no-member
|
||||
serverIP = server if isinstance( server, basestring ) else server.IP()
|
||||
serverIP = server if isinstance( server, BaseString ) else server.IP()
|
||||
cmd = ( 'echo A | telnet -e A %s %s' % ( serverIP, port ) )
|
||||
time = 0
|
||||
result = runCmd( cmd )
|
||||
|
||||
@@ -5,9 +5,9 @@
|
||||
*
|
||||
* - closing all file descriptors except stdin/out/error
|
||||
* - detaching from a controlling tty using setsid
|
||||
* - running in network and other namespaces
|
||||
* - running in network and mount namespaces
|
||||
* - printing out the pid of a process so we can identify it later
|
||||
* - attaching to namespace(s) and cgroup
|
||||
* - attaching to a namespace and cgroup
|
||||
* - setting RT scheduling
|
||||
*
|
||||
* Partially based on public domain setsid(1)
|
||||
@@ -24,29 +24,21 @@
|
||||
#include <sched.h>
|
||||
#include <ctype.h>
|
||||
#include <sys/mount.h>
|
||||
#include <sys/wait.h>
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#if !defined(VERSION)
|
||||
#define VERSION "(devel)"
|
||||
#endif
|
||||
|
||||
|
||||
void usage(char *name)
|
||||
{
|
||||
printf("Execution utility for Mininet\n\n"
|
||||
"Usage: %s [-cdmnPpu] [-a pid] [-g group] [-r rtprio] cmd args...\n\n"
|
||||
"Usage: %s [-cdnp] [-a pid] [-g group] [-r rtprio] cmd args...\n\n"
|
||||
"Options:\n"
|
||||
" -c: close all file descriptors except stdin/out/error\n"
|
||||
" -d: detach from tty by calling setsid()\n"
|
||||
" -m: run in a new mount namespace\n"
|
||||
" -n: run in a new network namespace\n"
|
||||
" -P: run in a new pid namespace (implies -m)\n"
|
||||
" -u: run in a new UTS (ipc, hostname) namespace\n"
|
||||
" -n: run in new network and mount namespaces\n"
|
||||
" -p: print ^A + pid\n"
|
||||
" -a pid: attach to pid's namespaces\n"
|
||||
" -a pid: attach to pid's network and mount namespaces\n"
|
||||
" -g group: add to cgroup\n"
|
||||
" -r rtprio: run with SCHED_RR (usually requires -g)\n"
|
||||
" -v: print version\n",
|
||||
@@ -54,13 +46,10 @@ void usage(char *name)
|
||||
}
|
||||
|
||||
|
||||
#if !defined(setns)
|
||||
int setns(int fd, int nstype)
|
||||
{
|
||||
return syscall(__NR_setns, fd, nstype);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
/* Validate alphanumeric path foo1/bar2/baz */
|
||||
void validate(char *path)
|
||||
@@ -74,7 +63,6 @@ void validate(char *path)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* Add our pid to cgroup */
|
||||
void cgroup(char *gname)
|
||||
{
|
||||
@@ -104,193 +92,117 @@ void cgroup(char *gname)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* Attach to ns 'name' if present; return 1 if pidns */
|
||||
int attachns( pid_t pid, char *name ) {
|
||||
char path[PATH_MAX];
|
||||
int nsid, err;
|
||||
int pidns = 0;
|
||||
|
||||
snprintf(path, PATH_MAX, "/proc/%d/ns/%s", pid, name) ;
|
||||
|
||||
if ((nsid = open(path, O_RDONLY)) < 0)
|
||||
return nsid;
|
||||
|
||||
if (strcmp(name, "pid") == 0) {
|
||||
struct stat buf1, buf2;
|
||||
int stat1 = stat(path, &buf1);
|
||||
int stat2 = stat("/proc/self/ns/pid", &buf2);
|
||||
/* Don't reattach to the same pid ns */
|
||||
if (stat1 == 0 && stat2 == 0 &&
|
||||
buf1.st_dev == buf2.st_dev &&
|
||||
buf1.st_ino == buf2.st_ino)
|
||||
return 0;
|
||||
pidns = 1;
|
||||
}
|
||||
|
||||
if ((err = setns(nsid, 0)) < 0) {
|
||||
perror("setns");
|
||||
return err;
|
||||
}
|
||||
|
||||
return pidns;
|
||||
}
|
||||
|
||||
/* Attach to pid's namespaces - returns 1 if pidns */
|
||||
int attach(int pid) {
|
||||
|
||||
char *cwd = get_current_dir_name();
|
||||
char path[PATH_MAX];
|
||||
int pidns = 0;
|
||||
|
||||
attachns(pid, "net");
|
||||
attachns(pid, "uts");
|
||||
if (attachns(pid, "pid") == 1)
|
||||
pidns = 1;
|
||||
|
||||
if (attachns(pid, "mnt") != 0) {
|
||||
/* Plan B: chroot into pid's root file system */
|
||||
sprintf(path, "/proc/%d/root", pid);
|
||||
if (chroot(path) < 0) {
|
||||
perror(path);
|
||||
}
|
||||
}
|
||||
|
||||
/* chdir to correct working directory */
|
||||
if (chdir(cwd) != 0)
|
||||
perror(cwd);
|
||||
|
||||
return pidns;
|
||||
}
|
||||
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
int c;
|
||||
int fd;
|
||||
char path[PATH_MAX];
|
||||
int nsid;
|
||||
int pid;
|
||||
char *cwd = get_current_dir_name();
|
||||
|
||||
/* Argument flags */
|
||||
int flags = 0;
|
||||
int closefds = 0;
|
||||
int attachpid = 0;
|
||||
char *cgrouparg = NULL;
|
||||
int detachtty = 0;
|
||||
int printpid = 0;
|
||||
int rtprio = 0;
|
||||
int pidns = 0;
|
||||
int dofork = 0;
|
||||
|
||||
while ((c = getopt(argc, argv, "+cdmnPpa:g:r:uvh")) != -1)
|
||||
static struct sched_param sp;
|
||||
while ((c = getopt(argc, argv, "+cdnpa:g:r:vh")) != -1)
|
||||
switch(c) {
|
||||
case 'c': closefds = 1; break;
|
||||
case 'd': detachtty = 1; break;
|
||||
case 'm': flags |= CLONE_NEWNS; break;
|
||||
case 'n': flags |= CLONE_NEWNET; break;
|
||||
case 'p': printpid = 1; break;
|
||||
case 'P': flags |= CLONE_NEWPID | CLONE_NEWNS; break;
|
||||
case 'a': attachpid = atoi(optarg);break;
|
||||
case 'g': cgrouparg = optarg ; break;
|
||||
case 'r': rtprio = atoi(optarg); break;
|
||||
case 'u': flags |= CLONE_NEWUTS; break;
|
||||
case 'v': printf("%s\n", VERSION); exit(0);
|
||||
case 'h': usage(argv[0]); exit(0);
|
||||
default: usage(argv[0]); exit(1);
|
||||
}
|
||||
|
||||
if (closefds) {
|
||||
/* close file descriptors except stdin/out/error */
|
||||
int fd;
|
||||
for (fd = getdtablesize(); fd > 2; fd--) close(fd);
|
||||
}
|
||||
|
||||
if (attachpid)
|
||||
/* Attach to existing namespace(s) */
|
||||
pidns = attach(attachpid);
|
||||
else {
|
||||
/* Create new namespace(s) */
|
||||
if (unshare(flags) == -1) {
|
||||
perror("unshare");
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (flags & CLONE_NEWPID || pidns)
|
||||
/* pidns requires fork/wait; child will be pid 1 */
|
||||
dofork = 1;
|
||||
|
||||
if (detachtty && getpgrp() == getpid())
|
||||
/* Fork so that we will no longer be pgroup leader */
|
||||
dofork = 1;
|
||||
else
|
||||
/* We don't need a new session, only a new pgroup */
|
||||
detachtty = 0;
|
||||
|
||||
if (detachtty)
|
||||
/* Create a new session - and by implication a new process group */
|
||||
setsid();
|
||||
else
|
||||
/* Use a new process group (in the current session)
|
||||
* so Mininet can use killpg without unintended effects */
|
||||
setpgid(0, 0);
|
||||
|
||||
if (dofork) {
|
||||
/* Fork and then wait if necessary */
|
||||
pid_t pid = fork();
|
||||
switch(pid) {
|
||||
int status;
|
||||
case -1:
|
||||
perror("fork");
|
||||
case 'c':
|
||||
/* close file descriptors except stdin/out/error */
|
||||
for (fd = getdtablesize(); fd > 2; fd--)
|
||||
close(fd);
|
||||
break;
|
||||
case 'd':
|
||||
/* detach from tty */
|
||||
if (getpgrp() == getpid()) {
|
||||
switch(fork()) {
|
||||
case -1:
|
||||
perror("fork");
|
||||
return 1;
|
||||
case 0: /* child */
|
||||
break;
|
||||
default: /* parent */
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
setsid();
|
||||
break;
|
||||
case 'n':
|
||||
/* run in network and mount namespaces */
|
||||
if (unshare(CLONE_NEWNET|CLONE_NEWNS) == -1) {
|
||||
perror("unshare");
|
||||
return 1;
|
||||
case 0:
|
||||
/* Child continues below */
|
||||
break;
|
||||
default:
|
||||
/* We print the *child pid* in *parent's pidns* if needed */
|
||||
if (printpid) {
|
||||
printf("\001%d\n", pid);
|
||||
fflush(stdout);
|
||||
}
|
||||
|
||||
/* Mark our whole hierarchy recursively as private, so that our
|
||||
* mounts do not propagate to other processes.
|
||||
*/
|
||||
|
||||
if (mount("none", "/", NULL, MS_REC|MS_PRIVATE, NULL) == -1) {
|
||||
perror("remount");
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* mount sysfs to pick up the new network namespace */
|
||||
if (mount("sysfs", "/sys", "sysfs", MS_MGC_VAL, NULL) == -1) {
|
||||
perror("mount");
|
||||
return 1;
|
||||
}
|
||||
break;
|
||||
case 'p':
|
||||
/* print pid */
|
||||
printf("\001%d\n", getpid());
|
||||
fflush(stdout);
|
||||
break;
|
||||
case 'a':
|
||||
/* Attach to pid's network namespace and mount namespace */
|
||||
pid = atoi(optarg);
|
||||
sprintf(path, "/proc/%d/ns/net", pid);
|
||||
nsid = open(path, O_RDONLY);
|
||||
if (nsid < 0) {
|
||||
perror(path);
|
||||
return 1;
|
||||
}
|
||||
if (setns(nsid, 0) != 0) {
|
||||
perror("setns");
|
||||
return 1;
|
||||
}
|
||||
/* Plan A: call setns() to attach to mount namespace */
|
||||
sprintf(path, "/proc/%d/ns/mnt", pid);
|
||||
nsid = open(path, O_RDONLY);
|
||||
if (nsid < 0 || setns(nsid, 0) != 0) {
|
||||
/* Plan B: chroot/chdir into pid's root file system */
|
||||
sprintf(path, "/proc/%d/root", pid);
|
||||
if (chroot(path) < 0) {
|
||||
perror(path);
|
||||
return 1;
|
||||
}
|
||||
/* For pid namespace, we need to fork and wait for child ;-( */
|
||||
if (flags & CLONE_NEWPID || pidns) {
|
||||
wait(&status);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
/* chdir to correct working directory */
|
||||
if (chdir(cwd) != 0) {
|
||||
perror(cwd);
|
||||
return 1;
|
||||
}
|
||||
break;
|
||||
case 'g':
|
||||
/* Attach to cgroup */
|
||||
cgroup(optarg);
|
||||
break;
|
||||
case 'r':
|
||||
/* Set RT scheduling priority */
|
||||
sp.sched_priority = atoi(optarg);
|
||||
if (sched_setscheduler(getpid(), SCHED_RR, &sp) < 0) {
|
||||
perror("sched_setscheduler");
|
||||
return 1;
|
||||
}
|
||||
break;
|
||||
case 'v':
|
||||
printf("%s\n", VERSION);
|
||||
exit(0);
|
||||
case 'h':
|
||||
usage(argv[0]);
|
||||
exit(0);
|
||||
default:
|
||||
usage(argv[0]);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
if (printpid && !dofork) {
|
||||
/* Print child pid if we didn't fork/aren't in a pidns */
|
||||
assert(!pidns);
|
||||
printf("\001%d\n", getpid());
|
||||
fflush(stdout);
|
||||
}
|
||||
|
||||
if (flags & CLONE_NEWPID) {
|
||||
/* Child remounts /proc for ps */
|
||||
if (mount("proc", "/proc", "proc", MS_MGC_VAL, NULL) == -1) {
|
||||
perror("mountproc");
|
||||
}
|
||||
}
|
||||
|
||||
/* Attach to cgroup if necessary */
|
||||
if (cgrouparg) cgroup(cgrouparg);
|
||||
|
||||
if (flags & CLONE_NEWNET & CLONE_NEWNS) {
|
||||
/* Mount sysfs to pick up the new network namespace */
|
||||
if (mount("sysfs", "/sys", "sysfs", MS_MGC_VAL, NULL) == -1) {
|
||||
perror("mount");
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (rtprio != 0) {
|
||||
/* Set RT scheduling priority */
|
||||
static struct sched_param sp;
|
||||
sp.sched_priority = atoi(optarg);
|
||||
if (sched_setscheduler(getpid(), SCHED_RR, &sp) < 0) {
|
||||
perror("sched_setscheduler");
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (optind < argc) {
|
||||
execvp(argv[optind], &argv[optind]);
|
||||
|
||||
+30
-24
@@ -102,6 +102,14 @@ function version_ge {
|
||||
[ "$1" == "$latest" ]
|
||||
}
|
||||
|
||||
# Attempt to identify Python version
|
||||
PYTHON=${PYTHON:-python}
|
||||
if $PYTHON --version |& grep 'Python 2' > /dev/null; then
|
||||
PYTHON_VERSION=2; PYPKG=python
|
||||
else
|
||||
PYTHON_VERSION=3; PYPKG=python3
|
||||
fi
|
||||
echo "${PYTHON} is version ${PYTHON_VERSION}"
|
||||
|
||||
# Kernel Deb pkg to be removed:
|
||||
KERNEL_IMAGE_OLD=linux-image-2.6.26-33-generic
|
||||
@@ -145,19 +153,20 @@ function mn_deps {
|
||||
$install gcc make socat psmisc xterm openssh-clients iperf \
|
||||
iproute telnet python-setuptools libcgroup-tools \
|
||||
ethtool help2man pyflakes pylint python-pep8 python-pexpect
|
||||
elif [ "$DIST" = "SUSE LINUX" ]; then
|
||||
elif [ "$DIST" = "SUSE LINUX" ]; then
|
||||
$install gcc make socat psmisc xterm openssh iperf \
|
||||
iproute telnet python-setuptools libcgroup-tools \
|
||||
ethtool help2man python-pyflakes python3-pylint python-pep8 python-pexpect
|
||||
else
|
||||
iproute telnet ${PYPKG}-setuptools libcgroup-tools \
|
||||
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 \
|
||||
python-setuptools cgroup-bin ethtool help2man \
|
||||
pyflakes pylint pep8 python-pexpect
|
||||
cgroup-bin ethtool help2man pyflakes pylint pep8 \
|
||||
${PYPKG}-setuptools ${PYPKG}-pexpect ${PYPKG}-tk
|
||||
fi
|
||||
|
||||
echo "Installing Mininet core"
|
||||
pushd $MININET_DIR/mininet
|
||||
sudo make install
|
||||
sudo PYTHON=${PYTHON} make install
|
||||
popd
|
||||
}
|
||||
|
||||
@@ -204,10 +213,14 @@ function of {
|
||||
function of13 {
|
||||
echo "Installing OpenFlow 1.3 soft switch implementation..."
|
||||
cd $BUILD_DIR/
|
||||
$install git-core autoconf automake autotools-dev pkg-config \
|
||||
make gcc g++ libtool libc6-dev cmake libpcap-dev libxerces-c2-dev \
|
||||
$install git-core autoconf automake autotools-dev pkg-config \
|
||||
make gcc g++ libtool libc6-dev cmake libpcap-dev \
|
||||
unzip libpcre3-dev flex bison libboost-dev
|
||||
|
||||
if [ "$DIST" = "Ubuntu" ] && version_le $RELEASE 16.04; then
|
||||
$install libxerces-c2-dev
|
||||
else
|
||||
$install libxerces-c-dev
|
||||
fi
|
||||
if [ ! -d "ofsoftswitch13" ]; then
|
||||
git clone https://github.com/CPqD/ofsoftswitch13.git
|
||||
if [[ -n "$OF13_SWITCH_REV" ]]; then
|
||||
@@ -218,24 +231,17 @@ function of13 {
|
||||
fi
|
||||
|
||||
# Install netbee
|
||||
if [ "$DIST" = "Ubuntu" ] && version_ge $RELEASE 14.04; then
|
||||
NBEESRC="nbeesrc-feb-24-2015"
|
||||
NBEEDIR="netbee"
|
||||
else
|
||||
NBEESRC="nbeesrc-jan-10-2013"
|
||||
NBEEDIR="nbeesrc-jan-10-2013"
|
||||
if [ ! -d "netbee" ]; then
|
||||
git clone https://github.com/netgroup-polito/netbee.git
|
||||
fi
|
||||
|
||||
NBEEURL=${NBEEURL:-http://www.nbee.org/download/}
|
||||
wget -nc ${NBEEURL}${NBEESRC}.zip
|
||||
unzip ${NBEESRC}.zip
|
||||
cd ${NBEEDIR}/src
|
||||
cd netbee/src
|
||||
cmake .
|
||||
make
|
||||
cd $BUILD_DIR/
|
||||
sudo cp ${NBEEDIR}/bin/libn*.so /usr/local/lib
|
||||
|
||||
cd $BUILD_DIR
|
||||
sudo cp netbee/bin/libn*.so /usr/local/lib
|
||||
sudo ldconfig
|
||||
sudo cp -R ${NBEEDIR}/include/ /usr/
|
||||
sudo cp -R netbee/include/ /usr/
|
||||
|
||||
# Resume the install:
|
||||
cd $BUILD_DIR/ofsoftswitch13
|
||||
|
||||
@@ -33,5 +33,12 @@ if [ -d "$cgroup" ]; then
|
||||
cg="-g $host"
|
||||
fi
|
||||
|
||||
cmd="exec sudo -E mnexec $cg -a $pid $cmd"
|
||||
# Check whether host should be running in a chroot dir
|
||||
rootdir="/var/run/mn/$host/root"
|
||||
if [ -d $rootdir -a -x $rootdir/bin/bash ]; then
|
||||
cmd="'cd `pwd`; exec $cmd'"
|
||||
cmd="chroot $rootdir /bin/bash -c $cmd"
|
||||
fi
|
||||
|
||||
cmd="exec sudo mnexec $cg -a $pid $cmd"
|
||||
eval $cmd
|
||||
|
||||
@@ -1,14 +1,19 @@
|
||||
#!/usr/bin/python
|
||||
|
||||
from subprocess import check_output as co
|
||||
from sys import exit
|
||||
from sys import exit, version_info
|
||||
|
||||
def run(*args, **kwargs):
|
||||
"Run co and decode for python3"
|
||||
result = co(*args, **kwargs)
|
||||
return result.decode() if version_info[ 0 ] >= 3 else result
|
||||
|
||||
# Actually run bin/mn rather than importing via python path
|
||||
version = 'Mininet ' + co( 'PYTHONPATH=. bin/mn --version 2>&1', shell=True )
|
||||
version = 'Mininet ' + run( 'PYTHONPATH=. bin/mn --version 2>&1', shell=True )
|
||||
version = version.strip()
|
||||
|
||||
# Find all Mininet path references
|
||||
lines = co( "egrep -or 'Mininet [0-9\.\+]+\w*' *", shell=True )
|
||||
lines = run( "egrep -or 'Mininet [0-9\.\+]+\w*' *", shell=True )
|
||||
|
||||
error = False
|
||||
|
||||
|
||||
Reference in New Issue
Block a user