Python 3 Compatibility (Merge pull request #817 from lantz/py3-compat)

Changes for compatibility with Python 3.

The approach is to make as few changes as possible to maintain compatibility with both Python 2 and Python 3.

We use whatever python is installed, and also support a PYTHON environment variable for installing another version.

For simplicity, we provide mininet.util.pexpect which works out of the box with Python 3 utf-8 strings.

Thanks to @cuihantao for looking at this as well and also for changes to MiniEdit.

Closes #794
This commit is contained in:
lantz
2018-07-26 14:03:34 -07:00
committed by GitHub
45 changed files with 260 additions and 151 deletions
+15 -11
View File
@@ -4,28 +4,32 @@ sudo: required
matrix: matrix:
include: include:
- dist: trusty - dist: trusty
python: 2.7
env: dist="14.04 LTS trusty"
- dist: trusty
python: 3.6
env: dist="14.04 LTS trusty" env: dist="14.04 LTS trusty"
# - dist: xenial
# env: dist="16.04 LTS xenial"
# Travis-CI only proposes 14.04 LTS Trusty and there is no plan to update to 16.04 xenial
# (c.f. https://github.com/travis-ci/travis-ci/issues/5821)
# It is useless to add a second job because it will run in the same Ubuntu version (14.04)
before_install: before_install:
- sudo apt-get update -qq - sudo apt-get update -qq
- sudo apt-get install -qq vlan - sudo apt-get install -qq vlan
- sudo util/install.sh -n - PYTHON=`which python` util/install.sh -n
install: install:
- bash -c "if [ `lsb_release -rs` == '14.04' ]; then make codecheck; fi" - bash -c "if [ `lsb_release -rs` == '14.04' ]; then make codecheck; fi"
- sudo util/install.sh -fnvw - pip install pexpect || pip3 install pexpect
- util/install.sh -nfvw
script: script:
- sudo mn --test pingall - alias sudo="sudo env PATH=$PATH"
- sudo python mininet/test/runner.py -v -quick - export PYTHON=`which python`
- sudo python examples/test/runner.py -v -quick - echo 'px import sys; print(sys.version_info)' | sudo $PYTHON bin/mn -v output
- sudo $PYTHON bin/mn --test pingall
- sudo $PYTHON mininet/test/runner.py -v -quick
- sudo $PYTHON examples/test/runner.py -v -quick
notifications: notifications:
email: email:
on_success: never on_success: never
# More details: https://docs.travis-ci.com/user/notifications#Configuring-email-notifications
# More details: https://docs.travis-ci.com/user/notifications
+4 -3
View File
@@ -2,7 +2,8 @@ MININET = mininet/*.py
TEST = mininet/test/*.py TEST = mininet/test/*.py
EXAMPLES = mininet/examples/*.py EXAMPLES = mininet/examples/*.py
MN = bin/mn MN = bin/mn
PYMN = python -B bin/mn PYTHON ?= python
PYMN = $(PYTHON) -B bin/mn
BIN = $(MN) BIN = $(MN)
PYSRC = $(MININET) $(TEST) $(EXAMPLES) $(BIN) PYSRC = $(MININET) $(TEST) $(EXAMPLES) $(BIN)
MNEXEC = mnexec MNEXEC = mnexec
@@ -54,13 +55,13 @@ install-manpages: $(MANPAGES)
install -D -t $(MANDIR) $(MANPAGES) install -D -t $(MANDIR) $(MANPAGES)
install: install-mnexec install-manpages install: install-mnexec install-manpages
python setup.py install $(PYTHON) setup.py install
develop: $(MNEXEC) $(MANPAGES) develop: $(MNEXEC) $(MANPAGES)
# Perhaps we should link these as well # Perhaps we should link these as well
install $(MNEXEC) $(BINDIR) install $(MNEXEC) $(BINDIR)
install $(MANPAGES) $(MANDIR) install $(MANPAGES) $(MANDIR)
python setup.py develop $(PYTHON) setup.py develop
man: $(MANPAGES) man: $(MANPAGES)
+6 -4
View File
@@ -186,8 +186,10 @@ class MininetRunner( object ):
for fileName in files: for fileName in files:
customs = {} customs = {}
if os.path.isfile( fileName ): if os.path.isfile( fileName ):
execfile( fileName, customs, customs ) # pylint: disable=exec-used
for name, val in customs.iteritems(): exec( compile( open( fileName ).read(), fileName, 'exec' ),
customs, customs )
for name, val in customs.items():
self.setCustom( name, val ) self.setCustom( name, val )
else: else:
raise Exception( 'could not find custom file: %s' % fileName ) raise Exception( 'could not find custom file: %s' % fileName )
@@ -256,7 +258,7 @@ class MininetRunner( object ):
opts.add_option( '--arp', action='store_true', opts.add_option( '--arp', action='store_true',
default=False, help='set all-pairs ARP entries' ) default=False, help='set all-pairs ARP entries' )
opts.add_option( '--verbosity', '-v', type='choice', opts.add_option( '--verbosity', '-v', type='choice',
choices=LEVELS.keys(), default = 'info', choices=list( LEVELS.keys() ), default = 'info',
help = '|'.join( LEVELS.keys() ) ) help = '|'.join( LEVELS.keys() ) )
opts.add_option( '--innamespace', action='store_true', opts.add_option( '--innamespace', action='store_true',
default=False, help='sw and ctrl in namespace?' ) default=False, help='sw and ctrl in namespace?' )
@@ -286,7 +288,7 @@ class MininetRunner( object ):
metavar='server1,server2...', metavar='server1,server2...',
help=( 'run on multiple servers (experimental!)' ) ) help=( 'run on multiple servers (experimental!)' ) )
opts.add_option( '--placement', type='choice', opts.add_option( '--placement', type='choice',
choices=PLACEMENT.keys(), default='block', choices=list( PLACEMENT.keys() ), default='block',
metavar='block|random', metavar='block|random',
help=( 'node placement for --cluster ' help=( 'node placement for --cluster '
'(experimental!) ' ) ) '(experimental!) ' ) )
+1 -1
View File
@@ -126,7 +126,7 @@ class ClusterCleanup( object ):
def cleanup( cls ): def cleanup( cls ):
"Clean up" "Clean up"
info( '*** Cleaning up cluster\n' ) info( '*** Cleaning up cluster\n' )
for server, user in cls.serveruser.iteritems(): for server, user in cls.serveruser.items():
if server == 'localhost': if server == 'localhost':
# Handled by mininet.clean.cleanup() # Handled by mininet.clean.cleanup()
continue continue
+1 -1
View File
@@ -49,7 +49,7 @@ class MininetFacade( object ):
args: unnamed networks passed as arguments args: unnamed networks passed as arguments
kwargs: named networks passed as arguments""" kwargs: named networks passed as arguments"""
self.net = net self.net = net
self.nets = [ net ] + list( args ) + kwargs.values() self.nets = [ net ] + list( args ) + list( kwargs.values() )
self.nameToNet = kwargs self.nameToNet = kwargs
self.nameToNet['net'] = net self.nameToNet['net'] = net
+5 -4
View File
@@ -31,9 +31,9 @@ rate includes buffering.
from mininet.net import Mininet from mininet.net import Mininet
from mininet.node import CPULimitedHost from mininet.node import CPULimitedHost
from mininet.topolib import TreeTopo 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.log import setLogLevel, info
from mininet.clean import cleanup
def bwtest( cpuLimits, period_us=100000, seconds=10 ): def bwtest( cpuLimits, period_us=100000, seconds=10 ):
"""Example/test of link and CPU bandwidth limits """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 ) net = Mininet( topo=topo, host=host )
# pylint: disable=bare-except # pylint: disable=bare-except
except: except:
info( '*** Skipping scheduler %s\n' % sched ) info( '*** Skipping scheduler %s and cleaning up\n' % sched )
cleanup()
break break
net.start() net.start()
net.pingAll() net.pingAll()
@@ -70,7 +71,7 @@ def bwtest( cpuLimits, period_us=100000, seconds=10 ):
# ignore empty result from waitListening/telnet # ignore empty result from waitListening/telnet
popen.stdout.readline() popen.stdout.readline()
client.cmd( 'iperf -yc -t %s -c %s' % ( seconds, server.IP() ) ) 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 ] ) bps = float( result[ -1 ] )
popen.terminate() popen.terminate()
net.stop() net.stop()
+31 -16
View File
@@ -20,24 +20,39 @@ OpenFlow icon from https://www.opennetworking.org/
MINIEDIT_VERSION = '2.2.0.1' MINIEDIT_VERSION = '2.2.0.1'
import sys
from optparse import OptionParser 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 from subprocess import call
import tkFont
import tkFileDialog # pylint: disable=import-error
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
import tkFont
import tkFileDialog
import tkSimpleDialog
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
# pylint: enable=import-error
import re import re
import json import json
from distutils.version import StrictVersion from distutils.version import StrictVersion
import os import os
import sys
from functools import partial from functools import partial
if 'PYTHONPATH' in os.environ: if 'PYTHONPATH' in os.environ:
@@ -1408,7 +1423,7 @@ class MiniEdit( Frame ):
def convertJsonUnicode(self, text): def convertJsonUnicode(self, text):
"Some part of Mininet don't like Unicode" "Some part of Mininet don't like Unicode"
if isinstance(text, dict): 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): elif isinstance(text, list):
return [self.convertJsonUnicode(element) for element in text] return [self.convertJsonUnicode(element) for element in text]
elif isinstance(text, unicode): elif isinstance(text, unicode):
@@ -1835,7 +1850,7 @@ class MiniEdit( Frame ):
# Save Links # Save Links
f.write(" info( '*** Add links\\n')\n") 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) tags = self.canvas.gettags(key)
if 'data' in tags: if 'data' in tags:
optsExist = False optsExist = False
@@ -2875,7 +2890,7 @@ class MiniEdit( Frame ):
def buildLinks( self, net): def buildLinks( self, net):
# Make links # Make links
info( "Getting Links.\n" ) info( "Getting Links.\n" )
for key,link in self.links.iteritems(): for key,link in self.links.items():
tags = self.canvas.gettags(key) tags = self.canvas.gettags(key)
if 'data' in tags: if 'data' in tags:
src=link['src'] src=link['src']
@@ -3211,7 +3226,7 @@ class MiniEdit( Frame ):
customs = {} customs = {}
if os.path.isfile( fileName ): if os.path.isfile( fileName ):
execfile( fileName, customs, customs ) execfile( fileName, customs, customs )
for name, val in customs.iteritems(): for name, val in customs.items():
self.setCustom( name, val ) self.setCustom( name, val )
else: else:
raise Exception( 'could not find custom file: %s' % fileName ) raise Exception( 'could not find custom file: %s' % fileName )
+3 -2
View File
@@ -9,6 +9,7 @@ monitoring them
from mininet.topo import SingleSwitchTopo from mininet.topo import SingleSwitchTopo
from mininet.net import Mininet from mininet.net import Mininet
from mininet.log import info, setLogLevel from mininet.log import info, setLogLevel
from mininet.util import decode
from time import time from time import time
from select import poll, POLLIN from select import poll, POLLIN
@@ -19,7 +20,7 @@ def monitorFiles( outfiles, seconds, timeoutms ):
"Monitor set of files and return [(host, line)...]" "Monitor set of files and return [(host, line)...]"
devnull = open( '/dev/null', 'w' ) devnull = open( '/dev/null', 'w' )
tails, fdToFile, fdToHost = {}, {}, {} tails, fdToFile, fdToHost = {}, {}, {}
for h, outfile in outfiles.iteritems(): for h, outfile in outfiles.items():
tail = Popen( [ 'tail', '-f', outfile ], tail = Popen( [ 'tail', '-f', outfile ],
stdout=PIPE, stderr=devnull ) stdout=PIPE, stderr=devnull )
fd = tail.stdout.fileno() fd = tail.stdout.fileno()
@@ -40,7 +41,7 @@ def monitorFiles( outfiles, seconds, timeoutms ):
host = fdToHost[ fd ] host = fdToHost[ fd ]
# Wait for a line of output # Wait for a line of output
line = f.readline().strip() line = f.readline().strip()
yield host, line yield host, decode( line )
else: else:
# If we timed out, return nothing # If we timed out, return nothing
yield None, '' yield None, ''
+7 -3
View File
@@ -5,8 +5,9 @@ Tests for baresshd.py
""" """
import unittest import unittest
import pexpect from mininet.util import pexpect
from mininet.clean import cleanup, sh from mininet.clean import cleanup, sh
from sys import stdout
class testBareSSHD( unittest.TestCase ): class testBareSSHD( unittest.TestCase ):
@@ -14,7 +15,9 @@ class testBareSSHD( unittest.TestCase ):
def connected( self ): def connected( self ):
"Log into ssh server, check banner, then exit" "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: while True:
index = p.expect( self.opts ) index = p.expect( self.opts )
if index == 0: if index == 0:
@@ -22,6 +25,7 @@ class testBareSSHD( unittest.TestCase ):
else: else:
return False return False
def setUp( self ): def setUp( self ):
# verify that sshd is not running # verify that sshd is not running
self.assertFalse( self.connected() ) self.assertFalse( self.connected() )
@@ -55,7 +59,7 @@ class testBareSSHD( unittest.TestCase ):
def tearDown( self ): def tearDown( self ):
# kill the ssh process # 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() cleanup()
# remove public key pair # remove public key pair
sh( 'rm -rf /tmp/ssh' ) sh( 'rm -rf /tmp/ssh' )
+1 -1
View File
@@ -5,7 +5,7 @@ Tests for bind.py
""" """
import unittest import unittest
import pexpect from mininet.util import pexpect
class testBind( unittest.TestCase ): class testBind( unittest.TestCase ):
+1 -1
View File
@@ -5,7 +5,7 @@ A simple sanity check test for cluster edition
''' '''
import unittest import unittest
import pexpect from mininet.util import pexpect
class clusterSanityCheck( unittest.TestCase ): class clusterSanityCheck( unittest.TestCase ):
+1 -1
View File
@@ -5,7 +5,7 @@ Tests for controllers.py and controllers2.py
""" """
import unittest import unittest
import pexpect from mininet.util import pexpect
class testControllers( unittest.TestCase ): class testControllers( unittest.TestCase ):
+1 -1
View File
@@ -5,7 +5,7 @@ Test for controlnet.py
""" """
import unittest import unittest
import pexpect from mininet.util import pexpect
class testControlNet( unittest.TestCase ): class testControlNet( unittest.TestCase ):
+1 -1
View File
@@ -15,7 +15,7 @@ cfs 10% 1.29e+09
""" """
import unittest import unittest
import pexpect from mininet.util import pexpect
import sys import sys
class testCPU( unittest.TestCase ): class testCPU( unittest.TestCase ):
+1 -1
View File
@@ -5,7 +5,7 @@ Test for emptynet.py
""" """
import unittest import unittest
import pexpect from mininet.util import pexpect
class testEmptyNet( unittest.TestCase ): class testEmptyNet( unittest.TestCase ):
+1 -1
View File
@@ -7,7 +7,7 @@ Test for hwintf.py
import unittest import unittest
import re import re
import pexpect from mininet.util import pexpect
from mininet.log import setLogLevel from mininet.log import setLogLevel
from mininet.node import Node from mininet.node import Node
+1 -1
View File
@@ -5,7 +5,7 @@ Test for intfOptions.py
""" """
import unittest import unittest
import pexpect from mininet.util import pexpect
import sys import sys
class testIntfOptions( unittest.TestCase ): class testIntfOptions( unittest.TestCase ):
+1 -1
View File
@@ -5,7 +5,7 @@ Test for limit.py
""" """
import unittest import unittest
import pexpect from mininet.util import pexpect
import sys import sys
class testLimit( unittest.TestCase ): class testLimit( unittest.TestCase ):
+1 -1
View File
@@ -5,7 +5,7 @@ Test for linearbandwidth.py
""" """
import unittest import unittest
import pexpect from mininet.util import pexpect
import sys import sys
class testLinearBandwidth( unittest.TestCase ): class testLinearBandwidth( unittest.TestCase ):
+1 -1
View File
@@ -5,7 +5,7 @@ Test for linuxrouter.py
""" """
import unittest import unittest
import pexpect from mininet.util import pexpect
from mininet.util import quietRun from mininet.util import quietRun
class testLinuxRouter( unittest.TestCase ): class testLinuxRouter( unittest.TestCase ):
+1 -1
View File
@@ -6,7 +6,7 @@ validates mininet interfaces against systems interfaces
''' '''
import unittest import unittest
import pexpect from mininet.util import pexpect
class testMultiLink( unittest.TestCase ): class testMultiLink( unittest.TestCase ):
+1 -1
View File
@@ -5,7 +5,7 @@ Test for multiping.py
""" """
import unittest import unittest
import pexpect from mininet.util import pexpect
from collections import defaultdict from collections import defaultdict
class testMultiPing( unittest.TestCase ): class testMultiPing( unittest.TestCase ):
+1 -1
View File
@@ -5,7 +5,7 @@ Test for multipoll.py
""" """
import unittest import unittest
import pexpect from mininet.util import pexpect
class testMultiPoll( unittest.TestCase ): class testMultiPoll( unittest.TestCase ):
+1 -1
View File
@@ -5,7 +5,7 @@ Test for multitest.py
""" """
import unittest import unittest
import pexpect from mininet.util import pexpect
class testMultiTest( unittest.TestCase ): class testMultiTest( unittest.TestCase ):
+1 -1
View File
@@ -5,7 +5,7 @@ Test for nat.py
""" """
import unittest import unittest
import pexpect from mininet.util import pexpect
from mininet.util import quietRun from mininet.util import quietRun
destIP = '8.8.8.8' # Google DNS destIP = '8.8.8.8' # Google DNS
+1 -1
View File
@@ -5,7 +5,7 @@ Test for natnet.py
""" """
import unittest import unittest
import pexpect from mininet.util import pexpect
from mininet.util import quietRun from mininet.util import quietRun
class testNATNet( unittest.TestCase ): class testNATNet( unittest.TestCase ):
+1 -1
View File
@@ -5,7 +5,7 @@ Test for numberedports.py
""" """
import unittest import unittest
import pexpect from mininet.util import pexpect
from collections import defaultdict from collections import defaultdict
from mininet.node import OVSSwitch from mininet.node import OVSSwitch
+1 -1
View File
@@ -5,7 +5,7 @@ Test for popen.py and popenpoll.py
""" """
import unittest import unittest
import pexpect from mininet.util import pexpect
class testPopen( unittest.TestCase ): class testPopen( unittest.TestCase ):
+1 -1
View File
@@ -5,7 +5,7 @@ Test for scratchnet.py
""" """
import unittest import unittest
import pexpect from mininet.util import pexpect
class testScratchNet( unittest.TestCase ): class testScratchNet( unittest.TestCase ):
+2 -2
View File
@@ -5,7 +5,7 @@ Test for simpleperf.py
""" """
import unittest import unittest
import pexpect from mininet.util import pexpect
import sys import sys
from mininet.log import setLogLevel from mininet.log import setLogLevel
@@ -18,7 +18,7 @@ class testSimplePerf( unittest.TestCase ):
"Run the example and verify iperf results" "Run the example and verify iperf results"
# 10 Mb/s, plus or minus 20% tolerance # 10 Mb/s, plus or minus 20% tolerance
BW = 10 BW = 10
TOLERANCE = .2 TOLERANCE = .2
p = pexpect.spawn( 'python -m mininet.examples.simpleperf testmode' ) p = pexpect.spawn( 'python -m mininet.examples.simpleperf testmode' )
# check iperf results # check iperf results
p.expect( "Results: \['10M', '([\d\.]+) .bits/sec", timeout=480 ) p.expect( "Results: \['10M', '([\d\.]+) .bits/sec", timeout=480 )
+1 -1
View File
@@ -5,7 +5,7 @@ Test for sshd.py
""" """
import unittest import unittest
import pexpect from mininet.util import pexpect
from mininet.clean import sh from mininet.clean import sh
class testSSHD( unittest.TestCase ): class testSSHD( unittest.TestCase ):
+1 -1
View File
@@ -5,7 +5,7 @@ Test for tree1024.py
""" """
import unittest import unittest
import pexpect from mininet.util import pexpect
import sys import sys
class testTree1024( unittest.TestCase ): class testTree1024( unittest.TestCase ):
+1 -1
View File
@@ -5,7 +5,7 @@ Test for treeping64.py
""" """
import unittest import unittest
import pexpect from mininet.util import pexpect
import sys import sys
class testTreePing64( unittest.TestCase ): class testTreePing64( unittest.TestCase ):
+1 -1
View File
@@ -5,7 +5,7 @@ Test for vlanhost.py
""" """
import unittest import unittest
import pexpect from mininet.util import pexpect
import sys import sys
from mininet.util import quietRun from mininet.util import quietRun
+3 -3
View File
@@ -16,12 +16,13 @@ import time
from mininet.log import info from mininet.log import info
from mininet.term import cleanUpScreens from mininet.term import cleanUpScreens
from mininet.util import decode
def sh( cmd ): def sh( cmd ):
"Print a command and send it to the shell" "Print a command and send it to the shell"
info( cmd + '\n' ) 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 ): def killprocs( pattern ):
"Reliably terminate processes matching a pattern (including args)" "Reliably terminate processes matching a pattern (including args)"
@@ -76,7 +77,6 @@ class Cleanup( object ):
for dp in dps: for dp in dps:
if dp: if dp:
sh( 'dpctl deldp ' + dp ) sh( 'dpctl deldp ' + dp )
info( "*** Removing OVS datapaths\n" ) info( "*** Removing OVS datapaths\n" )
dps = sh("ovs-vsctl --timeout=1 list-br").strip().splitlines() dps = sh("ovs-vsctl --timeout=1 list-br").strip().splitlines()
if dps: if dps:
+2 -6
View File
@@ -164,7 +164,7 @@ class Intf( object ):
method: config method name method: config method name
param: arg=value (ignore if value=None) param: arg=value (ignore if value=None)
value may also be list or dict""" value may also be list or dict"""
name, value = param.items()[ 0 ] name, value = list( param.items() )[ 0 ]
f = getattr( self, method, None ) f = getattr( self, method, None )
if not f or value is None: if not f or value is None:
return return
@@ -285,11 +285,7 @@ class TCIntf( Intf ):
loss=None, max_queue_size=None ): loss=None, max_queue_size=None ):
"Internal method: return tc commands for delay and loss" "Internal method: return tc commands for delay and loss"
cmds = [] cmds = []
if delay and delay < 0: if loss and ( loss < 0 or loss > 100 ):
error( 'Negative delay', delay, '\n' )
elif jitter and jitter < 0:
error( 'Negative jitter', jitter, '\n' )
elif loss and ( loss < 0 or loss > 100 ):
error( 'Bad loss percentage', loss, '%%\n' ) error( 'Bad loss percentage', loss, '%%\n' )
else: else:
# Delay/jitter/loss/max queue size # Delay/jitter/loss/max queue size
+3 -3
View File
@@ -1,6 +1,6 @@
"Module dependency utility functions for Mininet." "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 mininet.log import info, error, debug
from os import environ 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""" add: string or list of module names to add, if not already loaded"""
subtract = subtract if subtract is not None else [] subtract = subtract if subtract is not None else []
add = add if add is not None else [] add = add if add is not None else []
if isinstance( subtract, basestring ): if isinstance( subtract, BaseString ):
subtract = [ subtract ] subtract = [ subtract ]
if isinstance( add, basestring ): if isinstance( add, BaseString ):
add = [ add ] add = [ add ]
for mod in subtract: for mod in subtract:
if mod in lsmod(): if mod in lsmod():
+7 -5
View File
@@ -104,7 +104,7 @@ from mininet.nodelib import NAT
from mininet.link import Link, Intf from mininet.link import Link, Intf
from mininet.util import ( quietRun, fixLimits, numCores, ensureRoot, from mininet.util import ( quietRun, fixLimits, numCores, ensureRoot,
macColonHex, ipStr, ipParse, netParse, ipAdd, macColonHex, ipStr, ipParse, netParse, ipAdd,
waitListening ) waitListening, BaseString )
from mininet.term import cleanUpScreens, makeTerms from mininet.term import cleanUpScreens, makeTerms
# Mininet version: should be consistent with README and LICENSE # Mininet version: should be consistent with README and LICENSE
@@ -383,8 +383,8 @@ class Mininet( object ):
params: additional link params (optional) params: additional link params (optional)
returns: link object""" returns: link object"""
# Accept node objects or names # Accept node objects or names
node1 = node1 if not isinstance( node1, basestring ) else self[ node1 ] node1 = node1 if not isinstance( node1, BaseString ) else self[ node1 ]
node2 = node2 if not isinstance( node2, basestring ) else self[ node2 ] node2 = node2 if not isinstance( node2, BaseString ) else self[ node2 ]
options = dict( params ) options = dict( params )
# Port is optional # Port is optional
if port1 is not None: if port1 is not None:
@@ -549,7 +549,8 @@ class Mininet( object ):
switch.start( self.controllers ) switch.start( self.controllers )
started = {} started = {}
for swclass, switches in groupby( for swclass, switches in groupby(
sorted( self.switches, key=type ), type ): sorted( self.switches,
key=lambda s: str( type( s ) ) ), type ):
switches = tuple( switches ) switches = tuple( switches )
if hasattr( swclass, 'batchStartup' ): if hasattr( swclass, 'batchStartup' ):
success = swclass.batchStartup( switches ) success = swclass.batchStartup( switches )
@@ -576,7 +577,8 @@ class Mininet( object ):
info( '*** Stopping %i switches\n' % len( self.switches ) ) info( '*** Stopping %i switches\n' % len( self.switches ) )
stopped = {} stopped = {}
for swclass, switches in groupby( for swclass, switches in groupby(
sorted( self.switches, key=type ), type ): sorted( self.switches,
key=lambda s: str( type( s ) ) ), type ):
switches = tuple( switches ) switches = tuple( switches )
if hasattr( swclass, 'batchShutdown' ): if hasattr( swclass, 'batchShutdown' ):
success = swclass.batchShutdown( switches ) success = swclass.batchShutdown( switches )
+28 -16
View File
@@ -62,7 +62,8 @@ from time import sleep
from mininet.log import info, error, warn, debug from mininet.log import info, error, warn, debug
from mininet.util import ( quietRun, errRun, errFail, moveIntf, isShellBuiltin, 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.moduledeps import moduleDeps, pathCheck, TUN
from mininet.link import Link, Intf, TCIntf, OVSIntf from mininet.link import Link, Intf, TCIntf, OVSIntf
from re import findall from re import findall
@@ -87,6 +88,9 @@ class Node( object ):
self.privateDirs = params.get( 'privateDirs', [] ) self.privateDirs = params.get( 'privateDirs', [] )
self.inNamespace = params.get( 'inNamespace', inNamespace ) self.inNamespace = params.get( 'inNamespace', inNamespace )
# Python 3 complains if we don't wait for shell exit
self.waitExited = params.get( 'waitExited', Python3 )
# Stash configuration parameters for future reference # Stash configuration parameters for future reference
self.params = params self.params = params
@@ -144,7 +148,9 @@ class Node( object ):
master, slave = pty.openpty() master, slave = pty.openpty()
self.shell = self._popen( cmd, stdin=slave, stdout=slave, stderr=slave, self.shell = self._popen( cmd, stdin=slave, stdout=slave, stderr=slave,
close_fds=False ) 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.stdout = self.stdin
self.pid = self.shell.pid self.pid = self.shell.pid
self.pollOut = select.poll() self.pollOut = select.poll()
@@ -171,7 +177,7 @@ class Node( object ):
def mountPrivateDirs( self ): def mountPrivateDirs( self ):
"mount private directories" "mount private directories"
# Avoid expanding a string into a list of chars # 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: for directory in self.privateDirs:
if isinstance( directory, tuple ): if isinstance( directory, tuple ):
# mount given private directory # mount given private directory
@@ -200,7 +206,9 @@ class Node( object ):
params: parameters to Popen()""" params: parameters to Popen()"""
# Leave this is as an instance method for now # Leave this is as an instance method for now
assert self assert self
return Popen( cmd, **params ) popen = Popen( cmd, **params )
debug( '_popen', cmd, popen.pid )
return popen
def cleanup( self ): def cleanup( self ):
"Help python collect its garbage." "Help python collect its garbage."
@@ -209,6 +217,9 @@ class Node( object ):
# for intfName in self.intfNames(): # for intfName in self.intfNames():
# if self.name in intfName: # if self.name in intfName:
# quietRun( 'ip link del ' + 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 self.shell = None
# Subshell I/O, commands and control # Subshell I/O, commands and control
@@ -218,7 +229,7 @@ class Node( object ):
maxbytes: maximum number of bytes to return""" maxbytes: maximum number of bytes to return"""
count = len( self.readbuf ) count = len( self.readbuf )
if count < maxbytes: if count < maxbytes:
data = os.read( self.stdout.fileno(), maxbytes - count ) data = decode( os.read( self.stdout.fileno(), maxbytes - count ) )
self.readbuf += data self.readbuf += data
if maxbytes >= len( self.readbuf ): if maxbytes >= len( self.readbuf ):
result = self.readbuf result = self.readbuf
@@ -242,7 +253,7 @@ class Node( object ):
def write( self, data ): def write( self, data ):
"""Write data to node. """Write data to node.
data: string""" data: string"""
os.write( self.stdin.fileno(), data ) os.write( self.stdin.fileno(), encode( data ) )
def terminate( self ): def terminate( self ):
"Send kill signal to Node and clean up after it." "Send kill signal to Node and clean up after it."
@@ -376,7 +387,7 @@ class Node( object ):
if isinstance( args[ 0 ], list ): if isinstance( args[ 0 ], list ):
# popen([cmd, arg1, arg2...]) # popen([cmd, arg1, arg2...])
cmd = args[ 0 ] cmd = args[ 0 ]
elif isinstance( args[ 0 ], basestring ): elif isinstance( args[ 0 ], BaseString ):
# popen("cmd arg1 arg2...") # popen("cmd arg1 arg2...")
cmd = args[ 0 ].split() cmd = args[ 0 ].split()
else: else:
@@ -400,7 +411,7 @@ class Node( object ):
# Warning: this can fail with large numbers of fds! # Warning: this can fail with large numbers of fds!
out, err = popen.communicate() out, err = popen.communicate()
exitcode = popen.wait() exitcode = popen.wait()
return out, err, exitcode return decode( out ), decode( err ), exitcode
# Interface management, configuration, and routing # Interface management, configuration, and routing
@@ -462,7 +473,7 @@ class Node( object ):
""" """
if not intf: if not intf:
return self.defaultIntf() return self.defaultIntf()
elif isinstance( intf, basestring): elif isinstance( intf, BaseString):
return self.nameToIntf[ intf ] return self.nameToIntf[ intf ]
else: else:
return intf return intf
@@ -489,7 +500,7 @@ class Node( object ):
# explicitly so that we won't get errors if we run before they # explicitly so that we won't get errors if we run before they
# have been removed by the kernel. Unfortunately this is very slow, # have been removed by the kernel. Unfortunately this is very slow,
# at least with Linux kernels before 2.6.33 # 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 # Protect against deleting hardware interfaces
if ( self.name in intf.name ) or ( not checkName ): if ( self.name in intf.name ) or ( not checkName ):
intf.delete() intf.delete()
@@ -514,7 +525,7 @@ class Node( object ):
"""Set the default route to go through intf. """Set the default route to go through intf.
intf: Intf or {dev <intfname> via <gw-ip> ...}""" intf: Intf or {dev <intfname> via <gw-ip> ...}"""
# Note setParam won't call us if intf is none # 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 params = intf
else: else:
params = 'dev %s' % intf params = 'dev %s' % intf
@@ -561,7 +572,7 @@ class Node( object ):
method: config method name method: config method name
param: arg=value (ignore if value=None) param: arg=value (ignore if value=None)
value may also be list or dict""" value may also be list or dict"""
name, value = param.items()[ 0 ] name, value = list( param.items() )[ 0 ]
if value is None: if value is None:
return return
f = getattr( self, method, None ) f = getattr( self, method, None )
@@ -610,7 +621,7 @@ class Node( object ):
def intfList( self ): def intfList( self ):
"List of our interfaces sorted by port number" "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 ): def intfNames( self ):
"The names of our interfaces sorted by port number" "The names of our interfaces sorted by port number"
@@ -881,7 +892,7 @@ class Switch( Node ):
"Return correctly formatted dpid from dpid or switch name (s1 -> 1)" "Return correctly formatted dpid from dpid or switch name (s1 -> 1)"
if dpid: if dpid:
# Remove any colons and make sure it's a good hex number # 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 assert len( dpid ) <= self.dpidLen and int( dpid, 16 ) >= 0
else: else:
# Use hex of the first number in the switch name # Use hex of the first number in the switch name
@@ -889,6 +900,7 @@ class Switch( Node ):
if nums: if nums:
dpid = hex( int( nums[ 0 ] ) )[ 2: ] dpid = hex( int( nums[ 0 ] ) )[ 2: ]
else: else:
self.terminate() # Python 3.6 crash workaround
raise Exception( 'Unable to derive default datapath ID - ' raise Exception( 'Unable to derive default datapath ID - '
'please either specify a dpid or use a ' 'please either specify a dpid or use a '
'canonical switch name such as s23.' ) 'canonical switch name such as s23.' )
@@ -1232,7 +1244,7 @@ class OVSSwitch( Switch ):
run( cmds, shell=True ) run( cmds, shell=True )
# Reapply link config if necessary... # Reapply link config if necessary...
for switch in switches: for switch in switches:
for intf in switch.intfs.itervalues(): for intf in switch.intfs.values():
if isinstance( intf, TCIntf ): if isinstance( intf, TCIntf ):
intf.config( **intf.params ) intf.config( **intf.params )
return switches return switches
@@ -1258,7 +1270,7 @@ class OVSSwitch( Switch ):
pids = ' '.join( str( switch.pid ) for switch in switches ) pids = ' '.join( str( switch.pid ) for switch in switches )
run( 'kill -HUP ' + pids ) run( 'kill -HUP ' + pids )
for switch in switches: for switch in switches:
switch.shell = None switch.terminate()
return switches return switches
+17 -13
View File
@@ -30,10 +30,10 @@ class TestSwitchDpidAssignmentOVS( unittest.TestCase ):
def testDefaultDpid( self ): def testDefaultDpid( self ):
"""Verify that the default dpid is assigned using a valid provided """Verify that the default dpid is assigned using a valid provided
canonical switchname if no dpid is passed in switch creation.""" canonical switchname if no dpid is passed in switch creation."""
switch = Mininet( Topo(), net = Mininet( Topo(), self.switchClass, Host, Controller )
self.switchClass, switch = net.addSwitch( 's1' )
Host, Controller ).addSwitch( 's1' )
self.assertEqual( switch.defaultDpid(), switch.dpid ) self.assertEqual( switch.defaultDpid(), switch.dpid )
net.stop()
def dpidFrom( self, num ): def dpidFrom( self, num ):
"Compute default dpid from number" "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 """Verify that Switch dpid is the actual dpid assigned if dpid is
passed in switch creation.""" passed in switch creation."""
dpid = self.dpidFrom( 0xABCD ) dpid = self.dpidFrom( 0xABCD )
switch = Mininet( Topo(), self.switchClass, net = Mininet( Topo(), self.switchClass, Host, Controller )
Host, Controller ).addSwitch( switch = net.addSwitch( 's1', dpid=dpid )
's1', dpid=dpid )
self.assertEqual( switch.dpid, dpid ) self.assertEqual( switch.dpid, dpid )
net.stop()
def testDefaultDpidAssignmentFailure( self ): def testDefaultDpidAssignmentFailure( self ):
"""Verify that Default dpid assignment raises an Exception if the """Verify that Default dpid assignment raises an Exception if the
name of the switch does not contin a digit. Also verify the name of the switch does not contin a digit. Also verify the
exception message.""" exception message."""
net = Mininet( Topo(), self.switchClass, Host, Controller )
with self.assertRaises( Exception ) as raises_cm: with self.assertRaises( Exception ) as raises_cm:
Mininet( Topo(), self.switchClass, net.addSwitch( 'A' )
Host, Controller ).addSwitch( 'A' ) self.assertTrue( 'Unable to derive '
self.assertEqual(raises_cm.exception.message, 'Unable to derive '
'default datapath ID - please either specify a dpid ' '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 ): def testDefaultDpidLen( self ):
"""Verify that Default dpid length is 16 characters consisting of """Verify that Default dpid length is 16 characters consisting of
16 - len(hex of first string of contiguous digits passed in switch 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 name) 0's followed by hex of first string of contiguous digits passed
in switch name.""" in switch name."""
switch = Mininet( Topo(), self.switchClass, net = Mininet( Topo(), self.switchClass, Host, Controller )
Host, Controller ).addSwitch( 's123' ) switch = net.addSwitch( 's123' )
self.assertEqual( switch.dpid, self.dpidFrom( 123 ) ) self.assertEqual( switch.dpid, self.dpidFrom( 123 ) )
net.stop()
class OVSUser( OVSSwitch): class OVSUser( OVSSwitch):
"OVS User Switch convenience class" "OVS User Switch convenience class"
@@ -95,3 +98,4 @@ class testSwitchUserspace( TestSwitchDpidAssignmentOVS ):
if __name__ == '__main__': if __name__ == '__main__':
setLogLevel( 'warning' ) setLogLevel( 'warning' )
unittest.main() unittest.main()
cleanup()
+16 -9
View File
@@ -7,13 +7,13 @@ TODO: missing xterm test
""" """
import unittest import unittest
import pexpect
import os import os
import re import re
from mininet.util import quietRun from mininet.util import quietRun, pexpect
from distutils.version import StrictVersion from distutils.version import StrictVersion
from time import sleep from time import sleep
def tsharkVersion(): def tsharkVersion():
"Return tshark version" "Return tshark version"
versionStr = quietRun( 'tshark -v' ) versionStr = quietRun( 'tshark -v' )
@@ -95,7 +95,8 @@ class testWalkthrough( unittest.TestCase ):
p = pexpect.spawn( 'mn' ) p = pexpect.spawn( 'mn' )
p.expect( self.prompt ) p.expect( self.prompt )
# Third pattern is a local interface beginning with 'eth' or 'en' # 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 ] self.prompt ]
# h1 ifconfig # h1 ifconfig
p.sendline( 'h1 ifconfig -a' ) p.sendline( 'h1 ifconfig -a' )
@@ -122,7 +123,7 @@ class testWalkthrough( unittest.TestCase ):
ifcount += 1 ifcount += 1
else: else:
break break
self.assertTrue( ifcount >= 3, 'Missing interfaces on s1') self.assertTrue( ifcount <= 3, 'Missing interfaces on s1')
# h1 ps # h1 ps
p.sendline( "h1 ps -a | egrep -v 'ps|grep'" ) p.sendline( "h1 ps -a | egrep -v 'ps|grep'" )
p.expect( self.prompt ) p.expect( self.prompt )
@@ -156,9 +157,13 @@ class testWalkthrough( unittest.TestCase ):
def testSimpleHTTP( self ): def testSimpleHTTP( self ):
"Start an HTTP server on h1 and wget from h2" "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 = pexpect.spawn( 'mn' )
p.expect( self.prompt ) 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 # The walkthrough doesn't specify a delay here, and
# we also don't read the output (also a possible problem), # we also don't read the output (also a possible problem),
# but for now let's wait a couple of seconds to make # 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 = ' p.expect( r'rtt min/avg/max/mdev = '
r'([\d\.]+)/([\d\.]+)/([\d\.]+)/([\d\.]+) ms' ) r'([\d\.]+)/([\d\.]+)/([\d\.]+)/([\d\.]+) ms' )
delay = float( p.match.group( 2 ) ) delay = float( p.match.group( 2 ) )
self.assertTrue( delay > 40, 'Delay < 40ms' ) self.assertTrue( delay >= 40, 'Delay < 40ms' )
self.assertTrue( delay < 45, 'Delay > 40ms' ) self.assertTrue( delay <= 50, 'Delay > 50ms' )
p.expect( self.prompt ) p.expect( self.prompt )
p.sendline( 'exit' ) p.sendline( 'exit' )
p.wait() p.wait()
@@ -260,7 +265,7 @@ class testWalkthrough( unittest.TestCase ):
p.expect( self.prompt ) p.expect( self.prompt )
for i in range( 1, 3 ): for i in range( 1, 3 ):
p.sendline( 'h%d ifconfig' % i ) 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.expect( self.prompt )
p.sendline( 'exit' ) p.sendline( 'exit' )
p.expect( pexpect.EOF ) p.expect( pexpect.EOF )
@@ -286,7 +291,9 @@ class testWalkthrough( unittest.TestCase ):
"Test running user switch in its own namespace" "Test running user switch in its own namespace"
p = pexpect.spawn( 'mn --innamespace --switch user' ) p = pexpect.spawn( 'mn --innamespace --switch user' )
p.expect( self.prompt ) 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' ) p.sendline( 's1 ifconfig -a' )
ifcount = 0 ifcount = 0
while True: while True:
+3 -3
View File
@@ -57,12 +57,12 @@ class MultiGraph( object ):
def edges_iter( self, data=False, keys=False ): def edges_iter( self, data=False, keys=False ):
"Iterator: return graph edges, optionally with data and keys" "Iterator: return graph edges, optionally with data and keys"
for src, entry in self.edge.iteritems(): for src, entry in self.edge.items():
for dst, entrykeys in entry.iteritems(): for dst, entrykeys in entry.items():
if src > dst: if src > dst:
# Skip duplicate edges # Skip duplicate edges
continue continue
for k, attrs in entrykeys.iteritems(): for k, attrs in entrykeys.items():
if data: if data:
if keys: if keys:
yield( src, dst, k, attrs ) yield( src, dst, k, attrs )
+58 -12
View File
@@ -12,6 +12,39 @@ from fcntl import fcntl, F_GETFL, F_SETFL
from os import O_NONBLOCK from os import O_NONBLOCK
import os import os
from functools import partial 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:
# pylint: disable=import-error
oldpexpect = None
import pexpect as oldpexpect
# pylint: enable=import-error
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 # Command execution support
@@ -57,7 +90,7 @@ def oldQuietRun( *cmd ):
# This is a bit complicated, but it enables us to # This is a bit complicated, but it enables us to
# monitor command output as it is happening # monitor command output as it is happening
# pylint: disable=too-many-branches # pylint: disable=too-many-branches,too-many-statements
def errRun( *cmd, **kwargs ): def errRun( *cmd, **kwargs ):
"""Run a command and return stdout, stderr and return code """Run a command and return stdout, stderr and return code
cmd: string or list of command and args cmd: string or list of command and args
@@ -98,6 +131,8 @@ def errRun( *cmd, **kwargs ):
f = fdtofile[ fd ] f = fdtofile[ fd ]
if event & POLLIN: if event & POLLIN:
data = f.read( 1024 ) data = f.read( 1024 )
if Python3:
data = data.decode( Encoding )
if echo: if echo:
output( data ) output( data )
if f == popen.stdout: if f == popen.stdout:
@@ -116,6 +151,10 @@ def errRun( *cmd, **kwargs ):
poller.unregister( fd ) poller.unregister( fd )
returncode = popen.wait() 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 ) debug( out, err, returncode )
return out, err, returncode return out, err, returncode
# pylint: enable=too-many-branches # pylint: enable=too-many-branches
@@ -374,7 +413,7 @@ def pmonitor(popens, timeoutms=500, readline=True,
terminates: when all EOFs received""" terminates: when all EOFs received"""
poller = poll() poller = poll()
fdToHost = {} fdToHost = {}
for host, popen in popens.iteritems(): for host, popen in popens.items():
fd = popen.stdout.fileno() fd = popen.stdout.fileno()
fdToHost[ fd ] = host fdToHost[ fd ] = host
poller.register( fd, POLLIN ) poller.register( fd, POLLIN )
@@ -382,6 +421,13 @@ def pmonitor(popens, timeoutms=500, readline=True,
# Use non-blocking reads # Use non-blocking reads
flags = fcntl( fd, F_GETFL ) flags = fcntl( fd, F_GETFL )
fcntl( fd, F_SETFL, flags | O_NONBLOCK ) 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: while popens:
fds = poller.poll( timeoutms ) fds = poller.poll( timeoutms )
if fds: if fds:
@@ -389,15 +435,15 @@ def pmonitor(popens, timeoutms=500, readline=True,
host = fdToHost[ fd ] host = fdToHost[ fd ]
popen = popens[ host ] popen = popens[ host ]
if event & POLLIN: if event & POLLIN:
if readline: line = readit( popen.stdout )
# Attempt to read a line of output
# This blocks until we receive a newline!
line = popen.stdout.readline()
else:
line = popen.stdout.read( readmax )
yield host, line yield host, line
# Check for EOF if event & POLLHUP:
elif event & POLLHUP: while True:
# Drain buffer
line = readit( popen.stdout )
yield host, line
if line == '':
break
poller.unregister( fd ) poller.unregister( fd )
del popens[ host ] del popens[ host ]
else: else:
@@ -460,7 +506,7 @@ def fixLimits():
def mountCgroups(): def mountCgroups():
"Make sure cgroups file system is mounted" "Make sure cgroups file system is mounted"
mounts = quietRun( 'cat /proc/mounts' ) mounts = quietRun( 'grep cgroup /proc/mounts' )
cgdir = '/sys/fs/cgroup' cgdir = '/sys/fs/cgroup'
csdir = cgdir + '/cpuset' csdir = cgdir + '/cpuset'
if ('cgroup %s' % cgdir not in mounts and if ('cgroup %s' % cgdir not in mounts and
@@ -600,7 +646,7 @@ def waitListening( client=None, server='127.0.0.1', port=80, timeout=None ):
if not runCmd( 'which telnet' ): if not runCmd( 'which telnet' ):
raise Exception('Could not find telnet' ) raise Exception('Could not find telnet' )
# pylint: disable=maybe-no-member # 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 ) ) cmd = ( 'echo A | telnet -e A %s %s' % ( serverIP, port ) )
time = 0 time = 0
result = runCmd( cmd ) result = runCmd( cmd )
+16 -7
View File
@@ -102,6 +102,14 @@ function version_ge {
[ "$1" == "$latest" ] [ "$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 Deb pkg to be removed:
KERNEL_IMAGE_OLD=linux-image-2.6.26-33-generic 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 \ $install gcc make socat psmisc xterm openssh-clients iperf \
iproute telnet python-setuptools libcgroup-tools \ iproute telnet python-setuptools libcgroup-tools \
ethtool help2man pyflakes pylint python-pep8 python-pexpect 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 \ $install gcc make socat psmisc xterm openssh iperf \
iproute telnet python-setuptools libcgroup-tools \ iproute telnet ${PYPKG}-setuptools libcgroup-tools \
ethtool help2man python-pyflakes python3-pylint python-pep8 python-pexpect ethtool help2man python-pyflakes python3-pylint \
else python-pep8 ${PYPKG}-pexpect ${PYPKG}-tk
else # Debian/Ubuntu
$install gcc make socat psmisc xterm ssh iperf iproute2 telnet \ $install gcc make socat psmisc xterm ssh iperf iproute2 telnet \
python-setuptools cgroup-bin ethtool help2man \ cgroup-bin ethtool help2man pyflakes pylint pep8 \
pyflakes pylint pep8 python-pexpect ${PYPKG}-setuptools ${PYPKG}-pexpect ${PYPKG}-tk
fi fi
echo "Installing Mininet core" echo "Installing Mininet core"
pushd $MININET_DIR/mininet pushd $MININET_DIR/mininet
sudo make install sudo PYTHON=${PYTHON} make install
popd popd
} }
+8 -3
View File
@@ -1,14 +1,19 @@
#!/usr/bin/python #!/usr/bin/python
from subprocess import check_output as co 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 # 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() version = version.strip()
# Find all Mininet path references # 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 error = False