Use incremental UTF-8 decoder for buffered reading

This commit is contained in:
Ben Frankel
2019-07-11 16:57:09 -07:00
parent de28f67a97
commit 10e758e80a
2 changed files with 68 additions and 34 deletions
+12 -9
View File
@@ -63,7 +63,7 @@ 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, BaseString, decode, numCores, retry, mountCgroups, BaseString, decode,
encode, Python3, which ) encode, getincrementaldecoder, Python3, which )
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
@@ -106,6 +106,9 @@ class Node( object ):
self.waiting = False self.waiting = False
self.readbuf = '' self.readbuf = ''
# Incremental decoder for buffered reading
self.decoder = getincrementaldecoder()
# Start command interpreter shell # Start command interpreter shell
self.master, self.slave = None, None # pylint self.master, self.slave = None, None # pylint
self.startShell() self.startShell()
@@ -229,19 +232,19 @@ class Node( object ):
# Subshell I/O, commands and control # Subshell I/O, commands and control
def read( self, maxbytes=1024 ): def read( self, size=1024 ):
"""Buffered read from node, potentially blocking. """Buffered read from node, potentially blocking.
maxbytes: maximum number of bytes to return""" size: maximum number of characters to return"""
count = len( self.readbuf ) count = len( self.readbuf )
if count < maxbytes: if count < size:
data = decode( os.read( self.stdout.fileno(), maxbytes - count ) ) data = os.read( self.stdout.fileno(), size - count )
self.readbuf += data self.readbuf += self.decoder.decode( data )
if maxbytes >= len( self.readbuf ): if size >= len( self.readbuf ):
result = self.readbuf result = self.readbuf
self.readbuf = '' self.readbuf = ''
else: else:
result = self.readbuf[ :maxbytes ] result = self.readbuf[ :size ]
self.readbuf = self.readbuf[ maxbytes: ] self.readbuf = self.readbuf[ size: ]
return result return result
def readline( self ): def readline( self ):
+56 -25
View File
@@ -13,23 +13,48 @@ from os import O_NONBLOCK
import os import os
from functools import partial from functools import partial
import sys import sys
import codecs
# Python 2/3 compatibility # Python 2/3 compatibility
Python3 = sys.version_info[0] == 3 Python3 = sys.version_info[0] == 3
BaseString = str if Python3 else getattr( str, '__base__' ) BaseString = str if Python3 else getattr( str, '__base__' )
Encoding = 'utf-8' if Python3 else None Encoding = 'utf-8' if Python3 else None
def decode( s ): class NullCodec( object ):
"Decode a byte string if needed for Python 3" "Null codec for Python 2"
return s.decode( Encoding ) if Python3 else s @staticmethod
def encode( s ): def decode( buf ):
"Encode a byte string if needed for Python 3" "Null decode"
return s.encode( Encoding ) if Python3 else s return buf
@staticmethod
def encode( buf ):
"Null encode"
return buf
if Python3:
def decode( buf ):
"Decode buffer for Python 3"
return buf.decode( Encoding )
def encode( buf ):
"Encode buffer for Python 3"
return buf.encode( Encoding )
getincrementaldecoder = codecs.getincrementaldecoder( Encoding )
else:
decode, encode = NullCodec.decode, NullCodec.encode
def getincrementaldecoder():
"Return null codec for Python 2"
return NullCodec
try: try:
# pylint: disable=import-error # pylint: disable=import-error
oldpexpect = None oldpexpect = None
import pexpect as oldpexpect import pexpect as oldpexpect
# pylint: enable=import-error
# pylint: enable=import-error
class Pexpect( object ): class Pexpect( object ):
"Custom pexpect that is compatible with str" "Custom pexpect that is compatible with str"
@staticmethod @staticmethod
@@ -119,20 +144,21 @@ def errRun( *cmd, **kwargs ):
out, err = '', '' out, err = '', ''
poller = poll() poller = poll()
poller.register( popen.stdout, POLLIN ) poller.register( popen.stdout, POLLIN )
fdtofile = { popen.stdout.fileno(): popen.stdout } fdToFile = { popen.stdout.fileno(): popen.stdout }
fdToDecoder = { popen.stdout.fileno(): getincrementaldecoder() }
outDone, errDone = False, True outDone, errDone = False, True
if popen.stderr: if popen.stderr:
fdtofile[ popen.stderr.fileno() ] = popen.stderr fdToFile[ popen.stderr.fileno() ] = popen.stderr
fdToDecoder[ popen.stderr.fileno() ] = getincrementaldecoder()
poller.register( popen.stderr, POLLIN ) poller.register( popen.stderr, POLLIN )
errDone = False errDone = False
while not outDone or not errDone: while not outDone or not errDone:
readable = poller.poll() readable = poller.poll()
for fd, event in readable: for fd, event in readable:
f = fdtofile[ fd ] f = fdToFile[ fd ]
decoder = fdToDecoder[ fd ]
if event & POLLIN: if event & POLLIN:
data = f.read( 1024 ) data = decoder.decode( 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:
@@ -187,8 +213,10 @@ def isShellBuiltin( cmd ):
cmd = cmd[ :space] cmd = cmd[ :space]
return cmd in isShellBuiltin.builtIns return cmd in isShellBuiltin.builtIns
isShellBuiltin.builtIns = None isShellBuiltin.builtIns = None
# Interface management # Interface management
# #
# Interfaces are managed as strings which are simply the # Interfaces are managed as strings which are simply the
@@ -375,7 +403,7 @@ def netParse( ipstr ):
if '/' in ipstr: if '/' in ipstr:
ip, pf = ipstr.split( '/' ) ip, pf = ipstr.split( '/' )
prefixLen = int( pf ) prefixLen = int( pf )
#if no prefix is specified, set the prefix to 24 # if no prefix is specified, set the prefix to 24
else: else:
ip = ipstr ip = ipstr
prefixLen = 24 prefixLen = 24
@@ -418,9 +446,11 @@ def pmonitor(popens, timeoutms=500, readline=True,
terminates: when all EOFs received""" terminates: when all EOFs received"""
poller = poll() poller = poll()
fdToHost = {} fdToHost = {}
fdToDecoder = {}
for host, popen in popens.items(): for host, popen in popens.items():
fd = popen.stdout.fileno() fd = popen.stdout.fileno()
fdToHost[ fd ] = host fdToHost[ fd ] = host
fdToDecoder[ fd ] = getincrementaldecoder()
poller.register( fd, POLLIN | POLLHUP ) poller.register( fd, POLLIN | POLLHUP )
flags = fcntl( fd, F_GETFL ) flags = fcntl( fd, F_GETFL )
fcntl( fd, F_SETFL, flags | O_NONBLOCK ) fcntl( fd, F_SETFL, flags | O_NONBLOCK )
@@ -429,13 +459,14 @@ def pmonitor(popens, timeoutms=500, readline=True,
if fds: if fds:
for fd, event in fds: for fd, event in fds:
host = fdToHost[ fd ] host = fdToHost[ fd ]
decoder = fdToDecoder[ fd ]
popen = popens[ host ] popen = popens[ host ]
if event & POLLIN or event & POLLHUP: if event & POLLIN or event & POLLHUP:
while True: while True:
try: try:
f = popen.stdout f = popen.stdout
line = decode( f.readline() if readline line = decoder.decode( f.readline() if readline
else f.read( readmax ) ) else f.read( readmax ) )
except IOError: except IOError:
line = '' line = ''
if line == '': if line == '':
@@ -450,19 +481,19 @@ def pmonitor(popens, timeoutms=500, readline=True,
# Other stuff we use # Other stuff we use
def sysctlTestAndSet( name, limit ): def sysctlTestAndSet( name, limit ):
"Helper function to set sysctl limits" "Helper function to set sysctl limits"
#convert non-directory names into directory names # convert non-directory names into directory names
if '/' not in name: if '/' not in name:
name = '/proc/sys/' + name.replace( '.', '/' ) name = '/proc/sys/' + name.replace( '.', '/' )
#read limit # read limit
with open( name, 'r' ) as readFile: with open( name, 'r' ) as readFile:
oldLimit = readFile.readline() oldLimit = readFile.readline()
if isinstance( limit, int ): if isinstance( limit, int ):
#compare integer limits before overriding # compare integer limits before overriding
if int( oldLimit ) < limit: if int( oldLimit ) < limit:
with open( name, 'w' ) as writeFile: with open( name, 'w' ) as writeFile:
writeFile.write( "%d" % limit ) writeFile.write( "%d" % limit )
else: else:
#overwrite non-integer limits # overwrite non-integer limits
with open( name, 'w' ) as writeFile: with open( name, 'w' ) as writeFile:
writeFile.write( limit ) writeFile.write( limit )
@@ -479,21 +510,21 @@ def fixLimits():
try: try:
rlimitTestAndSet( RLIMIT_NPROC, 8192 ) rlimitTestAndSet( RLIMIT_NPROC, 8192 )
rlimitTestAndSet( RLIMIT_NOFILE, 16384 ) rlimitTestAndSet( RLIMIT_NOFILE, 16384 )
#Increase open file limit # Increase open file limit
sysctlTestAndSet( 'fs.file-max', 10000 ) sysctlTestAndSet( 'fs.file-max', 10000 )
#Increase network buffer space # Increase network buffer space
sysctlTestAndSet( 'net.core.wmem_max', 16777216 ) sysctlTestAndSet( 'net.core.wmem_max', 16777216 )
sysctlTestAndSet( 'net.core.rmem_max', 16777216 ) sysctlTestAndSet( 'net.core.rmem_max', 16777216 )
sysctlTestAndSet( 'net.ipv4.tcp_rmem', '10240 87380 16777216' ) sysctlTestAndSet( 'net.ipv4.tcp_rmem', '10240 87380 16777216' )
sysctlTestAndSet( 'net.ipv4.tcp_wmem', '10240 87380 16777216' ) sysctlTestAndSet( 'net.ipv4.tcp_wmem', '10240 87380 16777216' )
sysctlTestAndSet( 'net.core.netdev_max_backlog', 5000 ) sysctlTestAndSet( 'net.core.netdev_max_backlog', 5000 )
#Increase arp cache size # Increase arp cache size
sysctlTestAndSet( 'net.ipv4.neigh.default.gc_thresh1', 4096 ) sysctlTestAndSet( 'net.ipv4.neigh.default.gc_thresh1', 4096 )
sysctlTestAndSet( 'net.ipv4.neigh.default.gc_thresh2', 8192 ) sysctlTestAndSet( 'net.ipv4.neigh.default.gc_thresh2', 8192 )
sysctlTestAndSet( 'net.ipv4.neigh.default.gc_thresh3', 16384 ) sysctlTestAndSet( 'net.ipv4.neigh.default.gc_thresh3', 16384 )
#Increase routing table size # Increase routing table size
sysctlTestAndSet( 'net.ipv4.route.max_size', 32768 ) sysctlTestAndSet( 'net.ipv4.route.max_size', 32768 )
#Increase number of PTYs for nodes # Increase number of PTYs for nodes
sysctlTestAndSet( 'kernel.pty.max', 20000 ) sysctlTestAndSet( 'kernel.pty.max', 20000 )
# pylint: disable=broad-except # pylint: disable=broad-except
except Exception: except Exception: