From 88cbf4ec4204f3d9f4f21fa9faeecab60baed035 Mon Sep 17 00:00:00 2001 From: Bob Lantz Date: Wed, 11 Jul 2018 23:13:45 +0000 Subject: [PATCH 01/38] Kinder, gentler Python 3 compatibility Looking at trying to minimize code changes for Python 3. For now we are keeping byte str() in py2 and unicode str() in py3. It's a pain to rewrite all string code to usee u'' for py2 compatibility. However we may need to revisit this for pexpect(). Created compatibility wrappers in util.py: BaseString (replacement for basestring) decode() (optionally decode utf-8 for py3) encode() (optionally encode utf-8 for py3) Also we've switched from the .iter*() flavors to their static alternatives for py2 (note they are iterators in py3!) Changed util.errRun and clean.sh to call decode() --- bin/mn | 4 ++-- mininet/clean.py | 6 +++--- mininet/link.py | 2 +- mininet/moduledeps.py | 6 +++--- mininet/net.py | 6 +++--- mininet/node.py | 25 ++++++++++++++----------- mininet/topo.py | 6 +++--- mininet/util.py | 16 +++++++++++++++- 8 files changed, 44 insertions(+), 27 deletions(-) diff --git a/bin/mn b/bin/mn index a7b1479..24dd6fb 100755 --- a/bin/mn +++ b/bin/mn @@ -256,7 +256,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 +286,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!) ' ) ) diff --git a/mininet/clean.py b/mininet/clean.py index 248de8b..f66d633 100755 --- a/mininet/clean.py +++ b/mininet/clean.py @@ -16,12 +16,13 @@ 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)" @@ -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: diff --git a/mininet/link.py b/mininet/link.py index d514de6..170d022 100644 --- a/mininet/link.py +++ b/mininet/link.py @@ -164,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 diff --git a/mininet/moduledeps.py b/mininet/moduledeps.py index 860c21c..fda2d70 100644 --- a/mininet/moduledeps.py +++ b/mininet/moduledeps.py @@ -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(): diff --git a/mininet/net.py b/mininet/net.py index 2c7668d..d7bbf4f 100755 --- a/mininet/net.py +++ b/mininet/net.py @@ -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 @@ -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: diff --git a/mininet/node.py b/mininet/node.py index 42b3780..ba09f84 100644 --- a/mininet/node.py +++ b/mininet/node.py @@ -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 ) from mininet.moduledeps import moduleDeps, pathCheck, TUN from mininet.link import Link, Intf, TCIntf, OVSIntf from re import findall @@ -144,7 +145,9 @@ class Node( object ): 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() @@ -171,7 +174,7 @@ class Node( object ): def mountPrivateDirs( self ): "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 @@ -218,7 +221,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 @@ -242,7 +245,7 @@ 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." @@ -376,7 +379,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: @@ -462,7 +465,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 @@ -514,7 +517,7 @@ class Node( object ): """Set the default route to go through intf. intf: Intf or {dev via ...}""" # 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 @@ -561,7 +564,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 ) @@ -610,7 +613,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" @@ -1232,7 +1235,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 diff --git a/mininet/topo.py b/mininet/topo.py index 629632f..bc60c05 100644 --- a/mininet/topo.py +++ b/mininet/topo.py @@ -57,12 +57,12 @@ class MultiGraph( object ): def edges_iter( self, data=False, keys=False ): "Iterator: return graph edges, optionally with data and keys" - for src, entry in self.edge.iteritems(): - for dst, entrykeys in entry.iteritems(): + for src, entry in self.edge.items(): + for dst, entrykeys in entry.items(): if src > dst: # Skip duplicate edges continue - for k, attrs in entrykeys.iteritems(): + for k, attrs in entrykeys.items(): if data: if keys: yield( src, dst, k, attrs ) diff --git a/mininet/util.py b/mininet/util.py index 6cccddc..3b80f58 100644 --- a/mininet/util.py +++ b/mininet/util.py @@ -12,6 +12,18 @@ 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 basestring +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 # Command execution support @@ -98,6 +110,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: @@ -374,7 +388,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 ) From 56fc6c3955f504a7b51b7d6de31cc8efe930ef72 Mon Sep 17 00:00:00 2001 From: Bob Lantz Date: Thu, 12 Jul 2018 01:33:36 +0000 Subject: [PATCH 02/38] Close popen stdout and stderr They should get cleaned up anyway, but Python 3 gives us an error message. This silences it. --- mininet/util.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/mininet/util.py b/mininet/util.py index 3b80f58..1269410 100644 --- a/mininet/util.py +++ b/mininet/util.py @@ -130,6 +130,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 From 1ef12e450a916ceae355e664e020887536a577ca Mon Sep 17 00:00:00 2001 From: Bob Lantz Date: Thu, 12 Jul 2018 01:34:37 +0000 Subject: [PATCH 03/38] Apparently Python 3 types aren't ordered --- mininet/net.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/mininet/net.py b/mininet/net.py index d7bbf4f..29d3876 100755 --- a/mininet/net.py +++ b/mininet/net.py @@ -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 ) From bf83f4f343ad5c6da9cc856be40fc844efa9fa9f Mon Sep 17 00:00:00 2001 From: Bob Lantz Date: Thu, 12 Jul 2018 01:51:16 +0000 Subject: [PATCH 04/38] Don't bother checking for negative delay/jitter It's kind of pointless. However technically the argument should be a number and not a string - it's always in ms. --- mininet/link.py | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/mininet/link.py b/mininet/link.py index 170d022..59cb9ec 100644 --- a/mininet/link.py +++ b/mininet/link.py @@ -285,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 From f314a6626a03dd4706ab074e48ab13b8b776543d Mon Sep 17 00:00:00 2001 From: Bob Lantz Date: Thu, 12 Jul 2018 01:53:10 +0000 Subject: [PATCH 05/38] Missed one instance of basestring --- mininet/util.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mininet/util.py b/mininet/util.py index 1269410..fd70690 100644 --- a/mininet/util.py +++ b/mininet/util.py @@ -618,7 +618,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 ) From cac884a85e4ed48f1de091322fe15c5efc3e8b11 Mon Sep 17 00:00:00 2001 From: Bob Lantz Date: Thu, 12 Jul 2018 01:59:49 +0000 Subject: [PATCH 06/38] Wait for Node() shell to exit on Python 3 I'm not a fan of this, but Python 3's subprocess module complains otherwise. For now we're only doing this on Python 3. We should probably quantify the slowdown however. --- mininet/node.py | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/mininet/node.py b/mininet/node.py index ba09f84..0a1907b 100644 --- a/mininet/node.py +++ b/mininet/node.py @@ -63,7 +63,7 @@ from time import sleep from mininet.log import info, error, warn, debug from mininet.util import ( quietRun, errRun, errFail, moveIntf, isShellBuiltin, numCores, retry, mountCgroups, BaseString, decode, - encode ) + encode, Python3 ) from mininet.moduledeps import moduleDeps, pathCheck, TUN from mininet.link import Link, Intf, TCIntf, OVSIntf from re import findall @@ -88,6 +88,9 @@ class Node( object ): self.privateDirs = params.get( 'privateDirs', [] ) self.inNamespace = params.get( 'inNamespace', inNamespace ) + # 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 @@ -203,7 +206,9 @@ class Node( object ): 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." @@ -212,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 @@ -724,6 +732,7 @@ class CPULimitedHost( Host ): super( CPULimitedHost, self ).cleanup() retry( retries=3, delaySecs=.1, fn=self.cgroupDel ) + _rtGroupSched = False # internal class var: Is CONFIG_RT_GROUP_SCHED set? @classmethod @@ -1261,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 From 1a2925923f23aeee0c5eae72db57cc22350c7084 Mon Sep 17 00:00:00 2001 From: Bob Lantz Date: Thu, 12 Jul 2018 02:39:24 +0000 Subject: [PATCH 07/38] translate() -> replace() for Python 3 compatibility str.translate() works differently in Python 3 --- mininet/node.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mininet/node.py b/mininet/node.py index 0a1907b..25accc5 100644 --- a/mininet/node.py +++ b/mininet/node.py @@ -893,7 +893,7 @@ class Switch( Node ): "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 From 853815500d4fc20239d7a8ba65584c9a4e2f1463 Mon Sep 17 00:00:00 2001 From: Bob Lantz Date: Thu, 12 Jul 2018 03:05:39 +0000 Subject: [PATCH 08/38] Create util.pexpect which is compatible with Python 3 strings Unfortunately pexpect isn't compatible with Python 3 strings unless you specify unicode encoding, which isn't the default. With this helper code, you can import pexpect from mininet.util and get default pexpect.spawn() behavior that works with Python 3 strings. --- mininet/util.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/mininet/util.py b/mininet/util.py index fd70690..851df34 100644 --- a/mininet/util.py +++ b/mininet/util.py @@ -24,6 +24,18 @@ def decode( s ): def encode( s ): "Encode a byte string if needed for Python 3" return s.encode( Encoding ) if Python3 else s +# Make pexpect compatible with Python 3 strings +try: + import pexpect as oldpexpect + pexpect, oldspawn = oldpexpect, oldpexpect.spawn + def spawn( self, *args, **kwargs): + "Let pexpect work with Python3 utf-8 strings" + if Python3: + kwargs.update( encoding='utf-8' ) + return oldspawn( self, *args, **kwargs ) + oldpexpect.spawn = spawn +except: + pass # Command execution support From 2822998cada989b15288cbe9247fc7953e7607df Mon Sep 17 00:00:00 2001 From: Bob Lantz Date: Thu, 12 Jul 2018 03:38:08 +0000 Subject: [PATCH 09/38] A couple of missed py3 items: execfile, iteritems --- bin/mn | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/bin/mn b/bin/mn index 24dd6fb..df82b89 100755 --- a/bin/mn +++ b/bin/mn @@ -186,8 +186,9 @@ class MininetRunner( object ): for fileName in files: customs = {} if os.path.isfile( fileName ): - execfile( fileName, customs, customs ) - 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 ) else: raise Exception( 'could not find custom file: %s' % fileName ) From 4b744d1fb024715af93a0c16ebe7c3d4e5aec852 Mon Sep 17 00:00:00 2001 From: Bob Lantz Date: Thu, 12 Jul 2018 04:17:05 +0000 Subject: [PATCH 10/38] Fix dynamic Python items() in deleteIntfs() --- mininet/node.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mininet/node.py b/mininet/node.py index 25accc5..942644b 100644 --- a/mininet/node.py +++ b/mininet/node.py @@ -411,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 @@ -500,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() From e28348f6cd7542b0cb7dda9bc23c2c65f90b236d Mon Sep 17 00:00:00 2001 From: Bob Lantz Date: Thu, 12 Jul 2018 04:21:44 +0000 Subject: [PATCH 11/38] Indent error. --- mininet/util.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mininet/util.py b/mininet/util.py index 851df34..585e3af 100644 --- a/mininet/util.py +++ b/mininet/util.py @@ -32,8 +32,8 @@ try: "Let pexpect work with Python3 utf-8 strings" if Python3: kwargs.update( encoding='utf-8' ) - return oldspawn( self, *args, **kwargs ) - oldpexpect.spawn = spawn + return oldspawn( self, *args, **kwargs ) + oldpexpect.spawn = spawn except: pass From 2e00a7de97409f61e99f815359cc6522893e2f7c Mon Sep 17 00:00:00 2001 From: Bob Lantz Date: Thu, 12 Jul 2018 06:33:17 +0000 Subject: [PATCH 12/38] Python 3 compatibility --- mininet/node.py | 1 + mininet/test/test_switchdpidassignment.py | 32 ++++++++++++++--------- 2 files changed, 20 insertions(+), 13 deletions(-) diff --git a/mininet/node.py b/mininet/node.py index 942644b..1458428 100644 --- a/mininet/node.py +++ b/mininet/node.py @@ -901,6 +901,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.' ) diff --git a/mininet/test/test_switchdpidassignment.py b/mininet/test/test_switchdpidassignment.py index 9364e64..2bd7a49 100755 --- a/mininet/test/test_switchdpidassignment.py +++ b/mininet/test/test_switchdpidassignment.py @@ -30,10 +30,11 @@ 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 +45,35 @@ 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 +100,4 @@ class testSwitchUserspace( TestSwitchDpidAssignmentOVS ): if __name__ == '__main__': setLogLevel( 'warning' ) unittest.main() + cleanup() From 3eef584c6b7c1b07879463138728acb48521ee42 Mon Sep 17 00:00:00 2001 From: Bob Lantz Date: Thu, 12 Jul 2018 06:34:13 +0000 Subject: [PATCH 13/38] Python 3 compatibility And some Ubuntu 18 compat: - ifconfig's output has changed - relaxing timing as it seems first ping is slower --- mininet/test/test_walkthrough.py | 25 ++++++++++++++++--------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/mininet/test/test_walkthrough.py b/mininet/test/test_walkthrough.py index 8738537..4f90ce9 100755 --- a/mininet/test/test_walkthrough.py +++ b/mininet/test/test_walkthrough.py @@ -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: From 2283bb01e679478d325ffba4e6183a8110dcedbf Mon Sep 17 00:00:00 2001 From: Bob Lantz Date: Thu, 12 Jul 2018 07:16:27 +0000 Subject: [PATCH 14/38] Python3 compat and add timeout for connect --- examples/test/test_baresshd.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/examples/test/test_baresshd.py b/examples/test/test_baresshd.py index 3d5df93..7888e74 100755 --- a/examples/test/test_baresshd.py +++ b/examples/test/test_baresshd.py @@ -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() ) From 356b024d6bf16631a65a42e0d9cfb66783d54813 Mon Sep 17 00:00:00 2001 From: Bob Lantz Date: Thu, 12 Jul 2018 07:23:28 +0000 Subject: [PATCH 15/38] Wrap reads in pmonitor() for python3 --- mininet/util.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mininet/util.py b/mininet/util.py index 585e3af..42d2c04 100644 --- a/mininet/util.py +++ b/mininet/util.py @@ -422,9 +422,9 @@ def pmonitor(popens, timeoutms=500, readline=True, if readline: # Attempt to read a line of output # This blocks until we receive a newline! - line = popen.stdout.readline() + line = decode( popen.stdout.readline() ) else: - line = popen.stdout.read( readmax ) + line = decode( popen.stdout.read( readmax ) ) yield host, line # Check for EOF elif event & POLLHUP: From f94ee8ec97ff2d13db162ede9eddafcdba5d5d22 Mon Sep 17 00:00:00 2001 From: Bob Lantz Date: Thu, 12 Jul 2018 08:18:57 +0000 Subject: [PATCH 16/38] Redesign pmonitor() This is tricky to get right, but I think it is correct to drain the buffers like this. --- mininet/util.py | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/mininet/util.py b/mininet/util.py index 42d2c04..2fcacde 100644 --- a/mininet/util.py +++ b/mininet/util.py @@ -412,6 +412,11 @@ 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: @@ -419,15 +424,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 = decode( popen.stdout.readline() ) - else: - line = decode( 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: From af4921adc5bdb340737ea957a86f8abc754dc69b Mon Sep 17 00:00:00 2001 From: Bob Lantz Date: Thu, 12 Jul 2018 08:20:36 +0000 Subject: [PATCH 17/38] Minor changes for Python 3 items() vs. iteritems() and decode() bytes in monitorFiles Probably not strictly correct if bytes are split - we still need to deal with this properly. --- examples/cluster.py | 2 +- examples/controlnet.py | 2 +- examples/multipoll.py | 5 +++-- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/examples/cluster.py b/examples/cluster.py index 8959868..078dca4 100755 --- a/examples/cluster.py +++ b/examples/cluster.py @@ -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 diff --git a/examples/controlnet.py b/examples/controlnet.py index 9633089..e5cf765 100755 --- a/examples/controlnet.py +++ b/examples/controlnet.py @@ -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 diff --git a/examples/multipoll.py b/examples/multipoll.py index 0b735ba..bb1c9d3 100755 --- a/examples/multipoll.py +++ b/examples/multipoll.py @@ -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, '' From 1a134cb4d236bd83536e67281a2fd3177fc4ea25 Mon Sep 17 00:00:00 2001 From: Bob Lantz Date: Thu, 12 Jul 2018 08:24:10 +0000 Subject: [PATCH 18/38] 80 columns --- examples/test/test_baresshd.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/test/test_baresshd.py b/examples/test/test_baresshd.py index 7888e74..251b75d 100755 --- a/examples/test/test_baresshd.py +++ b/examples/test/test_baresshd.py @@ -59,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' ) From 6a387faa048bd7208c1a62e9d28b2b9aa5d957da Mon Sep 17 00:00:00 2001 From: Bob Lantz Date: Thu, 12 Jul 2018 08:25:44 +0000 Subject: [PATCH 19/38] Fix whitespace error --- examples/test/test_simpleperf.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/test/test_simpleperf.py b/examples/test/test_simpleperf.py index f8e94d5..027c039 100755 --- a/examples/test/test_simpleperf.py +++ b/examples/test/test_simpleperf.py @@ -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 ) From e37afe996b143d0eb78ad5f6b34be97337e928c3 Mon Sep 17 00:00:00 2001 From: Bob Lantz Date: Thu, 12 Jul 2018 08:26:03 +0000 Subject: [PATCH 20/38] First crack at Python 3 compatibility Mostly we just use mininet.util.pexpect so that we can use unicode strings without changing calls to pexpect.spawn() --- examples/test/test_bind.py | 2 +- examples/test/test_clusterSanity.py | 2 +- examples/test/test_controllers.py | 2 +- examples/test/test_controlnet.py | 2 +- examples/test/test_cpu.py | 2 +- examples/test/test_emptynet.py | 2 +- examples/test/test_hwintf.py | 2 +- examples/test/test_intfoptions.py | 2 +- examples/test/test_limit.py | 2 +- examples/test/test_linearbandwidth.py | 2 +- examples/test/test_linuxrouter.py | 2 +- examples/test/test_multilink.py | 2 +- examples/test/test_multiping.py | 2 +- examples/test/test_multipoll.py | 2 +- examples/test/test_multitest.py | 2 +- examples/test/test_nat.py | 2 +- examples/test/test_natnet.py | 2 +- examples/test/test_numberedports.py | 2 +- examples/test/test_popen.py | 2 +- examples/test/test_scratchnet.py | 2 +- examples/test/test_simpleperf.py | 2 +- examples/test/test_sshd.py | 2 +- examples/test/test_tree1024.py | 2 +- examples/test/test_treeping64.py | 2 +- examples/test/test_vlanhost.py | 2 +- 25 files changed, 25 insertions(+), 25 deletions(-) diff --git a/examples/test/test_bind.py b/examples/test/test_bind.py index dcc9fbf..e423e39 100755 --- a/examples/test/test_bind.py +++ b/examples/test/test_bind.py @@ -5,7 +5,7 @@ Tests for bind.py """ import unittest -import pexpect +from mininet.util import pexpect class testBind( unittest.TestCase ): diff --git a/examples/test/test_clusterSanity.py b/examples/test/test_clusterSanity.py index cdbc390..13e51e2 100755 --- a/examples/test/test_clusterSanity.py +++ b/examples/test/test_clusterSanity.py @@ -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 ): diff --git a/examples/test/test_controllers.py b/examples/test/test_controllers.py index b9b93d9..abc79eb 100755 --- a/examples/test/test_controllers.py +++ b/examples/test/test_controllers.py @@ -5,7 +5,7 @@ Tests for controllers.py and controllers2.py """ import unittest -import pexpect +from mininet.util import pexpect class testControllers( unittest.TestCase ): diff --git a/examples/test/test_controlnet.py b/examples/test/test_controlnet.py index 3a99ea4..ba22740 100755 --- a/examples/test/test_controlnet.py +++ b/examples/test/test_controlnet.py @@ -5,7 +5,7 @@ Test for controlnet.py """ import unittest -import pexpect +from mininet.util import pexpect class testControlNet( unittest.TestCase ): diff --git a/examples/test/test_cpu.py b/examples/test/test_cpu.py index c7a6266..8b36c6e 100755 --- a/examples/test/test_cpu.py +++ b/examples/test/test_cpu.py @@ -15,7 +15,7 @@ cfs 10% 1.29e+09 """ import unittest -import pexpect +from mininet.util import pexpect import sys class testCPU( unittest.TestCase ): diff --git a/examples/test/test_emptynet.py b/examples/test/test_emptynet.py index f8e7c4a..aead619 100755 --- a/examples/test/test_emptynet.py +++ b/examples/test/test_emptynet.py @@ -5,7 +5,7 @@ Test for emptynet.py """ import unittest -import pexpect +from mininet.util import pexpect class testEmptyNet( unittest.TestCase ): diff --git a/examples/test/test_hwintf.py b/examples/test/test_hwintf.py index 843fc40..e242ca3 100755 --- a/examples/test/test_hwintf.py +++ b/examples/test/test_hwintf.py @@ -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 diff --git a/examples/test/test_intfoptions.py b/examples/test/test_intfoptions.py index 6f1ebec..2d72471 100755 --- a/examples/test/test_intfoptions.py +++ b/examples/test/test_intfoptions.py @@ -5,7 +5,7 @@ Test for intfOptions.py """ import unittest -import pexpect +from mininet.util import pexpect import sys class testIntfOptions( unittest.TestCase ): diff --git a/examples/test/test_limit.py b/examples/test/test_limit.py index 2052e7e..47ceaa1 100755 --- a/examples/test/test_limit.py +++ b/examples/test/test_limit.py @@ -5,7 +5,7 @@ Test for limit.py """ import unittest -import pexpect +from mininet.util import pexpect import sys class testLimit( unittest.TestCase ): diff --git a/examples/test/test_linearbandwidth.py b/examples/test/test_linearbandwidth.py index a18d97a..9e876bc 100755 --- a/examples/test/test_linearbandwidth.py +++ b/examples/test/test_linearbandwidth.py @@ -5,7 +5,7 @@ Test for linearbandwidth.py """ import unittest -import pexpect +from mininet.util import pexpect import sys class testLinearBandwidth( unittest.TestCase ): diff --git a/examples/test/test_linuxrouter.py b/examples/test/test_linuxrouter.py index 60af201..f9cf648 100644 --- a/examples/test/test_linuxrouter.py +++ b/examples/test/test_linuxrouter.py @@ -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 ): diff --git a/examples/test/test_multilink.py b/examples/test/test_multilink.py index 4bea8b0..0925548 100755 --- a/examples/test/test_multilink.py +++ b/examples/test/test_multilink.py @@ -6,7 +6,7 @@ validates mininet interfaces against systems interfaces ''' import unittest -import pexpect +from mininet.util import pexpect class testMultiLink( unittest.TestCase ): diff --git a/examples/test/test_multiping.py b/examples/test/test_multiping.py index bb5440a..2519121 100755 --- a/examples/test/test_multiping.py +++ b/examples/test/test_multiping.py @@ -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 ): diff --git a/examples/test/test_multipoll.py b/examples/test/test_multipoll.py index 61e5cec..8815033 100755 --- a/examples/test/test_multipoll.py +++ b/examples/test/test_multipoll.py @@ -5,7 +5,7 @@ Test for multipoll.py """ import unittest -import pexpect +from mininet.util import pexpect class testMultiPoll( unittest.TestCase ): diff --git a/examples/test/test_multitest.py b/examples/test/test_multitest.py index 09172c5..4352b8e 100755 --- a/examples/test/test_multitest.py +++ b/examples/test/test_multitest.py @@ -5,7 +5,7 @@ Test for multitest.py """ import unittest -import pexpect +from mininet.util import pexpect class testMultiTest( unittest.TestCase ): diff --git a/examples/test/test_nat.py b/examples/test/test_nat.py index c4bb096..865615f 100755 --- a/examples/test/test_nat.py +++ b/examples/test/test_nat.py @@ -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 diff --git a/examples/test/test_natnet.py b/examples/test/test_natnet.py index 3addc92..4b9e099 100644 --- a/examples/test/test_natnet.py +++ b/examples/test/test_natnet.py @@ -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 ): diff --git a/examples/test/test_numberedports.py b/examples/test/test_numberedports.py index 81ee187..ea7d61e 100755 --- a/examples/test/test_numberedports.py +++ b/examples/test/test_numberedports.py @@ -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 diff --git a/examples/test/test_popen.py b/examples/test/test_popen.py index c7f83f0..f41d531 100755 --- a/examples/test/test_popen.py +++ b/examples/test/test_popen.py @@ -5,7 +5,7 @@ Test for popen.py and popenpoll.py """ import unittest -import pexpect +from mininet.util import pexpect class testPopen( unittest.TestCase ): diff --git a/examples/test/test_scratchnet.py b/examples/test/test_scratchnet.py index 43d8286..3d44955 100755 --- a/examples/test/test_scratchnet.py +++ b/examples/test/test_scratchnet.py @@ -5,7 +5,7 @@ Test for scratchnet.py """ import unittest -import pexpect +from mininet.util import pexpect class testScratchNet( unittest.TestCase ): diff --git a/examples/test/test_simpleperf.py b/examples/test/test_simpleperf.py index 027c039..93a36aa 100755 --- a/examples/test/test_simpleperf.py +++ b/examples/test/test_simpleperf.py @@ -5,7 +5,7 @@ Test for simpleperf.py """ import unittest -import pexpect +from mininet.util import pexpect import sys from mininet.log import setLogLevel diff --git a/examples/test/test_sshd.py b/examples/test/test_sshd.py index 6a39bba..193b1ea 100755 --- a/examples/test/test_sshd.py +++ b/examples/test/test_sshd.py @@ -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 ): diff --git a/examples/test/test_tree1024.py b/examples/test/test_tree1024.py index e26af2c..06f8e49 100755 --- a/examples/test/test_tree1024.py +++ b/examples/test/test_tree1024.py @@ -5,7 +5,7 @@ Test for tree1024.py """ import unittest -import pexpect +from mininet.util import pexpect import sys class testTree1024( unittest.TestCase ): diff --git a/examples/test/test_treeping64.py b/examples/test/test_treeping64.py index ae02afc..8c346cd 100755 --- a/examples/test/test_treeping64.py +++ b/examples/test/test_treeping64.py @@ -5,7 +5,7 @@ Test for treeping64.py """ import unittest -import pexpect +from mininet.util import pexpect import sys class testTreePing64( unittest.TestCase ): diff --git a/examples/test/test_vlanhost.py b/examples/test/test_vlanhost.py index d68448f..6781cce 100644 --- a/examples/test/test_vlanhost.py +++ b/examples/test/test_vlanhost.py @@ -5,7 +5,7 @@ Test for vlanhost.py """ import unittest -import pexpect +from mininet.util import pexpect import sys from mininet.util import quietRun From 08a59783f4956f68c06b1e69d3dac55709025e95 Mon Sep 17 00:00:00 2001 From: Bob Lantz Date: Thu, 12 Jul 2018 09:03:16 +0000 Subject: [PATCH 21/38] Decode readline() and call cleanup() for python 3 Python 3.6 crashes if there are leftover processes, even if they shut down later... --- examples/cpu.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/examples/cpu.py b/examples/cpu.py index c83177b..accb0ea 100755 --- a/examples/cpu.py +++ b/examples/cpu.py @@ -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() From 0d9a6796df731ea0631ba1aa01e5101e670b1692 Mon Sep 17 00:00:00 2001 From: Bob Lantz Date: Thu, 12 Jul 2018 09:07:16 +0000 Subject: [PATCH 22/38] Add Python 2.7 and Python 3.6 --- .travis.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.travis.yml b/.travis.yml index 735a97c..3ebe8fe 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,6 +1,10 @@ language: python sudo: required +python: + - "2.7 + - "3.6" + matrix: include: - dist: trusty From 3a0ef258d11cb3279accf98c1c64c204ae98c35f Mon Sep 17 00:00:00 2001 From: Bob Lantz Date: Thu, 12 Jul 2018 19:16:08 +0000 Subject: [PATCH 23/38] grep cgroup /proc/mounts in mountCgroup --- mininet/util.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mininet/util.py b/mininet/util.py index 2fcacde..ae552f4 100644 --- a/mininet/util.py +++ b/mininet/util.py @@ -495,7 +495,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 From 7b48460e9e4f3ff54ac9165579b293d08989b409 Mon Sep 17 00:00:00 2001 From: Bob Lantz Date: Tue, 24 Jul 2018 22:58:44 -0700 Subject: [PATCH 24/38] Change pexpect hackery to use a custom class So the original pexpect is unperturbed --- mininet/util.py | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/mininet/util.py b/mininet/util.py index ae552f4..7397651 100644 --- a/mininet/util.py +++ b/mininet/util.py @@ -16,7 +16,7 @@ import sys # Python 2/3 compatibility Python3 = sys.version_info[0] == 3 -BaseString = str if Python3 else basestring +BaseString = str if Python3 else str.__base__ Encoding = 'utf-8' if Python3 else None def decode( s ): "Decode a byte string if needed for Python 3" @@ -24,19 +24,22 @@ def decode( s ): def encode( s ): "Encode a byte string if needed for Python 3" return s.encode( Encoding ) if Python3 else s -# Make pexpect compatible with Python 3 strings try: import pexpect as oldpexpect - pexpect, oldspawn = oldpexpect, oldpexpect.spawn - def spawn( self, *args, **kwargs): - "Let pexpect work with Python3 utf-8 strings" - if Python3: - kwargs.update( encoding='utf-8' ) - return oldspawn( self, *args, **kwargs ) - oldpexpect.spawn = spawn + class Pexpect( object ): + "Custom pexpect that is compatible with str" + def spawn( self, *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: pass + # Command execution support def run( cmd ): From f98154a32346f8dc54005065f621b8be918d6d02 Mon Sep 17 00:00:00 2001 From: Bob Lantz Date: Wed, 25 Jul 2018 14:43:54 -0700 Subject: [PATCH 25/38] Decode subprocess output for python3 --- util/versioncheck.py | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/util/versioncheck.py b/util/versioncheck.py index c418bf9..0a4af2b 100755 --- a/util/versioncheck.py +++ b/util/versioncheck.py @@ -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 From 2ac4f92af38a7dd104a5a0171ab8653a0638eb62 Mon Sep 17 00:00:00 2001 From: Bob Lantz Date: Wed, 25 Jul 2018 18:32:37 -0700 Subject: [PATCH 26/38] Add PYTHON variable for python2/python3 make install --- Makefile | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/Makefile b/Makefile index d8c828e..c97ea7a 100644 --- a/Makefile +++ b/Makefile @@ -2,7 +2,8 @@ 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 @@ -54,13 +55,13 @@ install-manpages: $(MANPAGES) install -D -t $(MANDIR) $(MANPAGES) install: install-mnexec install-manpages - python setup.py install + $(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) From f4490069ca9aec97af441e014f5444fc8ae52058 Mon Sep 17 00:00:00 2001 From: Bob Lantz Date: Wed, 25 Jul 2018 18:53:54 -0700 Subject: [PATCH 27/38] Add PYTHON and python2/python3 support Perhaps we should have an option for py2/p3, but for now we detect the default python version and it can also be specified via the PYTHON env var. --- util/install.sh | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/util/install.sh b/util/install.sh index 749accc..1ddaaf9 100755 --- a/util/install.sh +++ b/util/install.sh @@ -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,19 @@ 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 + 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 fi echo "Installing Mininet core" pushd $MININET_DIR/mininet - sudo make install + sudo PYTHON=${PYTHON} make install popd } From 81027047262822eccf5644270493a187c88b02ce Mon Sep 17 00:00:00 2001 From: Bob Lantz Date: Wed, 25 Jul 2018 19:42:08 -0700 Subject: [PATCH 28/38] Pass code check (14.04) --- bin/mn | 3 ++- mininet/node.py | 5 ++--- mininet/test/test_switchdpidassignment.py | 2 -- mininet/util.py | 13 +++++++++---- 4 files changed, 13 insertions(+), 10 deletions(-) diff --git a/bin/mn b/bin/mn index df82b89..4c5cf3d 100755 --- a/bin/mn +++ b/bin/mn @@ -186,7 +186,8 @@ class MininetRunner( object ): for fileName in files: customs = {} if os.path.isfile( fileName ): - exec( compile ( open( fileName ).read(), fileName, 'exec' ), + # pylint: disable=exec-used + exec( compile( open( fileName ).read(), fileName, 'exec' ), customs, customs ) for name, val in customs.items(): self.setCustom( name, val ) diff --git a/mininet/node.py b/mininet/node.py index 1458428..8b6f8cf 100644 --- a/mininet/node.py +++ b/mininet/node.py @@ -89,7 +89,7 @@ class Node( object ): self.inNamespace = params.get( 'inNamespace', inNamespace ) # Python 3 complains if we don't wait for shell exit - self.waitExited = params.get( 'waitExited', Python3==True ) + self.waitExited = params.get( 'waitExited', Python3 == True ) # Stash configuration parameters for future reference self.params = params @@ -207,7 +207,7 @@ class Node( object ): # Leave this is as an instance method for now assert self popen = Popen( cmd, **params ) - debug( '_popen', cmd, popen.pid ) + debug( '_popen', cmd, popen.pid ) return popen def cleanup( self ): @@ -732,7 +732,6 @@ class CPULimitedHost( Host ): super( CPULimitedHost, self ).cleanup() retry( retries=3, delaySecs=.1, fn=self.cgroupDel ) - _rtGroupSched = False # internal class var: Is CONFIG_RT_GROUP_SCHED set? @classmethod diff --git a/mininet/test/test_switchdpidassignment.py b/mininet/test/test_switchdpidassignment.py index 2bd7a49..62ef493 100755 --- a/mininet/test/test_switchdpidassignment.py +++ b/mininet/test/test_switchdpidassignment.py @@ -35,7 +35,6 @@ class TestSwitchDpidAssignmentOVS( unittest.TestCase ): self.assertEqual( switch.defaultDpid(), switch.dpid ) net.stop() - def dpidFrom( self, num ): "Compute default dpid from number" fmt = ( '%0' + str( self.switchClass.dpidLen ) + 'x' ) @@ -50,7 +49,6 @@ class TestSwitchDpidAssignmentOVS( unittest.TestCase ): 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 diff --git a/mininet/util.py b/mininet/util.py index 7397651..15a7b52 100644 --- a/mininet/util.py +++ b/mininet/util.py @@ -16,7 +16,7 @@ import sys # Python 2/3 compatibility Python3 = sys.version_info[0] == 3 -BaseString = str if Python3 else str.__base__ +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" @@ -26,17 +26,20 @@ def encode( s ): return s.encode( Encoding ) if Python3 else s try: import pexpect as oldpexpect + class Pexpect( object ): "Custom pexpect that is compatible with str" - def spawn( self, *args, **kwargs): + @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: +except ImportError: pass @@ -84,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 @@ -415,11 +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: From 3e81ea7455a236450673b66777c79f7649465562 Mon Sep 17 00:00:00 2001 From: Hantao Cui Date: Wed, 25 Jul 2018 20:13:53 -0700 Subject: [PATCH 29/38] miniedit.py changes for Python 3 --- examples/miniedit.py | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/examples/miniedit.py b/examples/miniedit.py index 54c31f8..309becd 100755 --- a/examples/miniedit.py +++ b/examples/miniedit.py @@ -20,24 +20,30 @@ 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, +from subprocess import call + +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 +from tkinter.ttk import Notebook +from tkinter.messagebox import showerror +from tkinter import font as tkFont + +if sys.version_info[0] == 2: + import tkSimpleDialog + import tkFileDialog +elif sys.version_info[0] == 3: + 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: From afa83cd5ae2ccc8964ff2403101ca0a0dfa915d1 Mon Sep 17 00:00:00 2001 From: Bob Lantz Date: Wed, 25 Jul 2018 21:32:09 -0700 Subject: [PATCH 30/38] Restore python 2 compatibility --- examples/miniedit.py | 37 +++++++++++++++++++++++-------------- 1 file changed, 23 insertions(+), 14 deletions(-) diff --git a/examples/miniedit.py b/examples/miniedit.py index 309becd..bb0ec99 100755 --- a/examples/miniedit.py +++ b/examples/miniedit.py @@ -24,19 +24,28 @@ import sys from optparse import OptionParser from subprocess import call -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 - 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 -elif sys.version_info[0] == 3: +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 @@ -1414,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): @@ -1841,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 @@ -2881,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'] @@ -3217,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 ) From 8933c996cbcb1590548413306206eca066886ae0 Mon Sep 17 00:00:00 2001 From: Bob Lantz Date: Wed, 25 Jul 2018 21:39:48 -0700 Subject: [PATCH 31/38] Add python-tk/python3-tk --- util/install.sh | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/util/install.sh b/util/install.sh index 1ddaaf9..492c46c 100755 --- a/util/install.sh +++ b/util/install.sh @@ -156,11 +156,12 @@ function mn_deps { elif [ "$DIST" = "SUSE LINUX" ]; then $install gcc make socat psmisc xterm openssh iperf \ iproute telnet ${PYPKG}-setuptools libcgroup-tools \ - ethtool help2man python-pyflakes python3-pylint python-pep8 ${PYPKG}-pexpect + 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 \ cgroup-bin ethtool help2man pyflakes pylint pep8 \ - ${PYPKG}-setuptools ${PYPKG}-pexpect + ${PYPKG}-setuptools ${PYPKG}-pexpect ${PYPKG}-tk fi echo "Installing Mininet core" From 1e9ca5f7fc39371a0ee7410320b2ac4ea04420ad Mon Sep 17 00:00:00 2001 From: Bob Lantz Date: Wed, 25 Jul 2018 22:03:30 -0700 Subject: [PATCH 32/38] Fix missing quote in .travis.yml --- .travis.yml | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/.travis.yml b/.travis.yml index 3ebe8fe..d85fc42 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,18 +2,13 @@ language: python sudo: required python: - - "2.7 - - "3.6" +- "2.7" +- "3.6" matrix: include: - dist: 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: - sudo apt-get update -qq @@ -32,4 +27,5 @@ script: notifications: email: on_success: never -# More details: https://docs.travis-ci.com/user/notifications#Configuring-email-notifications + +# More details: https://docs.travis-ci.com/user/notifications From 71f3931f2fdd9b93748e95f7e9f4665c6f7494cd Mon Sep 17 00:00:00 2001 From: Bob Lantz Date: Wed, 25 Jul 2018 22:23:06 -0700 Subject: [PATCH 33/38] Remove redundancies in miniedit; pass code check --- examples/miniedit.py | 4 ++-- mininet/node.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/miniedit.py b/examples/miniedit.py index bb0ec99..c1057c6 100755 --- a/examples/miniedit.py +++ b/examples/miniedit.py @@ -24,6 +24,7 @@ import sys from optparse import OptionParser from subprocess import call +# pylint: disable=import-error if sys.version_info[0] == 2: from Tkinter import ( Frame, Label, LabelFrame, Entry, OptionMenu, Checkbutton, Menu, Toplevel, Button, BitmapImage, @@ -32,11 +33,9 @@ if sys.version_info[0] == 2: 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, @@ -48,6 +47,7 @@ else: 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 json diff --git a/mininet/node.py b/mininet/node.py index 8b6f8cf..3c61393 100644 --- a/mininet/node.py +++ b/mininet/node.py @@ -89,7 +89,7 @@ class Node( object ): self.inNamespace = params.get( 'inNamespace', inNamespace ) # Python 3 complains if we don't wait for shell exit - self.waitExited = params.get( 'waitExited', Python3 == True ) + self.waitExited = params.get( 'waitExited', Python3 ) # Stash configuration parameters for future reference self.params = params From 17f9756e5c9a716d46fdb5009332448108d0c91d Mon Sep 17 00:00:00 2001 From: Bob Lantz Date: Wed, 25 Jul 2018 22:32:35 -0700 Subject: [PATCH 34/38] sudo interferes with travis python 3 environment --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index d85fc42..953f3fb 100644 --- a/.travis.yml +++ b/.travis.yml @@ -13,7 +13,7 @@ matrix: before_install: - sudo apt-get update -qq - sudo apt-get install -qq vlan -- sudo util/install.sh -n +- util/install.sh -n install: - bash -c "if [ `lsb_release -rs` == '14.04' ]; then make codecheck; fi" From 323989953b245733d9df9c8bdac51e6b415b8668 Mon Sep 17 00:00:00 2001 From: Bob Lantz Date: Wed, 25 Jul 2018 22:39:07 -0700 Subject: [PATCH 35/38] Try to fix pylint errors ;-p --- mininet/util.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/mininet/util.py b/mininet/util.py index 15a7b52..cb09f27 100644 --- a/mininet/util.py +++ b/mininet/util.py @@ -25,7 +25,10 @@ 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" From c7deeae11cc1c9b8b7ff277deed1682809a326dc Mon Sep 17 00:00:00 2001 From: Bob Lantz Date: Wed, 25 Jul 2018 22:50:22 -0700 Subject: [PATCH 36/38] Add sanity check for python mn version --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index 953f3fb..5191096 100644 --- a/.travis.yml +++ b/.travis.yml @@ -20,6 +20,7 @@ install: - sudo util/install.sh -fnvw script: +- echo "px import sys; print(sys.version_info)" | sudo mn -v output - sudo mn --test pingall - sudo python mininet/test/runner.py -v -quick - sudo python examples/test/runner.py -v -quick From d96b35694ab257fffbe779830a42f012e524346f Mon Sep 17 00:00:00 2001 From: Bob Lantz Date: Wed, 25 Jul 2018 23:01:28 -0700 Subject: [PATCH 37/38] Use appropriate python version --- .travis.yml | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/.travis.yml b/.travis.yml index 5191096..8c9b987 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,29 +1,32 @@ language: python sudo: required -python: -- "2.7" -- "3.6" - matrix: include: - dist: trusty + python: 2.7 + env: dist="14.04 LTS trusty" + - dist: trusty + python: 3.6 env: dist="14.04 LTS trusty" before_install: - sudo apt-get update -qq - sudo apt-get install -qq vlan -- util/install.sh -n +- PYTHON=`which python` util/install.sh -n install: - 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: -- echo "px import sys; print(sys.version_info)" | sudo mn -v output -- sudo mn --test pingall -- sudo python mininet/test/runner.py -v -quick -- sudo python examples/test/runner.py -v -quick +- alias sudo="sudo env PATH=$PATH" +- export PYTHON=`which python` +- 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: email: From b70eed69d37c8722edf15d474a892d0a9c580244 Mon Sep 17 00:00:00 2001 From: Bob Lantz Date: Thu, 26 Jul 2018 13:52:15 -0700 Subject: [PATCH 38/38] Fix typo (50s -> 50ms) --- mininet/test/test_walkthrough.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mininet/test/test_walkthrough.py b/mininet/test/test_walkthrough.py index 4f90ce9..c322c13 100755 --- a/mininet/test/test_walkthrough.py +++ b/mininet/test/test_walkthrough.py @@ -228,7 +228,7 @@ class testWalkthrough( unittest.TestCase ): r'([\d\.]+)/([\d\.]+)/([\d\.]+)/([\d\.]+) ms' ) delay = float( p.match.group( 2 ) ) self.assertTrue( delay >= 40, 'Delay < 40ms' ) - self.assertTrue( delay <= 50, 'Delay > 50s' ) + self.assertTrue( delay <= 50, 'Delay > 50ms' ) p.expect( self.prompt ) p.sendline( 'exit' ) p.wait()