First crack at restoring mininet python style, assisted by handy
'unpep8' script, which does most of the work. - topo.py is still in pep8 - not all examples work, but this is due to other issues
This commit is contained in:
@@ -8,10 +8,12 @@ TEST = mininet/test/*.py
|
||||
BIN = bin/mn bin/mnclean
|
||||
PYSRC = $(MININET) $(TEST) $(BIN)
|
||||
|
||||
P8IGN = E251,E201,E302
|
||||
|
||||
codecheck: $(PYSRC)
|
||||
pyflakes $(PYSRC)
|
||||
pylint --rcfile=.pylint $(PYSRC)
|
||||
pep8 --ignore=E251 $(PYSRC)
|
||||
pep8 --ignore=$(P8IGN) $(PYSRC)
|
||||
|
||||
test: $(MININET) $(TEST)
|
||||
mininet/test/test_nets.py
|
||||
|
||||
@@ -130,7 +130,7 @@ class MininetRunner(object):
|
||||
'''Setup and validate environment.'''
|
||||
|
||||
# set logging verbosity
|
||||
lg.set_loglevel(self.options.verbosity)
|
||||
lg.setLogLevel(self.options.verbosity)
|
||||
|
||||
# validate environment setup
|
||||
init()
|
||||
|
||||
+1
-1
@@ -1 +1 @@
|
||||
'''Docstring to silence pylint; ignores --ignore option for __init__.py'''
|
||||
"Docstring to silence pylint; ignores --ignore option for __init__.py"
|
||||
|
||||
+62
-69
@@ -1,131 +1,124 @@
|
||||
'''Logging functions for Mininet.'''
|
||||
"Logging functions for Mininet."
|
||||
|
||||
import logging
|
||||
from logging import Logger
|
||||
import types
|
||||
|
||||
LEVELS = {'debug': logging.DEBUG,
|
||||
LEVELS = { 'debug': logging.DEBUG,
|
||||
'info': logging.INFO,
|
||||
'warning': logging.WARNING,
|
||||
'error': logging.ERROR,
|
||||
'critical': logging.CRITICAL}
|
||||
'critical': logging.CRITICAL }
|
||||
|
||||
# change this to logging.INFO to get printouts when running unit tests
|
||||
LOG_LEVEL_DEFAULT = logging.WARNING
|
||||
LOGLEVELDEFAULT = logging.WARNING
|
||||
|
||||
#default: '%(asctime)s - %(name)s - %(levelname)s - %(message)s'
|
||||
LOG_MSG_FORMAT = '%(message)s'
|
||||
LOGMSGFORMAT = '%(message)s'
|
||||
|
||||
|
||||
# Modified from python2.5/__init__.py
|
||||
class StreamHandlerNoNewline(logging.StreamHandler):
|
||||
'''StreamHandler that doesn't print newlines by default.
|
||||
class StreamHandlerNoNewline( logging.StreamHandler ):
|
||||
"""StreamHandler that doesn't print newlines by default.
|
||||
Since StreamHandler automatically adds newlines, define a mod to more
|
||||
easily support interactive mode when we want it, or errors-only logging
|
||||
for running unit tests."""
|
||||
|
||||
Since StreamHandler automatically adds newlines, define a mod to more
|
||||
easily support interactive mode when we want it, or errors-only logging for
|
||||
running unit tests.
|
||||
'''
|
||||
|
||||
def emit(self, record):
|
||||
'''
|
||||
Emit a record.
|
||||
|
||||
If a formatter is specified, it is used to format the record.
|
||||
The record is then written to the stream with a trailing newline
|
||||
[N.B. this may be removed depending on feedback]. If exception
|
||||
information is present, it is formatted using
|
||||
traceback.print_exception and appended to the stream.
|
||||
'''
|
||||
def emit( self, record ):
|
||||
"""Emit a record.
|
||||
If a formatter is specified, it is used to format the record.
|
||||
The record is then written to the stream with a trailing newline
|
||||
[ N.B. this may be removed depending on feedback ]. If exception
|
||||
information is present, it is formatted using
|
||||
traceback.printException and appended to the stream."""
|
||||
try:
|
||||
msg = self.format(record)
|
||||
msg = self.format( record )
|
||||
fs = '%s' # was '%s\n'
|
||||
if not hasattr(types, 'UnicodeType'): #if no unicode support...
|
||||
self.stream.write(fs % msg)
|
||||
if not hasattr( types, 'UnicodeType' ): #if no unicode support...
|
||||
self.stream.write( fs % msg )
|
||||
else:
|
||||
try:
|
||||
self.stream.write(fs % msg)
|
||||
self.stream.write( fs % msg )
|
||||
except UnicodeError:
|
||||
self.stream.write(fs % msg.encode('UTF-8'))
|
||||
self.stream.write( fs % msg.encode( 'UTF-8' ) )
|
||||
self.flush()
|
||||
except (KeyboardInterrupt, SystemExit):
|
||||
except ( KeyboardInterrupt, SystemExit ):
|
||||
raise
|
||||
except:
|
||||
self.handleError(record)
|
||||
self.handleError( record )
|
||||
|
||||
|
||||
class Singleton(type):
|
||||
'''Singleton pattern from Wikipedia
|
||||
class Singleton( type ):
|
||||
"""Singleton pattern from Wikipedia
|
||||
See http://en.wikipedia.org/wiki/SingletonPattern#Python
|
||||
|
||||
See http://en.wikipedia.org/wiki/Singleton_pattern#Python
|
||||
Intended to be used as a __metaclass_ param, as shown for the class
|
||||
below.
|
||||
|
||||
Intended to be used as a __metaclass_ param, as shown for the class below.
|
||||
Changed cls first args to mcs to satisfy pylint."""
|
||||
|
||||
Changed cls first args to mcs to satsify pylint.
|
||||
'''
|
||||
|
||||
def __init__(mcs, name, bases, dict_):
|
||||
super(Singleton, mcs).__init__(name, bases, dict_)
|
||||
def __init__( mcs, name, bases, dict_ ):
|
||||
super( Singleton, mcs ).__init__( name, bases, dict_ )
|
||||
mcs.instance = None
|
||||
|
||||
def __call__(mcs, *args, **kw):
|
||||
def __call__( mcs, *args, **kw ):
|
||||
if mcs.instance is None:
|
||||
mcs.instance = super(Singleton, mcs).__call__(*args, **kw)
|
||||
mcs.instance = super( Singleton, mcs ).__call__( *args, **kw )
|
||||
return mcs.instance
|
||||
|
||||
|
||||
class MininetLogger(Logger, object):
|
||||
'''Mininet-specific logger
|
||||
|
||||
Enable each mininet .py file to with one import:
|
||||
class MininetLogger( Logger, object ):
|
||||
"""Mininet-specific logger
|
||||
Enable each mininet .py file to with one import:
|
||||
|
||||
from mininet.log import lg
|
||||
|
||||
...get a default logger that doesn't require one newline per logging call.
|
||||
...get a default logger that doesn't require one newline per logging
|
||||
call.
|
||||
|
||||
Inherit from object to ensure that we have at least one new-style base
|
||||
class, and can then use the __metaclass__ directive, to prevent this error:
|
||||
Inherit from object to ensure that we have at least one new-style base
|
||||
class, and can then use the __metaclass__ directive, to prevent this
|
||||
error:
|
||||
|
||||
TypeError: Error when calling the metaclass bases
|
||||
TypeError: Error when calling the metaclass bases
|
||||
a new-style class can't have only classic bases
|
||||
|
||||
If Python2.5/logging/__init__.py defined Filterer as a new-style class,
|
||||
via Filterer(object): rather than Filterer, we wouldn't need this.
|
||||
If Python2.5/logging/__init__.py defined Filterer as a new-style class,
|
||||
via Filterer( object ): rather than Filterer, we wouldn't need this.
|
||||
|
||||
Use singleton pattern to ensure only one logger is ever created."""
|
||||
|
||||
Use singleton pattern to ensure only one logger is ever created.
|
||||
'''
|
||||
__metaclass__ = Singleton
|
||||
|
||||
def __init__(self):
|
||||
def __init__( self ):
|
||||
|
||||
Logger.__init__(self, "mininet")
|
||||
Logger.__init__( self, "mininet" )
|
||||
|
||||
# create console handler
|
||||
ch = StreamHandlerNoNewline()
|
||||
# create formatter
|
||||
formatter = logging.Formatter(LOG_MSG_FORMAT)
|
||||
formatter = logging.Formatter( LOGMSGFORMAT )
|
||||
# add formatter to ch
|
||||
ch.setFormatter(formatter)
|
||||
ch.setFormatter( formatter )
|
||||
# add ch to lg
|
||||
self.addHandler(ch)
|
||||
self.addHandler( ch )
|
||||
|
||||
self.set_loglevel()
|
||||
self.setLogLevel()
|
||||
|
||||
def set_loglevel(self, levelname = None):
|
||||
'''Setup loglevel.
|
||||
def setLogLevel( self, levelname=None ):
|
||||
"""Setup loglevel.
|
||||
Convenience function to support lowercase names.
|
||||
|
||||
Convenience function to support lowercase names.
|
||||
|
||||
@param level_name level name from LEVELS
|
||||
'''
|
||||
level = LOG_LEVEL_DEFAULT
|
||||
levelName: level name from LEVELS"""
|
||||
level = LOGLEVELDEFAULT
|
||||
if levelname != None:
|
||||
if levelname not in LEVELS:
|
||||
raise Exception('unknown loglevel seen in set_loglevel')
|
||||
raise Exception( 'unknown loglevel seen in set_loglevel' )
|
||||
else:
|
||||
level = LEVELS.get(levelname, level)
|
||||
level = LEVELS.get( levelname, level )
|
||||
|
||||
self.setLevel(level)
|
||||
self.handlers[0].setLevel(level)
|
||||
self.setLevel( level )
|
||||
self.handlers[ 0 ].setLevel( level )
|
||||
|
||||
|
||||
lg = MininetLogger()
|
||||
|
||||
+385
-412
File diff suppressed because it is too large
Load Diff
+276
-322
@@ -1,5 +1,5 @@
|
||||
#!/usr/bin/env python
|
||||
'''Node objects for Mininet.'''
|
||||
"Node objects for Mininet."
|
||||
|
||||
from subprocess import Popen, PIPE, STDOUT
|
||||
import os
|
||||
@@ -13,31 +13,31 @@ from mininet.log import lg
|
||||
from mininet.util import quietRun, macColonHex, ipStr
|
||||
|
||||
|
||||
class Node(object):
|
||||
'''A virtual network node is simply a shell in a network namespace.
|
||||
We communicate with it using pipes.'''
|
||||
class Node( object ):
|
||||
"""A virtual network node is simply a shell in a network namespace.
|
||||
We communicate with it using pipes."""
|
||||
inToNode = {}
|
||||
outToNode = {}
|
||||
|
||||
def __init__(self, name, inNamespace = True):
|
||||
def __init__( self, name, inNamespace=True ):
|
||||
self.name = name
|
||||
closeFds = False # speed vs. memory use
|
||||
# xpg_echo is needed so we can echo our sentinel in sendCmd
|
||||
cmd = ['/bin/bash', '-O', 'xpg_echo']
|
||||
# xpgEcho is needed so we can echo our sentinel in sendCmd
|
||||
cmd = [ '/bin/bash', '-O', 'xpg_echo' ]
|
||||
self.inNamespace = inNamespace
|
||||
if self.inNamespace:
|
||||
cmd = ['netns'] + cmd
|
||||
self.shell = Popen(cmd, stdin = PIPE, stdout = PIPE, stderr = STDOUT,
|
||||
close_fds = closeFds)
|
||||
cmd = [ 'netns' ] + cmd
|
||||
self.shell = Popen( cmd, stdin=PIPE, stdout=PIPE, stderr=STDOUT,
|
||||
closeFds=closeFds )
|
||||
self.stdin = self.shell.stdin
|
||||
self.stdout = self.shell.stdout
|
||||
self.pollOut = select.poll()
|
||||
self.pollOut.register(self.stdout)
|
||||
self.pollOut.register( self.stdout )
|
||||
# Maintain mapping between file descriptors and nodes
|
||||
# This could be useful for monitoring multiple nodes
|
||||
# using select.poll()
|
||||
self.outToNode[self.stdout.fileno()] = self
|
||||
self.inToNode[self.stdin.fileno()] = self
|
||||
self.outToNode[ self.stdout.fileno() ] = self
|
||||
self.inToNode[ self.stdin.fileno() ] = self
|
||||
self.pid = self.shell.pid
|
||||
self.intfCount = 0
|
||||
self.intfs = [] # list of interface names, as strings
|
||||
@@ -48,461 +48,415 @@ class Node(object):
|
||||
self.ports = {} # dict of ints to interface strings
|
||||
# replace with Port object, eventually
|
||||
|
||||
def fdToNode(self, f):
|
||||
'''Insert docstring.
|
||||
def fdToNode( self, f ):
|
||||
"""Insert docstring.
|
||||
f: unknown
|
||||
returns: bool unknown"""
|
||||
node = self.outToNode.get( f )
|
||||
return node or self.inToNode.get( f )
|
||||
|
||||
@param f unknown
|
||||
@return bool unknown
|
||||
'''
|
||||
node = self.outToNode.get(f)
|
||||
return node or self.inToNode.get(f)
|
||||
|
||||
def cleanup(self):
|
||||
'''Help python collect its garbage.'''
|
||||
def cleanup( self ):
|
||||
"Help python collect its garbage."
|
||||
self.shell = None
|
||||
|
||||
# Subshell I/O, commands and control
|
||||
def read(self, fileno_max):
|
||||
'''Insert docstring.
|
||||
def read( self, filenoMax ):
|
||||
"""Insert docstring.
|
||||
filenoMax: unknown"""
|
||||
return os.read( self.stdout.fileno(), filenoMax )
|
||||
|
||||
@param fileno_max unknown
|
||||
'''
|
||||
return os.read(self.stdout.fileno(), fileno_max)
|
||||
def write( self, data ):
|
||||
"""Write data to node.
|
||||
data: string"""
|
||||
os.write( self.stdin.fileno(), data )
|
||||
|
||||
def write(self, data):
|
||||
'''Write data to node.
|
||||
|
||||
@param data string
|
||||
'''
|
||||
os.write(self.stdin.fileno(), data)
|
||||
|
||||
def terminate(self):
|
||||
'''Send kill signal to Node and cleanup after it.'''
|
||||
os.kill(self.pid, signal.SIGKILL)
|
||||
def terminate( self ):
|
||||
"Send kill signal to Node and cleanup after it."
|
||||
os.kill( self.pid, signal.SIGKILL )
|
||||
self.cleanup()
|
||||
|
||||
def stop(self):
|
||||
'''Stop node.'''
|
||||
def stop( self ):
|
||||
"Stop node."
|
||||
self.terminate()
|
||||
|
||||
def waitReadable(self):
|
||||
'''Poll on node.'''
|
||||
def waitReadable( self ):
|
||||
"Poll on node."
|
||||
self.pollOut.poll()
|
||||
|
||||
def sendCmd(self, cmd):
|
||||
'''Send a command, followed by a command to echo a sentinel,
|
||||
and return without waiting for the command to complete.'''
|
||||
def sendCmd( self, cmd ):
|
||||
"""Send a command, followed by a command to echo a sentinel,
|
||||
and return without waiting for the command to complete."""
|
||||
assert not self.waiting
|
||||
if cmd[-1] == '&':
|
||||
if cmd[ -1 ] == '&':
|
||||
separator = '&'
|
||||
cmd = cmd[:-1]
|
||||
cmd = cmd[ :-1 ]
|
||||
else:
|
||||
separator = ';'
|
||||
if isinstance(cmd, list):
|
||||
cmd = ' '.join(cmd)
|
||||
self.write(cmd + separator + ' echo -n "\\0177" \n')
|
||||
if isinstance( cmd, list ):
|
||||
cmd = ' '.join( cmd )
|
||||
self.write( cmd + separator + ' echo -n "\\0177" \n' )
|
||||
self.waiting = True
|
||||
|
||||
def monitor(self):
|
||||
'''Monitor the output of a command, returning (done, data).'''
|
||||
def monitor( self ):
|
||||
"Monitor the output of a command, returning (done, data)."
|
||||
assert self.waiting
|
||||
self.waitReadable()
|
||||
data = self.read(1024)
|
||||
if len(data) > 0 and data[-1] == chr(0177):
|
||||
data = self.read( 1024 )
|
||||
if len( data ) > 0 and data[ -1 ] == chr( 0177 ):
|
||||
self.waiting = False
|
||||
return True, data[:-1]
|
||||
return True, data[ :-1 ]
|
||||
else:
|
||||
return False, data
|
||||
|
||||
def sendInt(self):
|
||||
'''Send ^C, hopefully interrupting a running subprocess.'''
|
||||
self.write(chr(3))
|
||||
def sendInt( self ):
|
||||
"Send ^C, hopefully interrupting a running subprocess."
|
||||
self.write( chr( 3 ) )
|
||||
|
||||
def waitOutput(self):
|
||||
'''Wait for a command to complete.
|
||||
|
||||
Completion is signaled by a sentinel character, ASCII(127) appearing in
|
||||
the output stream. Wait for the sentinel and return the output,
|
||||
including trailing newline.
|
||||
'''
|
||||
def waitOutput( self ):
|
||||
"""Wait for a command to complete.
|
||||
Completion is signaled by a sentinel character, ASCII( 127 )
|
||||
appearing in the output stream. Wait for the sentinel and return
|
||||
the output, including trailing newline."""
|
||||
assert self.waiting
|
||||
output = ''
|
||||
while True:
|
||||
self.waitReadable()
|
||||
data = self.read(1024)
|
||||
if len(data) > 0 and data[-1] == chr(0177):
|
||||
output += data[:-1]
|
||||
data = self.read( 1024 )
|
||||
if len( data ) > 0 and data[ -1 ] == chr( 0177 ):
|
||||
output += data[ :-1 ]
|
||||
break
|
||||
else:
|
||||
output += data
|
||||
self.waiting = False
|
||||
return output
|
||||
|
||||
def cmd(self, cmd):
|
||||
'''Send a command, wait for output, and return it.
|
||||
|
||||
@param cmd string
|
||||
'''
|
||||
self.sendCmd(cmd)
|
||||
def cmd( self, cmd ):
|
||||
"""Send a command, wait for output, and return it.
|
||||
cmd: string"""
|
||||
self.sendCmd( cmd )
|
||||
return self.waitOutput()
|
||||
|
||||
def cmdPrint(self, cmd):
|
||||
'''Call cmd and printing its output
|
||||
|
||||
@param cmd string
|
||||
'''
|
||||
#lg.info('*** %s : %s', self.name, cmd)
|
||||
result = self.cmd(cmd)
|
||||
#lg.info('%s\n', result)
|
||||
def cmdPrint( self, cmd ):
|
||||
"""Call cmd and printing its output
|
||||
cmd: string"""
|
||||
#lg.info( '*** %s : %s', self.name, cmd )
|
||||
result = self.cmd( cmd )
|
||||
#lg.info( '%s\n', result )
|
||||
return result
|
||||
|
||||
# Interface management, configuration, and routing
|
||||
def intfName(self, n):
|
||||
'''Construct a canonical interface name node-intf for interface N.'''
|
||||
return self.name + '-eth' + repr(n)
|
||||
def intfName( self, n ):
|
||||
"Construct a canonical interface name node-intf for interface N."
|
||||
return self.name + '-eth' + repr( n )
|
||||
|
||||
def newIntf(self):
|
||||
'''Reserve and return a new interface name.'''
|
||||
intfName = self.intfName(self.intfCount)
|
||||
def newIntf( self ):
|
||||
"Reserve and return a new interface name."
|
||||
intfName = self.intfName( self.intfCount )
|
||||
self.intfCount += 1
|
||||
self.intfs += [intfName]
|
||||
self.intfs += [ intfName ]
|
||||
return intfName
|
||||
|
||||
def setMAC(self, intf, mac):
|
||||
'''Set the MAC address for an interface.
|
||||
|
||||
@param mac MAC address as unsigned int
|
||||
'''
|
||||
mac_str = macColonHex(mac)
|
||||
result = self.cmd(['ifconfig', intf, 'down'])
|
||||
result += self.cmd(['ifconfig', intf, 'hw', 'ether', mac_str])
|
||||
result += self.cmd(['ifconfig', intf, 'up'])
|
||||
def setMAC( self, intf, mac ):
|
||||
"""Set the MAC address for an interface.
|
||||
mac: MAC address as unsigned int"""
|
||||
macStr = macColonHex( mac )
|
||||
result = self.cmd( [ 'ifconfig', intf, 'down' ] )
|
||||
result += self.cmd( [ 'ifconfig', intf, 'hw', 'ether', macStr ] )
|
||||
result += self.cmd( [ 'ifconfig', intf, 'up' ] )
|
||||
return result
|
||||
|
||||
def setARP(self, ip, mac):
|
||||
'''Add an ARP entry.
|
||||
|
||||
@param ip IP address as unsigned int
|
||||
@param mac MAC address as unsigned int
|
||||
'''
|
||||
ip_str = ipStr(ip)
|
||||
mac_str = macColonHex(mac)
|
||||
result = self.cmd(['arp', '-s', ip_str, mac_str])
|
||||
def setARP( self, ip, mac ):
|
||||
"""Add an ARP entry.
|
||||
ip: IP address as unsigned int
|
||||
mac: MAC address as unsigned int"""
|
||||
ip = ipStr( ip )
|
||||
mac = macColonHex( mac )
|
||||
result = self.cmd( [ 'arp', '-s', ip, mac ] )
|
||||
return result
|
||||
|
||||
def setIP(self, intf, ip, bits):
|
||||
'''Set the IP address for an interface.
|
||||
|
||||
@param intf string, interface name
|
||||
@param ip IP address as a string
|
||||
@param bits
|
||||
'''
|
||||
result = self.cmd(['ifconfig', intf, ip + bits, 'up'])
|
||||
self.ips[intf] = ip
|
||||
def setIP( self, intf, ip, bits ):
|
||||
"""Set the IP address for an interface.
|
||||
intf: string, interface name
|
||||
ip: IP address as a string
|
||||
bits:"""
|
||||
result = self.cmd( [ 'ifconfig', intf, ip + bits, 'up' ] )
|
||||
self.ips[ intf ] = ip
|
||||
return result
|
||||
|
||||
def setHostRoute(self, ip, intf):
|
||||
'''Add route to host.
|
||||
def setHostRoute( self, ip, intf ):
|
||||
"""Add route to host.
|
||||
ip: IP address as dotted decimal
|
||||
intf: string, interface name"""
|
||||
return self.cmd( 'route add -host ' + ip + ' dev ' + intf )
|
||||
|
||||
@param ip IP address as dotted decimal
|
||||
@param intf string, interface name
|
||||
'''
|
||||
return self.cmd('route add -host ' + ip + ' dev ' + intf)
|
||||
def setDefaultRoute( self, intf ):
|
||||
"""Set the default route to go through intf.
|
||||
intf: string, interface name"""
|
||||
self.cmd( 'ip route flush' )
|
||||
return self.cmd( 'route add default ' + intf )
|
||||
|
||||
def setDefaultRoute(self, intf):
|
||||
'''Set the default route to go through intf.
|
||||
def IP( self ):
|
||||
"Return IP address of first interface"
|
||||
if len( self.intfs ) > 0:
|
||||
return self.ips.get( self.intfs[ 0 ], None )
|
||||
|
||||
@param intf string, interface name
|
||||
'''
|
||||
self.cmd('ip route flush')
|
||||
return self.cmd('route add default ' + intf)
|
||||
|
||||
def IP(self):
|
||||
'''Return IP address of first interface'''
|
||||
if len(self.intfs) > 0:
|
||||
return self.ips.get(self.intfs[0], None)
|
||||
|
||||
def intfIsUp(self):
|
||||
'''Check if one of our interfaces is up.'''
|
||||
return 'UP' in self.cmd('ifconfig ' + self.intfs[0])
|
||||
def intfIsUp( self ):
|
||||
"Check if one of our interfaces is up."
|
||||
return 'UP' in self.cmd( 'ifconfig ' + self.intfs[ 0 ] )
|
||||
|
||||
# Other methods
|
||||
def __str__(self):
|
||||
def __str__( self ):
|
||||
result = self.name + ':'
|
||||
if self.IP():
|
||||
result += ' IP=' + self.IP()
|
||||
result += ' intfs=' + ','.join(self.intfs)
|
||||
result += ' waiting=' + repr(self.waiting)
|
||||
result += ' intfs=' + ','.join( self.intfs )
|
||||
result += ' waiting=' + repr( self.waiting )
|
||||
return result
|
||||
|
||||
|
||||
class Host(Node):
|
||||
'''A host is simply a Node.'''
|
||||
class Host( Node ):
|
||||
"A host is simply a Node."
|
||||
pass
|
||||
|
||||
|
||||
class Switch(Node):
|
||||
'''A Switch is a Node that is running (or has execed)
|
||||
an OpenFlow switch.'''
|
||||
class Switch( Node ):
|
||||
"""A Switch is a Node that is running ( or has execed )
|
||||
an OpenFlow switch."""
|
||||
|
||||
def sendCmd(self, cmd):
|
||||
'''Send command to Node.
|
||||
|
||||
@param cmd string
|
||||
'''
|
||||
def sendCmd( self, cmd ):
|
||||
"""Send command to Node.
|
||||
cmd: string"""
|
||||
if not self.execed:
|
||||
return Node.sendCmd(self, cmd)
|
||||
return Node.sendCmd( self, cmd )
|
||||
else:
|
||||
lg.error('*** Error: %s has execed and cannot accept commands' %
|
||||
self.name)
|
||||
lg.error( '*** Error: %s has execed and cannot accept commands' %
|
||||
self.name )
|
||||
|
||||
def monitor(self):
|
||||
'''Monitor node.'''
|
||||
def monitor( self ):
|
||||
"Monitor node."
|
||||
if not self.execed:
|
||||
return Node.monitor(self)
|
||||
return Node.monitor( self )
|
||||
else:
|
||||
return True, ''
|
||||
|
||||
|
||||
class UserSwitch(Switch):
|
||||
'''User-space switch.
|
||||
class UserSwitch( Switch ):
|
||||
"""User-space switch.
|
||||
Currently only works in the root namespace."""
|
||||
|
||||
Currently only works in the root namespace.
|
||||
'''
|
||||
def __init__( self, name ):
|
||||
"""Init.
|
||||
name: name for the switch"""
|
||||
Switch.__init__( self, name, inNamespace=False )
|
||||
|
||||
def __init__(self, name):
|
||||
'''Init.
|
||||
|
||||
@param name
|
||||
'''
|
||||
Switch.__init__(self, name, inNamespace = False)
|
||||
|
||||
def start(self, controllers):
|
||||
'''Start OpenFlow reference user datapath.
|
||||
|
||||
Log to /tmp/sN-{ofd,ofp}.log.
|
||||
|
||||
@param controllers dict of controller names to objects
|
||||
'''
|
||||
def start( self, controllers ):
|
||||
"""Start OpenFlow reference user datapath.
|
||||
Log to /tmp/sN-{ ofd,ofp }.log.
|
||||
controllers: dict of controller names to objects"""
|
||||
if 'c0' not in controllers:
|
||||
raise Exception('User datapath start() requires controller c0')
|
||||
controller = controllers['c0']
|
||||
raise Exception( 'User datapath start() requires controller c0' )
|
||||
controller = controllers[ 'c0' ]
|
||||
ofdlog = '/tmp/' + self.name + '-ofd.log'
|
||||
ofplog = '/tmp/' + self.name + '-ofp.log'
|
||||
self.cmd('ifconfig lo up')
|
||||
self.cmd( 'ifconfig lo up' )
|
||||
intfs = self.intfs
|
||||
self.cmdPrint('ofdatapath -i ' + ','.join(intfs) + ' punix:/tmp/' +
|
||||
self.name + ' 1> ' + ofdlog + ' 2> ' + ofdlog + ' &')
|
||||
self.cmdPrint('ofprotocol unix:/tmp/' + self.name + ' tcp:' +
|
||||
self.cmdPrint( 'ofdatapath -i ' + ','.join( intfs ) + ' punix:/tmp/' +
|
||||
self.name + ' 1> ' + ofdlog + ' 2> ' + ofdlog + ' &' )
|
||||
self.cmdPrint( 'ofprotocol unix:/tmp/' + self.name + ' tcp:' +
|
||||
controller.IP() + ' --fail=closed 1> ' + ofplog + ' 2>' +
|
||||
ofplog + ' &')
|
||||
ofplog + ' &' )
|
||||
|
||||
def stop(self):
|
||||
'''Stop OpenFlow reference user datapath.'''
|
||||
self.cmd('kill %ofdatapath')
|
||||
self.cmd('kill %ofprotocol')
|
||||
def stop( self ):
|
||||
"Stop OpenFlow reference user datapath."
|
||||
self.cmd( 'kill %ofdatapath' )
|
||||
self.cmd( 'kill %ofprotocol' )
|
||||
|
||||
|
||||
class KernelSwitch(Switch):
|
||||
'''Kernel-space switch.
|
||||
class KernelSwitch( Switch ):
|
||||
"""Kernel-space switch.
|
||||
Currently only works in the root namespace."""
|
||||
|
||||
Currently only works in the root namespace.
|
||||
'''
|
||||
|
||||
def __init__(self, name, dp = None, dpid = None):
|
||||
'''Init.
|
||||
|
||||
@param name
|
||||
@param dp netlink id (0, 1, 2, ...)
|
||||
@param dpid datapath ID as unsigned int; random value if None
|
||||
'''
|
||||
Switch.__init__(self, name, inNamespace = False)
|
||||
def __init__( self, name, dp=None, dpid=None ):
|
||||
"""Init.
|
||||
name:
|
||||
dp: netlink id ( 0, 1, 2, ... )
|
||||
dpid: datapath ID as unsigned int; random value if None"""
|
||||
Switch.__init__( self, name, inNamespace=False )
|
||||
self.dp = dp
|
||||
self.dpid = dpid
|
||||
|
||||
def start(self, controllers):
|
||||
'''Start up reference kernel datapath.'''
|
||||
def start( self, controllers ):
|
||||
"Start up reference kernel datapath."
|
||||
ofplog = '/tmp/' + self.name + '-ofp.log'
|
||||
quietRun('ifconfig lo up')
|
||||
quietRun( 'ifconfig lo up' )
|
||||
# Delete local datapath if it exists;
|
||||
# then create a new one monitoring the given interfaces
|
||||
quietRun('dpctl deldp nl:%i' % self.dp)
|
||||
self.cmdPrint('dpctl adddp nl:%i' % self.dp)
|
||||
quietRun( 'dpctl deldp nl:%i' % self.dp )
|
||||
self.cmdPrint( 'dpctl adddp nl:%i' % self.dp )
|
||||
if self.dpid:
|
||||
intf = 'of%i' % self.dp
|
||||
mac_str = macColonHex(self.dpid)
|
||||
self.cmd(['ifconfig', intf, 'hw', 'ether', mac_str])
|
||||
macStr = macColonHex( self.dpid )
|
||||
self.cmd( [ 'ifconfig', intf, 'hw', 'ether', macStr ] )
|
||||
|
||||
if len(self.ports) != max(self.ports.keys()) + 1:
|
||||
raise Exception('only contiguous, zero-indexed port ranges'
|
||||
'supported: %s' % self.ports)
|
||||
intfs = [self.ports[port] for port in self.ports.keys()]
|
||||
self.cmdPrint('dpctl addif nl:' + str(self.dp) + ' ' + ' '.join(intfs))
|
||||
if len( self.ports ) != max( self.ports.keys() ) + 1:
|
||||
raise Exception( 'only contiguous, zero-indexed port ranges'
|
||||
'supported: %s' % self.ports )
|
||||
intfs = [ self.ports[ port ] for port in self.ports.keys() ]
|
||||
self.cmdPrint( 'dpctl addif nl:' + str( self.dp ) + ' ' +
|
||||
' '.join( intfs ) )
|
||||
# Run protocol daemon
|
||||
self.cmdPrint('ofprotocol nl:' + str(self.dp) + ' tcp:' +
|
||||
controllers['c0'].IP() + ':' +
|
||||
str(controllers['c0'].port) +
|
||||
' --fail=closed 1> ' + ofplog + ' 2>' + ofplog + ' &')
|
||||
self.cmdPrint( 'ofprotocol nl:' + str( self.dp ) + ' tcp:' +
|
||||
controllers[ 'c0' ].IP() + ':' +
|
||||
str( controllers[ 'c0' ].port ) +
|
||||
' --fail=closed 1> ' + ofplog + ' 2>' + ofplog + ' &' )
|
||||
self.execed = False
|
||||
|
||||
def stop(self):
|
||||
'''Terminate kernel datapath.'''
|
||||
quietRun('dpctl deldp nl:%i' % self.dp)
|
||||
def stop( self ):
|
||||
"Terminate kernel datapath."
|
||||
quietRun( 'dpctl deldp nl:%i' % self.dp )
|
||||
# In theory the interfaces should go away after we shut down.
|
||||
# However, this takes time, so we're better off to remove them
|
||||
# explicitly so that we won't get errors if we run before they
|
||||
# have been removed by the kernel. Unfortunately this is very slow.
|
||||
self.cmd('kill %ofprotocol')
|
||||
self.cmd( 'kill %ofprotocol' )
|
||||
for intf in self.intfs:
|
||||
quietRun('ip link del ' + intf)
|
||||
lg.info('.')
|
||||
quietRun( 'ip link del ' + intf )
|
||||
lg.info( '.' )
|
||||
|
||||
|
||||
class OVSKernelSwitch(Switch):
|
||||
'''Open VSwitch kernel-space switch.
|
||||
class OVSKernelSwitch( Switch ):
|
||||
"""Open VSwitch kernel-space switch.
|
||||
Currently only works in the root namespace."""
|
||||
|
||||
Currently only works in the root namespace.
|
||||
'''
|
||||
|
||||
def __init__(self, name, dp = None, dpid = None):
|
||||
'''Init.
|
||||
|
||||
@param name
|
||||
@param dp netlink id (0, 1, 2, ...)
|
||||
@param dpid datapath ID as unsigned int; random value if None
|
||||
'''
|
||||
Switch.__init__(self, name, inNamespace = False)
|
||||
def __init__( self, name, dp=None, dpid=None ):
|
||||
"""Init.
|
||||
name:
|
||||
dp: netlink id ( 0, 1, 2, ... )
|
||||
dpid: datapath ID as unsigned int; random value if None"""
|
||||
Switch.__init__( self, name, inNamespace=False )
|
||||
self.dp = dp
|
||||
self.dpid = dpid
|
||||
|
||||
def start(self, controllers):
|
||||
'''Start up kernel datapath.'''
|
||||
def start( self, controllers ):
|
||||
"Start up kernel datapath."
|
||||
ofplog = '/tmp/' + self.name + '-ofp.log'
|
||||
quietRun('ifconfig lo up')
|
||||
quietRun( 'ifconfig lo up' )
|
||||
# Delete local datapath if it exists;
|
||||
# then create a new one monitoring the given interfaces
|
||||
quietRun('ovs-dpctl del-dp dp%i' % self.dp)
|
||||
self.cmdPrint('ovs-dpctl add-dp dp%i' % self.dp)
|
||||
quietRun( 'ovs-dpctl del-dp dp%i' % self.dp )
|
||||
self.cmdPrint( 'ovs-dpctl add-dp dp%i' % self.dp )
|
||||
if self.dpid:
|
||||
intf = 'dp' % self.dp
|
||||
mac_str = macColonHex(self.dpid)
|
||||
self.cmd(['ifconfig', intf, 'hw', 'ether', mac_str])
|
||||
macStr = macColonHex( self.dpid )
|
||||
self.cmd( [ 'ifconfig', intf, 'hw', 'ether', macStr ] )
|
||||
|
||||
if len(self.ports) != max(self.ports.keys()) + 1:
|
||||
raise Exception('only contiguous, zero-indexed port ranges'
|
||||
'supported: %s' % self.ports)
|
||||
intfs = [self.ports[port] for port in self.ports.keys()]
|
||||
self.cmdPrint('ovs-dpctl add-if dp' + str(self.dp) + ' ' +
|
||||
' '.join(intfs))
|
||||
if len( self.ports ) != max( self.ports.keys() ) + 1:
|
||||
raise Exception( 'only contiguous, zero-indexed port ranges'
|
||||
'supported: %s' % self.ports )
|
||||
intfs = [ self.ports[ port ] for port in self.ports.keys() ]
|
||||
self.cmdPrint( 'ovs-dpctl add-if dp' + str( self.dp ) + ' ' +
|
||||
' '.join( intfs ) )
|
||||
# Run protocol daemon
|
||||
self.cmdPrint('ovs-openflowd dp' + str(self.dp) + ' tcp:' +
|
||||
controllers['c0'].IP() + ':' +
|
||||
' --fail=closed 1> ' + ofplog + ' 2>' + ofplog + ' &')
|
||||
self.cmdPrint( 'ovs-openflowd dp' + str( self.dp ) + ' tcp:' +
|
||||
controllers[ 'c0' ].IP() + ':' +
|
||||
' --fail=closed 1> ' + ofplog + ' 2>' + ofplog + ' &' )
|
||||
self.execed = False
|
||||
|
||||
def stop(self):
|
||||
'''Terminate kernel datapath.'''
|
||||
quietRun('ovs-dpctl del-dp dp%i' % self.dp)
|
||||
def stop( self ):
|
||||
"Terminate kernel datapath."
|
||||
quietRun( 'ovs-dpctl del-dp dp%i' % self.dp )
|
||||
# In theory the interfaces should go away after we shut down.
|
||||
# However, this takes time, so we're better off to remove them
|
||||
# explicitly so that we won't get errors if we run before they
|
||||
# have been removed by the kernel. Unfortunately this is very slow.
|
||||
self.cmd('kill %ovs-openflowd')
|
||||
self.cmd( 'kill %ovs-openflowd' )
|
||||
for intf in self.intfs:
|
||||
quietRun('ip link del ' + intf)
|
||||
lg.info('.')
|
||||
quietRun( 'ip link del ' + intf )
|
||||
lg.info( '.' )
|
||||
|
||||
|
||||
class Controller(Node):
|
||||
'''A Controller is a Node that is running (or has execed) an
|
||||
OpenFlow controller.'''
|
||||
class Controller( Node ):
|
||||
"""A Controller is a Node that is running ( or has execed ) an
|
||||
OpenFlow controller."""
|
||||
|
||||
def __init__(self, name, inNamespace = False, controller = 'controller',
|
||||
cargs = '-v ptcp:', cdir = None, ip_address="127.0.0.1",
|
||||
port = 6633):
|
||||
def __init__( self, name, inNamespace=False, controller='controller',
|
||||
cargs='-v ptcp:', cdir=None, ipAddress="127.0.0.1",
|
||||
port=6633 ):
|
||||
self.controller = controller
|
||||
self.cargs = cargs
|
||||
self.cdir = cdir
|
||||
self.ip_address = ip_address
|
||||
self.ipAddress = ipAddress
|
||||
self.port = port
|
||||
Node.__init__(self, name, inNamespace = inNamespace)
|
||||
Node.__init__( self, name, inNamespace=inNamespace )
|
||||
|
||||
def start(self):
|
||||
'''Start <controller> <args> on controller.
|
||||
|
||||
Log to /tmp/cN.log
|
||||
'''
|
||||
def start( self ):
|
||||
"""Start <controller> <args> on controller.
|
||||
Log to /tmp/cN.log"""
|
||||
cout = '/tmp/' + self.name + '.log'
|
||||
if self.cdir is not None:
|
||||
self.cmdPrint('cd ' + self.cdir)
|
||||
self.cmdPrint(self.controller + ' ' + self.cargs +
|
||||
' 1> ' + cout + ' 2> ' + cout + ' &')
|
||||
self.cmdPrint( 'cd ' + self.cdir )
|
||||
self.cmdPrint( self.controller + ' ' + self.cargs +
|
||||
' 1> ' + cout + ' 2> ' + cout + ' &' )
|
||||
self.execed = False
|
||||
|
||||
def stop(self):
|
||||
'''Stop controller.'''
|
||||
self.cmd('kill %' + self.controller)
|
||||
def stop( self ):
|
||||
"Stop controller."
|
||||
self.cmd( 'kill %' + self.controller )
|
||||
self.terminate()
|
||||
|
||||
def IP(self):
|
||||
'''Return IP address of the Controller'''
|
||||
return self.ip_address
|
||||
def IP( self ):
|
||||
"Return IP address of the Controller"
|
||||
return self.ipAddress
|
||||
|
||||
|
||||
class ControllerParams(object):
|
||||
'''Container for controller IP parameters.'''
|
||||
class ControllerParams( object ):
|
||||
"Container for controller IP parameters."
|
||||
|
||||
def __init__(self, ip, subnet_size):
|
||||
'''Init.
|
||||
|
||||
@param ip integer, controller IP
|
||||
@param subnet_size integer, ex 8 for slash-8, covering 17M
|
||||
'''
|
||||
def __init__( self, ip, subnetSize ):
|
||||
"""Init.
|
||||
ip: integer, controller IP
|
||||
subnetSize: integer, ex 8 for slash-8, covering 17M"""
|
||||
self.ip = ip
|
||||
self.subnet_size = subnet_size
|
||||
self.subnetSize = subnetSize
|
||||
|
||||
|
||||
class NOX(Controller):
|
||||
'''Controller to run a NOX application.'''
|
||||
class NOX( Controller ):
|
||||
"Controller to run a NOX application."
|
||||
|
||||
def __init__(self, name, inNamespace = False, nox_args = None, **kwargs):
|
||||
'''Init.
|
||||
|
||||
@param name name to give controller
|
||||
@param nox_args list of args, or single arg, to pass to NOX
|
||||
'''
|
||||
if type(nox_args) != list:
|
||||
nox_args = [nox_args]
|
||||
if not nox_args:
|
||||
nox_args = ['packetdump']
|
||||
nox_core_dir = os.environ['NOX_CORE_DIR']
|
||||
if not nox_core_dir:
|
||||
raise Exception('please set NOX_CORE_DIR env var\n')
|
||||
Controller.__init__(self, name,
|
||||
controller = nox_core_dir + '/nox_core',
|
||||
cargs = '--libdir=/usr/local/lib -v -i ptcp: ' + \
|
||||
' '.join(nox_args),
|
||||
cdir = nox_core_dir, **kwargs)
|
||||
def __init__( self, name, inNamespace=False, noxArgs=None, **kwargs ):
|
||||
"""Init.
|
||||
name: name to give controller
|
||||
noxArgs: list of args, or single arg, to pass to NOX"""
|
||||
if type( noxArgs ) != list:
|
||||
noxArgs = [ noxArgs ]
|
||||
if not noxArgs:
|
||||
noxArgs = [ 'packetdump' ]
|
||||
noxCoreDir = os.environ[ 'NOX_CORE_DIR' ]
|
||||
if not noxCoreDir:
|
||||
raise Exception( 'please set NOX_CORE_DIR env var\n' )
|
||||
Controller.__init__( self, name,
|
||||
controller=noxCoreDir + '/nox_core',
|
||||
cargs='--libdir=/usr/local/lib -v -i ptcp: ' + \
|
||||
' '.join( noxArgs ),
|
||||
cdir = noxCoreDir, **kwargs )
|
||||
|
||||
|
||||
class RemoteController(Controller):
|
||||
'''Controller running outside of Mininet's control.'''
|
||||
class RemoteController( Controller ):
|
||||
"Controller running outside of Mininet's control."
|
||||
|
||||
def __init__(self, name, inNamespace = False, ip_address = '127.0.0.1',
|
||||
port = 6633):
|
||||
'''Init.
|
||||
def __init__( self, name, inNamespace=False, ipAddress='127.0.0.1',
|
||||
port=6633 ):
|
||||
"""Init.
|
||||
name: name to give controller
|
||||
ipAddress: the IP address where the remote controller is
|
||||
listening
|
||||
port: the port where the remote controller is listening"""
|
||||
Controller.__init__( self, name, ipAddress=ipAddress, port=port )
|
||||
|
||||
@param name name to give controller
|
||||
@param ip_address the IP address where the remote controller is
|
||||
listening
|
||||
@param port the port where the remote controller is listening
|
||||
'''
|
||||
Controller.__init__(self, name, ip_address = ip_address, port = port)
|
||||
|
||||
def start(self):
|
||||
'''Overridden to do nothing.'''
|
||||
def start( self ):
|
||||
"Overridden to do nothing."
|
||||
return
|
||||
|
||||
def stop(self):
|
||||
'''Overridden to do nothing.'''
|
||||
def stop( self ):
|
||||
"Overridden to do nothing."
|
||||
return
|
||||
|
||||
+89
-154
@@ -1,5 +1,5 @@
|
||||
#!/usr/bin/env python
|
||||
'''Utility functions for Mininet.'''
|
||||
"Utility functions for Mininet."
|
||||
|
||||
from time import sleep
|
||||
from resource import setrlimit, RLIMIT_NPROC, RLIMIT_NOFILE
|
||||
@@ -8,41 +8,32 @@ from subprocess import call, check_call, Popen, PIPE, STDOUT
|
||||
|
||||
from mininet.log import lg
|
||||
|
||||
def run( cmd ):
|
||||
"""Simple interface to subprocess.call()
|
||||
cmd: list of command params"""
|
||||
return call( cmd.split( ' ' ) )
|
||||
|
||||
def run(cmd):
|
||||
'''Simple interface to subprocess.call()
|
||||
def checkRun( cmd ):
|
||||
"""Simple interface to subprocess.check_call()
|
||||
cmd: list of command params"""
|
||||
check_call( cmd.split( ' ' ) )
|
||||
|
||||
@param cmd list of command params
|
||||
'''
|
||||
return call(cmd.split(' '))
|
||||
|
||||
|
||||
def checkRun(cmd):
|
||||
'''Simple interface to subprocess.check_call()
|
||||
|
||||
@param cmd list of command params
|
||||
'''
|
||||
check_call(cmd.split(' '))
|
||||
|
||||
|
||||
def quietRun(cmd):
|
||||
'''Run a command, routing stderr to stdout, and return the output.
|
||||
|
||||
@param cmd list of command params
|
||||
'''
|
||||
if isinstance(cmd, str):
|
||||
cmd = cmd.split(' ')
|
||||
popen = Popen(cmd, stdout=PIPE, stderr=STDOUT)
|
||||
def quietRun( cmd ):
|
||||
"""Run a command, routing stderr to stdout, and return the output.
|
||||
cmd: list of command params"""
|
||||
if isinstance( cmd, str ):
|
||||
cmd = cmd.split( ' ' )
|
||||
popen = Popen( cmd, stdout=PIPE, stderr=STDOUT )
|
||||
# We can't use Popen.communicate() because it uses
|
||||
# select(), which can't handle
|
||||
# high file descriptor numbers! poll() can, however.
|
||||
output = ''
|
||||
readable = select.poll()
|
||||
readable.register(popen.stdout)
|
||||
readable.register( popen.stdout )
|
||||
while True:
|
||||
while readable.poll():
|
||||
data = popen.stdout.read(1024)
|
||||
if len(data) == 0:
|
||||
data = popen.stdout.read( 1024 )
|
||||
if len( data ) == 0:
|
||||
break
|
||||
output += data
|
||||
popen.poll()
|
||||
@@ -50,42 +41,6 @@ def quietRun(cmd):
|
||||
break
|
||||
return output
|
||||
|
||||
|
||||
def make_veth_pair(intf1, intf2):
|
||||
'''Create a veth pair connecting intf1 and intf2.
|
||||
|
||||
@param intf1 string, interface name
|
||||
@param intf2 string, interface name
|
||||
'''
|
||||
# Delete any old interfaces with the same names
|
||||
quietRun('ip link del ' + intf1)
|
||||
quietRun('ip link del ' + intf2)
|
||||
# Create new pair
|
||||
cmd = 'ip link add name ' + intf1 + ' type veth peer name ' + intf2
|
||||
#lg.info('running command: %s\n' % cmd)
|
||||
return checkRun(cmd)
|
||||
|
||||
|
||||
def move_intf(intf, node):
|
||||
'''Move interface to node.
|
||||
|
||||
@param intf string interface name
|
||||
@param node Node object
|
||||
|
||||
@return success boolean, did operation complete?
|
||||
'''
|
||||
cmd = 'ip link set ' + intf + ' netns ' + repr(node.pid)
|
||||
#lg.info('running command: %s\n' % cmd)
|
||||
quietRun(cmd)
|
||||
#lg.info(' output: %s\n' % output)
|
||||
links = node.cmd('ip link show')
|
||||
if not intf in links:
|
||||
lg.error('*** Error: move_intf: %s not successfully moved to %s:\n' %
|
||||
(intf, node.name))
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
# Interface management
|
||||
#
|
||||
# Interfaces are managed as strings which are simply the
|
||||
@@ -99,116 +54,96 @@ def move_intf(intf, node):
|
||||
# live in the root namespace and thus do not have to be
|
||||
# explicitly moved.
|
||||
|
||||
|
||||
def makeIntfPair(intf1, intf2):
|
||||
'''Make a veth pair.
|
||||
|
||||
@param intf1 string, interface
|
||||
@param intf2 string, interface
|
||||
@return success boolean
|
||||
'''
|
||||
def makeIntfPair( intf1, intf2 ):
|
||||
"""Make a veth pair connecting intf1 and intf2.
|
||||
intf1: string, interface
|
||||
intf2: string, interface
|
||||
returns: success boolean"""
|
||||
# Delete any old interfaces with the same names
|
||||
quietRun('ip link del ' + intf1)
|
||||
quietRun('ip link del ' + intf2)
|
||||
quietRun( 'ip link del ' + intf1 )
|
||||
quietRun( 'ip link del ' + intf2 )
|
||||
# Create new pair
|
||||
cmd = 'ip link add name ' + intf1 + ' type veth peer name ' + intf2
|
||||
return checkRun(cmd)
|
||||
return checkRun( cmd )
|
||||
|
||||
def retry( retries, delaySecs, fn, *args, **keywords ):
|
||||
"""Try something several times before giving up.
|
||||
n: number of times to retry
|
||||
delaySecs: wait this long between tries
|
||||
fn: function to call
|
||||
args: args to apply to function call"""
|
||||
tries = 0
|
||||
while not fn( *args, **keywords ) and tries < retries:
|
||||
sleep( delaySecs )
|
||||
tries += 1
|
||||
if tries >= retries:
|
||||
lg.error( "*** gave up after %i retries\n" % tries )
|
||||
exit( 1 )
|
||||
|
||||
def moveIntf(intf, node, print_error = False):
|
||||
'''Move interface to node.
|
||||
|
||||
@param intf string, interface
|
||||
@param node Node object
|
||||
@param print_error if true, print error
|
||||
'''
|
||||
cmd = 'ip link set ' + intf + ' netns ' + repr(node.pid)
|
||||
quietRun(cmd)
|
||||
links = node.cmd('ip link show')
|
||||
def moveIntfNoRetry( intf, node, printError=False ):
|
||||
"""Move interface to node, without retrying.
|
||||
intf: string, interface
|
||||
node: Node object
|
||||
printError: if true, print error"""
|
||||
cmd = 'ip link set ' + intf + ' netns ' + repr( node.pid )
|
||||
quietRun( cmd )
|
||||
links = node.cmd( 'ip link show' )
|
||||
if not intf in links:
|
||||
if print_error:
|
||||
lg.error('*** Error: moveIntf: % not successfully moved to %s:\n' %
|
||||
(intf, node.name))
|
||||
if printError:
|
||||
lg.error( '*** Error: moveIntf: ' + intf +
|
||||
' not successfully moved to ' + node.name + '\n' )
|
||||
return False
|
||||
return True
|
||||
|
||||
def moveIntf( intf, node, printError=False, retries=3, delaySecs=0.001 ):
|
||||
"""Move interface to node, retrying on failure.
|
||||
intf: string, interface
|
||||
node: Node object
|
||||
printError: if true, print error"""
|
||||
retry( retries, delaySecs, moveIntf, intf, node, printError )
|
||||
|
||||
def retry(n, retry_delay, fn, *args, **keywords):
|
||||
'''Try something N times before giving up.
|
||||
|
||||
@param n number of times to retry
|
||||
@param retry_delay seconds wait this long between tries
|
||||
@param fn function to call
|
||||
@param args args to apply to function call
|
||||
'''
|
||||
tries = 0
|
||||
while not fn(*args, **keywords) and tries < n:
|
||||
sleep(retry_delay)
|
||||
tries += 1
|
||||
if tries >= n:
|
||||
lg.error("*** gave up after %i retries\n" % tries)
|
||||
exit(1)
|
||||
|
||||
|
||||
# delay between interface move checks in seconds
|
||||
MOVEINTF_DELAY = 0.0001
|
||||
|
||||
CREATE_LINK_RETRIES = 10
|
||||
|
||||
|
||||
def createLink(node1, node2):
|
||||
'''Create a link between nodes, making an interface for each.
|
||||
|
||||
@param node1 Node object
|
||||
@param node2 Node object
|
||||
'''
|
||||
def createLink( node1, node2, retries=10, delaySecs=0.001 ):
|
||||
"""Create a link between nodes, making an interface for each.
|
||||
node1: Node object
|
||||
node2: Node object"""
|
||||
intf1 = node1.newIntf()
|
||||
intf2 = node2.newIntf()
|
||||
makeIntfPair(intf1, intf2)
|
||||
makeIntfPair( intf1, intf2 )
|
||||
if node1.inNamespace:
|
||||
retry(CREATE_LINK_RETRIES, MOVEINTF_DELAY, moveIntf, intf1, node1)
|
||||
retry( retries, delaySecs, moveIntf, intf1, node1 )
|
||||
if node2.inNamespace:
|
||||
retry(CREATE_LINK_RETRIES, MOVEINTF_DELAY, moveIntf, intf2, node2)
|
||||
node1.connection[intf1] = (node2, intf2)
|
||||
node2.connection[intf2] = (node1, intf1)
|
||||
retry( retries, delaySecs, moveIntf, intf2, node2 )
|
||||
node1.connection[ intf1 ] = ( node2, intf2 )
|
||||
node2.connection[ intf2 ] = ( node1, intf1 )
|
||||
return intf1, intf2
|
||||
|
||||
|
||||
def fixLimits():
|
||||
'''Fix ridiculously small resource limits.'''
|
||||
setrlimit(RLIMIT_NPROC, (4096, 8192))
|
||||
setrlimit(RLIMIT_NOFILE, (16384, 32768))
|
||||
"Fix ridiculously small resource limits."
|
||||
setrlimit( RLIMIT_NPROC, ( 4096, 8192 ) )
|
||||
setrlimit( RLIMIT_NOFILE, ( 16384, 32768 ) )
|
||||
|
||||
|
||||
def _colonHex(val, bytes):
|
||||
'''Generate colon-hex string.
|
||||
|
||||
@param val input as unsigned int
|
||||
@param bytes number of bytes to convert
|
||||
@return ch_str colon-hex string
|
||||
'''
|
||||
def _colonHex( val, bytes ):
|
||||
"""Generate colon-hex string.
|
||||
val: input as unsigned int
|
||||
bytes: number of bytes to convert
|
||||
returns: chStr colon-hex string"""
|
||||
pieces = []
|
||||
for i in range(bytes - 1, -1, -1):
|
||||
pieces.append('%02x' % (((0xff << (i * 8)) & val) >> (i * 8)))
|
||||
ch_str = ':'.join(pieces)
|
||||
return ch_str
|
||||
for i in range( bytes - 1, -1, -1 ):
|
||||
piece = ( ( 0xff << ( i * 8 ) ) & val ) >> ( i * 8 )
|
||||
pieces.append( '%02x' % piece )
|
||||
chStr = ':'.join( pieces )
|
||||
return chStr
|
||||
|
||||
def macColonHex( mac ):
|
||||
"""Generate MAC colon-hex string from unsigned int.
|
||||
mac: MAC address as unsigned int
|
||||
returns: macStr MAC colon-hex string"""
|
||||
return _colonHex( mac, 6 )
|
||||
|
||||
def macColonHex(mac):
|
||||
'''Generate MAC colon-hex string from unsigned int.
|
||||
|
||||
@param mac MAC address as unsigned int
|
||||
@return mac_str MAC colon-hex string
|
||||
'''
|
||||
return _colonHex(mac, 6)
|
||||
|
||||
|
||||
def ipStr(ip):
|
||||
'''Generate IP address string
|
||||
|
||||
@return ip addr string
|
||||
'''
|
||||
hi = (ip & 0xff0000) >> 16
|
||||
mid = (ip & 0xff00) >> 8
|
||||
def ipStr( ip ):
|
||||
"""Generate IP address string
|
||||
returns: ip addr string"""
|
||||
hi = ( ip & 0xff0000 ) >> 16
|
||||
mid = ( ip & 0xff00 ) >> 8
|
||||
lo = ip & 0xff
|
||||
return "10.%i.%i.%i" % (hi, mid, lo)
|
||||
return "10.%i.%i.%i" % ( hi, mid, lo )
|
||||
|
||||
Executable → Regular
+24
-31
@@ -1,51 +1,44 @@
|
||||
#!/usr/bin/env python
|
||||
"""XTerm creation and cleanup.
|
||||
|
||||
Utility functions to run an xterm (connected via screen(1)) on each host.
|
||||
"""
|
||||
XTerm creation and cleanup.
|
||||
Utility functions to run an xterm ( connected via screen( 1 ) ) on each host.
|
||||
|
||||
Requires xterm(1) and GNU screen(1).
|
||||
Requires xterm( 1 ) and GNU screen( 1 ).
|
||||
"""
|
||||
|
||||
import re
|
||||
from subprocess import Popen
|
||||
|
||||
from mininet.util import quietRun
|
||||
|
||||
|
||||
def makeXterm(node, title):
|
||||
'''Run screen on a node, and hook up an xterm.
|
||||
|
||||
@param node Node object
|
||||
@param title base title
|
||||
@return process created
|
||||
'''
|
||||
def makeXterm( node, title ):
|
||||
"""Run screen on a node, and hook up an xterm.
|
||||
node: Node object
|
||||
title: base title
|
||||
returns: process created"""
|
||||
title += ': ' + node.name
|
||||
if not node.inNamespace:
|
||||
title += ' (root)'
|
||||
cmd = ['xterm', '-title', title, '-e']
|
||||
cmd = [ 'xterm', '-title', title, '-e' ]
|
||||
if not node.execed:
|
||||
node.cmdPrint('screen -dmS ' + node.name)
|
||||
cmd += ['screen', '-D', '-RR', '-S', node.name]
|
||||
node.cmdPrint( 'screen -dmS ' + node.name )
|
||||
cmd += [ 'screen', '-D', '-RR', '-S', node.name ]
|
||||
else:
|
||||
cmd += ['sh', '-c', 'exec tail -f /tmp/' + node.name + '*.log']
|
||||
return Popen(cmd)
|
||||
|
||||
cmd += [ 'sh', '-c', 'exec tail -f /tmp/' + node.name + '*.log' ]
|
||||
return Popen( cmd )
|
||||
|
||||
def cleanUpScreens():
|
||||
'''Remove moldy old screen sessions.'''
|
||||
"Remove moldy old screen sessions."
|
||||
r = r'(\d+.[hsc]\d+)'
|
||||
output = quietRun('screen -ls').split('\n')
|
||||
output = quietRun( 'screen -ls' ).split( '\n' )
|
||||
for line in output:
|
||||
m = re.search(r, line)
|
||||
m = re.search( r, line )
|
||||
if m:
|
||||
quietRun('screen -S ' + m.group(1) + ' -X kill')
|
||||
quietRun( 'screen -S ' + m.group( 1 ) + ' -X kill' )
|
||||
|
||||
|
||||
def makeXterms(nodes, title):
|
||||
'''Create XTerms.
|
||||
|
||||
@param nodes list of Node objects
|
||||
@param title base title for each
|
||||
@return list of created xterm processes
|
||||
'''
|
||||
return [makeXterm(node, title) for node in nodes]
|
||||
def makeXterms( nodes, title ):
|
||||
"""Create XTerms.
|
||||
nodes: list of Node objects
|
||||
title: base title for each
|
||||
returns: list of created xterm processes"""
|
||||
return [ makeXterm( node, title ) for node in nodes ]
|
||||
|
||||
+8
-8
@@ -1,16 +1,16 @@
|
||||
#!/usr/bin/python
|
||||
|
||||
"""
|
||||
unpep8:
|
||||
|
||||
Translate from PEP8 Python style to Mininet (i.e. Arista-like)
|
||||
Python style:
|
||||
Python style
|
||||
|
||||
- Reinstates CapWords for methods and instance variables.
|
||||
- Gets rid of triple single quotes.
|
||||
- Eliminates triple quotes on single lines.
|
||||
- Inserts extra spaces to improve readability.
|
||||
- Fixes Doxygen (or doxypy) ugliness.
|
||||
usage: unpep8 < old.py > new.py
|
||||
|
||||
- Reinstates CapWords for methods and instance variables
|
||||
- Gets rid of triple single quotes
|
||||
- Eliminates triple quotes on single lines
|
||||
- Inserts extra spaces to improve readability
|
||||
- Fixes Doxygen (or doxypy) ugliness
|
||||
|
||||
Does the following translations:
|
||||
|
||||
|
||||
Reference in New Issue
Block a user