Pass pylint.

This commit is contained in:
Bob Lantz
2010-05-06 16:24:15 -07:00
parent 259d713315
commit 82b7207295
11 changed files with 465 additions and 400 deletions
+1 -1
View File
@@ -9,7 +9,7 @@ EXAMPLES = examples/*.py
BIN = bin/mn BIN = bin/mn
PYSRC = $(MININET) $(TEST) $(EXAMPLES) $(BIN) PYSRC = $(MININET) $(TEST) $(EXAMPLES) $(BIN)
P8IGN = E251,E201,E302 P8IGN = E251,E201,E302,E202
codecheck: $(PYSRC) codecheck: $(PYSRC)
pyflakes $(PYSRC) pyflakes $(PYSRC)
+70 -65
View File
@@ -10,7 +10,7 @@ We monitor nodes in a couple of ways:
- First, each individual node is monitored, and its output is added - First, each individual node is monitored, and its output is added
to its console window to its console window
- Second, each time a console window gets iperf output, it is parsed - Second, each time a console window gets iperf output, it is parsed
and accumulated. Once we have output for all consoles, a bar is and accumulated. Once we have output for all consoles, a bar is
added to the bandwidth graph. added to the bandwidth graph.
@@ -36,7 +36,7 @@ from mininet.util import quietRun
class Console( Frame ): class Console( Frame ):
"A simple console on a host." "A simple console on a host."
def __init__( self, parent, net, node, height=10, width=32, title='Node' ): def __init__( self, parent, net, node, height=10, width=32, title='Node' ):
Frame.__init__( self, parent ) Frame.__init__( self, parent )
@@ -44,10 +44,10 @@ class Console( Frame ):
self.node = node self.node = node
self.prompt = node.name + '# ' self.prompt = node.name + '# '
self.height, self.width, self.title = height, width, title self.height, self.width, self.title = height, width, title
# Initialize widget styles # Initialize widget styles
self.buttonStyle = { 'font': 'Monaco 7' } self.buttonStyle = { 'font': 'Monaco 7' }
self.textStyle = { self.textStyle = {
'font': 'Monaco 7', 'font': 'Monaco 7',
'bg': 'black', 'bg': 'black',
'fg': 'green', 'fg': 'green',
@@ -58,22 +58,22 @@ class Console( Frame ):
'highlightcolor': 'green', 'highlightcolor': 'green',
'selectforeground': 'black', 'selectforeground': 'black',
'selectbackground': 'green' 'selectbackground': 'green'
} }
# Set up widgets # Set up widgets
self.text = self.makeWidgets( ) self.text = self.makeWidgets( )
self.bindEvents() self.bindEvents()
self.sendCmd( 'export TERM=dumb' ) self.sendCmd( 'export TERM=dumb' )
self.outputHook = None self.outputHook = None
def makeWidgets( self ): def makeWidgets( self ):
"Make a label, a text area, and a scroll bar." "Make a label, a text area, and a scroll bar."
def newTerm( net=self.net, node=self.node, title=self.title ): def newTerm( net=self.net, node=self.node, title=self.title ):
"Pop up a new terminal window for a node." "Pop up a new terminal window for a node."
net.terms += makeTerms( [ node ], title ) net.terms += makeTerms( [ node ], title )
label = Button( self, text=self.node.name, command=newTerm, label = Button( self, text=self.node.name, command=newTerm,
**self.buttonStyle ) **self.buttonStyle )
label.pack( side='top', fill='x' ) label.pack( side='top', fill='x' )
text = Text( self, wrap='word', **self.textStyle ) text = Text( self, wrap='word', **self.textStyle )
@@ -99,7 +99,7 @@ class Console( Frame ):
# We're not a terminal (yet?), so we ignore the following # We're not a terminal (yet?), so we ignore the following
# control characters other than [\b\n\r] # control characters other than [\b\n\r]
ignoreChars = re.compile( r'[\x00-\x07\x09\x0b\x0c\x0e-\x1f]+' ) ignoreChars = re.compile( r'[\x00-\x07\x09\x0b\x0c\x0e-\x1f]+' )
def append( self, text ): def append( self, text ):
"Append something to our text frame." "Append something to our text frame."
text = self.ignoreChars.sub( '', text ) text = self.ignoreChars.sub( '', text )
@@ -114,7 +114,7 @@ class Console( Frame ):
char = event.char char = event.char
if self.node.waiting: if self.node.waiting:
self.node.write( char ) self.node.write( char )
def handleReturn( self, event ): def handleReturn( self, event ):
"Handle a carriage return." "Handle a carriage return."
cmd = self.text.get( 'insert linestart', 'insert lineend' ) cmd = self.text.get( 'insert linestart', 'insert lineend' )
@@ -127,24 +127,29 @@ class Console( Frame ):
if pos >= 0: if pos >= 0:
cmd = cmd[ pos + len( self.prompt ): ] cmd = cmd[ pos + len( self.prompt ): ]
self.sendCmd( cmd ) self.sendCmd( cmd )
# Callback ignores event
# pylint: disable-msg=W0613
def handleInt( self, event=None ): def handleInt( self, event=None ):
"Handle control-c." "Handle control-c."
self.node.sendInt() self.node.sendInt()
# pylint: enable-msg=W0613
def sendCmd( self, cmd ): def sendCmd( self, cmd ):
"Send a command to our node." "Send a command to our node."
text, node = self.text, self.node if not self.node.waiting:
if not node.waiting: self.node.sendCmd( cmd )
node.sendCmd( cmd )
def handleReadable( self, file=None, mask=None, timeoutms=None ): # Callback ignores fds
# pylint: disable-msg=W0613
def handleReadable( self, fds, timeoutms=None ):
"Handle file readable event." "Handle file readable event."
data = self.node.monitor( timeoutms ) data = self.node.monitor( timeoutms )
self.append( data ) self.append( data )
if not self.node.waiting: if not self.node.waiting:
# Print prompt # Print prompt
self.append( self.prompt ) self.append( self.prompt )
# pylint: enable-msg=W0613
def waiting( self ): def waiting( self ):
"Are we waiting for output?" "Are we waiting for output?"
@@ -161,17 +166,17 @@ class Console( Frame ):
"Clear all of our text." "Clear all of our text."
self.text.delete( '1.0', 'end' ) self.text.delete( '1.0', 'end' )
class Graph( Frame ): class Graph( Frame ):
"Graph that we can add bars to over time." "Graph that we can add bars to over time."
def __init__( self, parent=None, def __init__( self, parent=None,
bg = 'white', bg = 'white',
gheight=200, gwidth=500, gheight=200, gwidth=500,
barwidth=10, barwidth=10,
ymax=3.5,): ymax=3.5,):
Frame.__init__( self, parent ) Frame.__init__( self, parent )
self.bg = bg self.bg = bg
@@ -182,57 +187,55 @@ class Graph( Frame ):
self.xpos = 0 self.xpos = 0
# Create everything # Create everything
self.title = self.graph = None self.title, self.scale, self.graph = self.createWidgets()
self.createWidgets()
self.updateScrollRegions() self.updateScrollRegions()
self.yview( 'moveto', '1.0' ) self.yview( 'moveto', '1.0' )
def createScale( self ):
def scale( self ):
"Create a and return a new canvas with scale markers." "Create a and return a new canvas with scale markers."
height = float( self.gheight ) height = float( self.gheight )
width = 25 width = 25
ymax = self.ymax ymax = self.ymax
scale = Canvas( self, width=width, height=height, background=self.bg ) scale = Canvas( self, width=width, height=height,
fill = 'red' background=self.bg )
opts = { 'fill': 'red' }
# Draw scale line # Draw scale line
scale.create_line( width - 1, height, width - 1, 0, fill=fill ) scale.create_line( width - 1, height, width - 1, 0, **opts )
# Draw ticks and numbers # Draw ticks and numbers
for y in range( 0, int( ymax + 1 ) ): for y in range( 0, int( ymax + 1 ) ):
ypos = height * (1 - float( y ) / ymax ) ypos = height * (1 - float( y ) / ymax )
scale.create_line( width, ypos, width - 10, ypos, fill=fill ) scale.create_line( width, ypos, width - 10, ypos, **opts )
scale.create_text( 10, ypos, text=str( y ), fill=fill ) scale.create_text( 10, ypos, text=str( y ), **opts )
return scale return scale
def updateScrollRegions( self ): def updateScrollRegions( self ):
"Update graph and scale scroll regions." "Update graph and scale scroll regions."
ofs = 20 ofs = 20
height = self.gheight + ofs height = self.gheight + ofs
self.graph.configure( scrollregion=( 0, -ofs, self.graph.configure( scrollregion=( 0, -ofs,
self.xpos * self.barwidth, height ) ) self.xpos * self.barwidth, height ) )
self.scale.configure( scrollregion=( 0, -ofs, 0, height ) ) self.scale.configure( scrollregion=( 0, -ofs, 0, height ) )
def yview( self, *args ): def yview( self, *args ):
"Scroll both scale and graph." "Scroll both scale and graph."
self.graph.yview( *args ) self.graph.yview( *args )
self.scale.yview( *args ) self.scale.yview( *args )
def createWidgets( self ): def createWidgets( self ):
"Create initial widget set." "Create initial widget set."
# Objects # Objects
title = Label( self, text="Bandwidth (Gb/s)", bg=self.bg ) title = Label( self, text='Bandwidth (Gb/s)', bg=self.bg )
width = self.gwidth width = self.gwidth
height = self.gheight height = self.gheight
scale = self.scale() scale = self.createScale()
graph = Canvas( self, width=width, height=height, background=self.bg) graph = Canvas( self, width=width, height=height, background=self.bg)
xbar = Scrollbar( self, orient='horizontal', command=graph.xview ) xbar = Scrollbar( self, orient='horizontal', command=graph.xview )
ybar = Scrollbar( self, orient='vertical', command=self.yview ) ybar = Scrollbar( self, orient='vertical', command=self.yview )
graph.configure( xscrollcommand=xbar.set, yscrollcommand=ybar.set, graph.configure( xscrollcommand=xbar.set, yscrollcommand=ybar.set,
scrollregion=(0, 0, width, height ) ) scrollregion=(0, 0, width, height ) )
scale.configure( yscrollcommand=ybar.set ) scale.configure( yscrollcommand=ybar.set )
# Layout # Layout
title.grid( row=0, columnspan=3, sticky='new') title.grid( row=0, columnspan=3, sticky='new')
scale.grid( row=1, column=0, sticky='nsew' ) scale.grid( row=1, column=0, sticky='nsew' )
@@ -241,12 +244,8 @@ class Graph( Frame ):
xbar.grid( row=2, column=0, columnspan=2, sticky='ew' ) xbar.grid( row=2, column=0, columnspan=2, sticky='ew' )
self.rowconfigure( 1, weight=1 ) self.rowconfigure( 1, weight=1 )
self.columnconfigure( 1, weight=1 ) self.columnconfigure( 1, weight=1 )
# Save for future reference return title, scale, graph
self.title = title
self.scale = scale
self.graph = graph
return graph
def addBar( self, yval ): def addBar( self, yval ):
"Add a new bar to our graph." "Add a new bar to our graph."
percent = yval / self.ymax percent = yval / self.ymax
@@ -279,6 +278,8 @@ class Graph( Frame ):
class ConsoleApp( Frame ): class ConsoleApp( Frame ):
"Simple Tk consoles for Mininet."
menuStyle = { 'font': 'Geneva 7 bold' } menuStyle = { 'font': 'Geneva 7 bold' }
def __init__( self, net, parent=None, width=4 ): def __init__( self, net, parent=None, width=4 ):
@@ -289,14 +290,14 @@ class ConsoleApp( Frame ):
self.menubar = self.createMenuBar() self.menubar = self.createMenuBar()
cframe = self.cframe = Frame( self ) cframe = self.cframe = Frame( self )
self.consoles = {} # consoles themselves self.consoles = {} # consoles themselves
titles = { titles = {
'hosts': 'Host', 'hosts': 'Host',
'switches': 'Switch', 'switches': 'Switch',
'controllers': 'Controller' 'controllers': 'Controller'
} }
for name in titles: for name in titles:
nodes = getattr( net, name ) nodes = getattr( net, name )
frame, consoles = self.createConsoles( frame, consoles = self.createConsoles(
cframe, nodes, width, titles[ name ] ) cframe, nodes, width, titles[ name ] )
self.consoles[ name ] = Object( frame=frame, consoles=consoles ) self.consoles[ name ] = Object( frame=frame, consoles=consoles )
self.selected = None self.selected = None
@@ -305,7 +306,7 @@ class ConsoleApp( Frame ):
cleanUpScreens() cleanUpScreens()
# Close window gracefully # Close window gracefully
Wm.wm_protocol( self.top, name='WM_DELETE_WINDOW', func=self.quit ) Wm.wm_protocol( self.top, name='WM_DELETE_WINDOW', func=self.quit )
# Initialize graph # Initialize graph
graph = Graph( cframe ) graph = Graph( cframe )
self.consoles[ 'graph' ] = Object( frame=graph, consoles=[ graph ] ) self.consoles[ 'graph' ] = Object( frame=graph, consoles=[ graph ] )
@@ -316,7 +317,9 @@ class ConsoleApp( Frame ):
self.bw = 0 self.bw = 0
self.pack( expand=True, fill='both' ) self.pack( expand=True, fill='both' )
# Update callback doesn't use console arg
# pylint: disable-msg=W0613
def updateGraph( self, console, output ): def updateGraph( self, console, output ):
"Update our graph." "Update our graph."
m = re.search( r'(\d+) Mbits/sec', output ) m = re.search( r'(\d+) Mbits/sec', output )
@@ -328,13 +331,15 @@ class ConsoleApp( Frame ):
self.graph.addBar( self.bw ) self.graph.addBar( self.bw )
self.bw = 0 self.bw = 0
self.updates = 0 self.updates = 0
# pylint: enable-msg=W0613
def setOutputHook( self, fn=None, consoles=None ): def setOutputHook( self, fn=None, consoles=None ):
"Register fn as output hook [on specific consoles.]"
if consoles is None: if consoles is None:
consoles = self.consoles[ 'hosts' ].consoles consoles = self.consoles[ 'hosts' ].consoles
for console in consoles: for console in consoles:
console.outputHook = fn console.outputHook = fn
def createConsoles( self, parent, nodes, width, title ): def createConsoles( self, parent, nodes, width, title ):
"Create a grid of consoles in a frame." "Create a grid of consoles in a frame."
f = Frame( parent ) f = Frame( parent )
@@ -342,7 +347,7 @@ class ConsoleApp( Frame ):
consoles = [] consoles = []
index = 0 index = 0
for node in nodes: for node in nodes:
console = Console( f, net, node, title=title ) console = Console( f, self.net, node, title=title )
consoles.append( console ) consoles.append( console )
row = index / width row = index / width
column = index % width column = index % width
@@ -351,12 +356,12 @@ class ConsoleApp( Frame ):
f.rowconfigure( row, weight=1 ) f.rowconfigure( row, weight=1 )
f.columnconfigure( column, weight=1 ) f.columnconfigure( column, weight=1 )
return f, consoles return f, consoles
def select( self, set ): def select( self, groupName ):
"Select a set of consoles to display." "Select a group of consoles to display."
if self.selected is not None: if self.selected is not None:
self.selected.frame.pack_forget() self.selected.frame.pack_forget()
self.selected = self.consoles[ set ] self.selected = self.consoles[ groupName ]
self.selected.frame.pack( expand=True, fill='both' ) self.selected.frame.pack( expand=True, fill='both' )
def createMenuBar( self ): def createMenuBar( self ):
@@ -365,8 +370,8 @@ class ConsoleApp( Frame ):
buttons = [ buttons = [
( 'Hosts', lambda: self.select( 'hosts' ) ), ( 'Hosts', lambda: self.select( 'hosts' ) ),
( 'Switches', lambda: self.select( 'switches' ) ), ( 'Switches', lambda: self.select( 'switches' ) ),
( 'Controllers', lambda: self.select( 'controllers' ) ), ( 'Controllers', lambda: self.select( 'controllers' ) ),
( 'Graph', lambda: self.select( 'graph' ) ), ( 'Graph', lambda: self.select( 'graph' ) ),
( 'Ping', self.ping ), ( 'Ping', self.ping ),
( 'Iperf', self.iperf ), ( 'Iperf', self.iperf ),
( 'Interrupt', self.stop ), ( 'Interrupt', self.stop ),
@@ -378,12 +383,12 @@ class ConsoleApp( Frame ):
b.pack( side='left' ) b.pack( side='left' )
f.pack( padx=4, pady=4, fill='x' ) f.pack( padx=4, pady=4, fill='x' )
return f return f
def clear( self ): def clear( self ):
"Clear selection." "Clear selection."
for console in self.selected.consoles: for console in self.selected.consoles:
console.clear() console.clear()
def waiting( self, consoles=None ): def waiting( self, consoles=None ):
"Are any of our hosts waiting for output?" "Are any of our hosts waiting for output?"
if consoles is None: if consoles is None:
@@ -453,8 +458,8 @@ class Object( object ):
if __name__ == '__main__': if __name__ == '__main__':
setLogLevel( 'info' ) setLogLevel( 'info' )
net = TreeNet( depth=2, fanout=2 ) network = TreeNet( depth=2, fanout=4 )
net.start() network.start()
app = ConsoleApp( net, width=4 ) app = ConsoleApp( network, width=4 )
app.mainloop() app.mainloop()
net.stop() network.stop()
+9 -9
View File
@@ -1,7 +1,7 @@
#!/usr/bin/python #!/usr/bin/python
""" """
This example shows how to create an empty Mininet object This example shows how to create an empty Mininet object
(without a topology object) and add nodes to it manually. (without a topology object) and add nodes to it manually.
""" """
@@ -13,32 +13,32 @@ from mininet.log import setLogLevel, info
def emptyNet(): def emptyNet():
"Create an empty network and add nodes to it." "Create an empty network and add nodes to it."
net = Mininet( controller=Controller ) net = Mininet( controller=Controller )
info( '*** Adding controller\n' ) info( '*** Adding controller\n' )
net.addController( 'c0' ) net.addController( 'c0' )
info( '*** Adding hosts\n' ) info( '*** Adding hosts\n' )
h1 = net.addHost( 'h1', ip='10.0.0.1' ) h1 = net.addHost( 'h1', ip='10.0.0.1' )
h2 = net.addHost( 'h2', ip='10.0.0.2' ) h2 = net.addHost( 'h2', ip='10.0.0.2' )
info( '*** Adding switch\n' ) info( '*** Adding switch\n' )
s3 = net.addSwitch( 's3' ) s3 = net.addSwitch( 's3' )
info( '*** Creating links\n' ) info( '*** Creating links\n' )
h1.linkTo( s3 ) h1.linkTo( s3 )
h2.linkTo( s3 ) h2.linkTo( s3 )
info( '*** Starting network\n') info( '*** Starting network\n')
net.start() net.start()
info( '*** Running CLI\n' ) info( '*** Running CLI\n' )
CLI( net ) CLI( net )
info( '*** Stopping network' ) info( '*** Stopping network' )
net.stop() net.stop()
if __name__ == '__main__': if __name__ == '__main__':
setLogLevel( 'info' ) setLogLevel( 'info' )
emptyNet() emptyNet()
+223 -177
View File
@@ -24,9 +24,9 @@ from mininet.term import makeTerm, cleanUpScreens
class MiniEdit( Frame ): class MiniEdit( Frame ):
"A simple network editor for Mininet." "A simple network editor for Mininet."
def __init__( self, parent=None, cheight=200, cwidth=500 ): def __init__( self, parent=None, cheight=200, cwidth=500 ):
Frame.__init__( self, parent ) Frame.__init__( self, parent )
self.action = None self.action = None
self.appName = 'MiniEdit' self.appName = 'MiniEdit'
@@ -39,19 +39,19 @@ class MiniEdit( Frame ):
# Title # Title
self.top = self.winfo_toplevel() self.top = self.winfo_toplevel()
self.top.title( self.appName ) self.top.title( self.appName )
# Menu bar # Menu bar
self.createMenubar() self.createMenubar()
# Editing canvas # Editing canvas
self.cheight, self.cwidth = cheight, cwidth self.cheight, self.cwidth = cheight, cwidth
self.cframe, self.canvas = self.createCanvas() self.cframe, self.canvas = self.createCanvas()
# Toolbar # Toolbar
self.images = miniEditImages()
self.buttons = {} self.buttons = {}
self.active = None self.active = None
self.tools = ( 'Select', 'Host', 'Switch', 'Link' ) self.tools = ( 'Select', 'Host', 'Switch', 'Link' )
self.images = self.createImages()
self.customColors = { 'Switch': 'darkGreen', 'Host': 'blue' } self.customColors = { 'Switch': 'darkGreen', 'Host': 'blue' }
self.toolbar = self.createToolbar() self.toolbar = self.createToolbar()
@@ -61,49 +61,53 @@ class MiniEdit( Frame ):
self.columnconfigure( 1, weight=1 ) self.columnconfigure( 1, weight=1 )
self.rowconfigure( 0, weight=1 ) self.rowconfigure( 0, weight=1 )
self.pack( expand=True, fill='both' ) self.pack( expand=True, fill='both' )
# About box # About box
self.aboutBox = None self.aboutBox = None
# Initialize node data # Initialize node data
self.nodeBindings = self.createNodeBindings() self.nodeBindings = self.createNodeBindings()
self.nodePrefixes = { 'Switch': 's', 'Host': 'h' } self.nodePrefixes = { 'Switch': 's', 'Host': 'h' }
self.widgetToItem = {} self.widgetToItem = {}
self.itemToWidget = {} self.itemToWidget = {}
# Initialize link tool # Initialize link tool
self.link = self.linkWidget = None self.link = self.linkWidget = None
# Selection support # Selection support
self.selection = None self.selection = None
# Keyboard bindings # Keyboard bindings
self.bind( '<Control-q>', lambda event: self.quit() ) self.bind( '<Control-q>', lambda event: self.quit() )
self.bind( '<KeyPress-Delete>', self.deleteSelection ) self.bind( '<KeyPress-Delete>', self.deleteSelection )
self.bind( '<KeyPress-BackSpace>', self.deleteSelection ) self.bind( '<KeyPress-BackSpace>', self.deleteSelection )
self.focus() self.focus()
# Event handling initalization
self.linkx = self.linky = self.linkItem = None
self.lastSelection = None
# Model initialization # Model initialization
self.links = {} self.links = {}
self.nodeCount = 0 self.nodeCount = 0
self.net = None self.net = None
# Close window gracefully # Close window gracefully
Wm.wm_protocol( self.top, name='WM_DELETE_WINDOW', func=self.quit ) Wm.wm_protocol( self.top, name='WM_DELETE_WINDOW', func=self.quit )
def quit( self ): def quit( self ):
"Stop our network, if any, then quit." "Stop our network, if any, then quit."
self.stop() self.stop()
Frame.quit( self ) Frame.quit( self )
def createMenubar( self ): def createMenubar( self ):
"Create our menu bar." "Create our menu bar."
font = self.font font = self.font
mbar = Menu( self.top, font=font ) mbar = Menu( self.top, font=font )
self.top.configure( menu=mbar ) self.top.configure( menu=mbar )
# Application menu # Application menu
appMenu = Menu( mbar, tearoff=False ) appMenu = Menu( mbar, tearoff=False )
mbar.add_cascade( label=self.appName, font=font, menu=appMenu ) mbar.add_cascade( label=self.appName, font=font, menu=appMenu )
@@ -111,20 +115,18 @@ class MiniEdit( Frame ):
font=font) font=font)
appMenu.add_separator() appMenu.add_separator()
appMenu.add_command( label='Quit', command=self.quit, font=font ) appMenu.add_command( label='Quit', command=self.quit, font=font )
""" #fileMenu = Menu( mbar, tearoff=False )
fileMenu = Menu( mbar, tearoff=False ) #mbar.add_cascade( label="File", font=font, menu=fileMenu )
mbar.add_cascade( label="File", font=font, menu=fileMenu ) #fileMenu.add_command( label="Load...", font=font )
fileMenu.add_command( label="Load...", font=font ) #fileMenu.add_separator()
fileMenu.add_separator() #fileMenu.add_command( label="Save", font=font )
fileMenu.add_command( label="Save", font=font ) #fileMenu.add_separator()
fileMenu.add_separator() #fileMenu.add_command( label="Print", font=font )
fileMenu.add_command( label="Print", font=font )
"""
editMenu = Menu( mbar, tearoff=False ) editMenu = Menu( mbar, tearoff=False )
mbar.add_cascade( label="Edit", font=font, menu=editMenu ) mbar.add_cascade( label="Edit", font=font, menu=editMenu )
editMenu.add_command( label="Cut", font=font, editMenu.add_command( label="Cut", font=font,
command=lambda: self.deleteSelection( None ) ) command=lambda: self.deleteSelection( None ) )
runMenu = Menu( mbar, tearoff=False ) runMenu = Menu( mbar, tearoff=False )
@@ -135,28 +137,28 @@ class MiniEdit( Frame ):
runMenu.add_command( label='Xterm', font=font, command=self.xterm ) runMenu.add_command( label='Xterm', font=font, command=self.xterm )
# Canvas # Canvas
def createCanvas( self ): def createCanvas( self ):
"Create and return our scrolling canvas frame." "Create and return our scrolling canvas frame."
f = Frame( self ) f = Frame( self )
canvas = Canvas( f, width=self.cwidth, height=self.cheight, canvas = Canvas( f, width=self.cwidth, height=self.cheight,
bg=self.bg ) bg=self.bg )
# Scroll bars # Scroll bars
xbar = Scrollbar( f, orient='horizontal', command=canvas.xview ) xbar = Scrollbar( f, orient='horizontal', command=canvas.xview )
ybar = Scrollbar( f, orient='vertical', command=canvas.yview ) ybar = Scrollbar( f, orient='vertical', command=canvas.yview )
canvas.configure( xscrollcommand=xbar.set, yscrollcommand=ybar.set ) canvas.configure( xscrollcommand=xbar.set, yscrollcommand=ybar.set )
# Resize box # Resize box
resize = Label( f, bg='white' ) resize = Label( f, bg='white' )
# Layout # Layout
canvas.grid( row=0, column=1, sticky='nsew') canvas.grid( row=0, column=1, sticky='nsew')
ybar.grid( row=0, column=2, sticky='ns') ybar.grid( row=0, column=2, sticky='ns')
xbar.grid( row=1, column=1, sticky='ew' ) xbar.grid( row=1, column=1, sticky='ew' )
resize.grid( row=1, column=2, sticky='nsew' ) resize.grid( row=1, column=2, sticky='nsew' )
# Resize behavior # Resize behavior
f.rowconfigure( 0, weight=1 ) f.rowconfigure( 0, weight=1 )
f.columnconfigure( 1, weight=1 ) f.columnconfigure( 1, weight=1 )
@@ -167,7 +169,7 @@ class MiniEdit( Frame ):
canvas.bind( '<ButtonPress-1>', self.clickCanvas ) canvas.bind( '<ButtonPress-1>', self.clickCanvas )
canvas.bind( '<B1-Motion>', self.dragCanvas ) canvas.bind( '<B1-Motion>', self.dragCanvas )
canvas.bind( '<ButtonRelease-1>', self.releaseCanvas ) canvas.bind( '<ButtonRelease-1>', self.releaseCanvas )
return f, canvas return f, canvas
def updateScrollRegion( self ): def updateScrollRegion( self ):
@@ -175,75 +177,76 @@ class MiniEdit( Frame ):
bbox = self.canvas.bbox( 'all' ) bbox = self.canvas.bbox( 'all' )
if bbox is not None: if bbox is not None:
self.canvas.configure( scrollregion=( 0, 0, bbox[ 2 ], bbox[ 3 ] ) ) self.canvas.configure( scrollregion=( 0, 0, bbox[ 2 ], bbox[ 3 ] ) )
def canvasx( self, x_root ): def canvasx( self, x_root ):
"Convert root x coordinate to canvas coordinate." "Convert root x coordinate to canvas coordinate."
c = self.canvas c = self.canvas
return c.canvasx( x_root ) - c.winfo_rootx() return c.canvasx( x_root ) - c.winfo_rootx()
def canvasy( self, y_root ): def canvasy( self, y_root ):
"Convert root y coordinate to canvas coordinate." "Convert root y coordinate to canvas coordinate."
c = self.canvas c = self.canvas
return c.canvasy( y_root ) - c.winfo_rooty() return c.canvasy( y_root ) - c.winfo_rooty()
# Toolbar # Toolbar
def activate( self, toolName ): def activate( self, toolName ):
"Activate a tool and press its button."
# Adjust button appearance # Adjust button appearance
if self.active: if self.active:
self.buttons[ self.active ].configure( relief='raised' ) self.buttons[ self.active ].configure( relief='raised' )
self.buttons[ toolName ].configure( relief='sunken' ) self.buttons[ toolName ].configure( relief='sunken' )
# Activate dynamic bindings # Activate dynamic bindings
self.active = toolName self.active = toolName
def createToolbar( self ): def createToolbar( self ):
"Create and return our toolbar frame." "Create and return our toolbar frame."
toolbar = Frame( self ) toolbar = Frame( self )
# Tools # Tools
for tool in self.tools: for tool in self.tools:
cmd = lambda t=tool: self.activate( t ) cmd = ( lambda t=tool: self.activate( t ) )
b = Button( toolbar, text=tool, font=self.smallFont, command=cmd) b = Button( toolbar, text=tool, font=self.smallFont, command=cmd)
if tool in self.images: if tool in self.images:
b.config( height=35, image=self.images[ tool ] ) b.config( height=35, image=self.images[ tool ] )
# b.config( compound='top' ) # b.config( compound='top' )
b.pack( fill='x' ) b.pack( fill='x' )
self.buttons[ tool ] = b self.buttons[ tool ] = b
self.activate( self.tools[ 0 ] ) self.activate( self.tools[ 0 ] )
# Spacer # Spacer
Label( toolbar, text='' ).pack() Label( toolbar, text='' ).pack()
# Commands # Commands
for cmd, color in [ ( 'Stop', 'darkRed' ), ( 'Run', 'darkGreen' ) ]: for cmd, color in [ ( 'Stop', 'darkRed' ), ( 'Run', 'darkGreen' ) ]:
def doCmd( f=getattr( self, 'do' + cmd ) ): doCmd = getattr( self, 'do' + cmd )
f() b = Button( toolbar, text=cmd, font=self.smallFont,
b = Button( toolbar, text=cmd, font=self.smallFont, fg=color, command=doCmd ) fg=color, command=doCmd )
b.pack( fill='x', side='bottom' ) b.pack( fill='x', side='bottom' )
return toolbar return toolbar
def doRun( self ): def doRun( self ):
"Run command." "Run command."
self.activate( 'Select' ) self.activate( 'Select' )
for tool in self.tools: for tool in self.tools:
self.buttons[ tool ].config( state='disabled' ) self.buttons[ tool ].config( state='disabled' )
self.start() self.start()
def doStop( self ): def doStop( self ):
"Stop command." "Stop command."
self.stop() self.stop()
for tool in self.tools: for tool in self.tools:
self.buttons[ tool ].config( state='normal' ) self.buttons[ tool ].config( state='normal' )
# Generic canvas handler # Generic canvas handler
# #
# We could have used bindtags, as in nodeIcon, but # We could have used bindtags, as in nodeIcon, but
# the dynamic approach used here # the dynamic approach used here
# may actually require less code. In any case, it's an # may actually require less code. In any case, it's an
# interesting introspection-based alternative to bindtags. # interesting introspection-based alternative to bindtags.
def canvasHandle( self, eventName, event ): def canvasHandle( self, eventName, event ):
"Generic canvas event handler" "Generic canvas event handler"
if self.active is None: if self.active is None:
@@ -252,36 +255,36 @@ class MiniEdit( Frame ):
handler = getattr( self, eventName + toolName, None ) handler = getattr( self, eventName + toolName, None )
if handler is not None: if handler is not None:
handler( event ) handler( event )
def clickCanvas( self, event ): def clickCanvas( self, event ):
"Canvas click handler." "Canvas click handler."
self.canvasHandle( 'click', event ) self.canvasHandle( 'click', event )
def dragCanvas( self, event ): def dragCanvas( self, event ):
"Canvas drag handler." "Canvas drag handler."
self.canvasHandle( 'drag', event ) self.canvasHandle( 'drag', event )
def releaseCanvas( self, event ): def releaseCanvas( self, event ):
"Canvas mouse up handler." "Canvas mouse up handler."
self.canvasHandle( 'release', event ) self.canvasHandle( 'release', event )
# Currently the only items we can select directly are # Currently the only items we can select directly are
# links. Nodes are handled by bindings in the node icon. # links. Nodes are handled by bindings in the node icon.
# If we want to allow node deletion, we will
def findItem( self, x, y ): def findItem( self, x, y ):
"Find items at a location in our canvas."
items = self.canvas.find_overlapping( x, y, x, y ) items = self.canvas.find_overlapping( x, y, x, y )
if len( items ) == 0: if len( items ) == 0:
return None return None
else: else:
return items[ 0 ] return items[ 0 ]
# Canvas bindings for Select, Host, Switch and Link tools # Canvas bindings for Select, Host, Switch and Link tools
def clickSelect( self, event ): def clickSelect( self, event ):
"Select an item." "Select an item."
self.selectItem( self.findItem( event.x, event.y ) ) self.selectItem( self.findItem( event.x, event.y ) )
def deleteItem( self, item ): def deleteItem( self, item ):
"Delete an item." "Delete an item."
# Don't delete while network is running # Don't delete while network is running
@@ -293,23 +296,27 @@ class MiniEdit( Frame ):
if item in self.itemToWidget: if item in self.itemToWidget:
self.deleteNode( item ) self.deleteNode( item )
# Delete from view # Delete from view
self.canvas.delete( item ) self.canvas.delete( item )
# Callback ignores event
# pylint: disable-msg=W0613
def deleteSelection( self, event ): def deleteSelection( self, event ):
"Delete the selected item."
if self.selection is not None: if self.selection is not None:
self.deleteItem( self.selection ) self.deleteItem( self.selection )
self.selectItem( None ) self.selectItem( None )
# pylint: enable-msg=W0613
def nodeIcon( self, node, name ): def nodeIcon( self, node, name ):
"Create a new node icon." "Create a new node icon."
icon = Button( self.canvas, image=self.images[ node ], icon = Button( self.canvas, image=self.images[ node ],
text=name, compound='top' ) text=name, compound='top' )
# Unfortunately bindtags wants a tuple # Unfortunately bindtags wants a tuple
bindtags = [ str( self.nodeBindings ) ] bindtags = [ str( self.nodeBindings ) ]
bindtags += list( icon.bindtags() ) bindtags += list( icon.bindtags() )
icon.bindtags( tuple( bindtags ) ) icon.bindtags( tuple( bindtags ) )
return icon return icon
def newNode( self, node, event ): def newNode( self, node, event ):
"Add a new node to our canvas." "Add a new node to our canvas."
c = self.canvas c = self.canvas
@@ -317,12 +324,13 @@ class MiniEdit( Frame ):
self.nodeCount += 1 self.nodeCount += 1
name = self.nodePrefixes[ node ] + str( self.nodeCount ) name = self.nodePrefixes[ node ] + str( self.nodeCount )
icon = self.nodeIcon( node, name ) icon = self.nodeIcon( node, name )
item = self.canvas.create_window( x, y, anchor='c', window=icon, tags=node ) item = self.canvas.create_window( x, y, anchor='c',
window=icon, tags=node )
self.widgetToItem[ icon ] = item self.widgetToItem[ icon ] = item
self.itemToWidget[ item ] = icon self.itemToWidget[ item ] = icon
self.selectItem( item ) self.selectItem( item )
icon.links = {} icon.links = {}
def clickHost( self, event ): def clickHost( self, event ):
"Add a new host to our canvas." "Add a new host to our canvas."
self.newNode( 'Host', event ) self.newNode( 'Host', event )
@@ -330,7 +338,7 @@ class MiniEdit( Frame ):
def clickSwitch( self, event ): def clickSwitch( self, event ):
"Add a new switch to our canvas." "Add a new switch to our canvas."
self.newNode( 'Switch', event ) self.newNode( 'Switch', event )
def dragLink( self, event ): def dragLink( self, event ):
"Drag a link's endpoint to another node." "Drag a link's endpoint to another node."
if self.link is None: if self.link is None:
@@ -341,40 +349,47 @@ class MiniEdit( Frame ):
c = self.canvas c = self.canvas
c.coords( self.link, self.linkx, self.linky, x, y ) c.coords( self.link, self.linkx, self.linky, x, y )
# Callback ignores event
# pylint: disable-msg=W0613
def releaseLink( self, event ): def releaseLink( self, event ):
"Give up on the current link." "Give up on the current link."
if self.link is not None: if self.link is not None:
self.canvas.delete( self.link ) self.canvas.delete( self.link )
self.linkWidget = self.linkItem = self.link = None self.linkWidget = self.linkItem = self.link = None
# pylint: enable-msg=W0613
# Generic node handlers
# Generic node handlers
def createBindings( self, bindings ):
l = Label()
for event, binding in bindings.items():
l.bind( event, binding )
return l
def createNodeBindings( self ): def createNodeBindings( self ):
"Create a set of bindings for nodes." "Create a set of bindings for nodes."
return self.createBindings( { bindings = {
'<ButtonPress-1>': self.clickNode, '<ButtonPress-1>': self.clickNode,
'<B1-Motion>': self.dragNode, '<B1-Motion>': self.dragNode,
'<ButtonRelease-1>': self.releaseNode, '<ButtonRelease-1>': self.releaseNode,
'<Enter>': self.enterNode, '<Enter>': self.enterNode,
'<Leave>': self.leaveNode, '<Leave>': self.leaveNode,
'<Double-ButtonPress-1>': self.xterm '<Double-ButtonPress-1>': self.xterm
} ) }
l = Label() # lightweight-ish owner for bindings
for event, binding in bindings.items():
l.bind( event, binding )
return l
def selectItem( self, item ): def selectItem( self, item ):
"Select an item and remember old selection."
self.lastSelection = self.selection self.lastSelection = self.selection
self.selection = item self.selection = item
def enterNode( self, event ): def enterNode( self, event ):
"Select node on entry."
self.selectNode( event ) self.selectNode( event )
# Callback ignores event
# pylint: disable-msg=W0613
def leaveNode( self, event ): def leaveNode( self, event ):
"Restore old selection on exit."
self.selectItem( self.lastSelection ) self.selectItem( self.lastSelection )
# pylint: enable-msg=W0613
def clickNode( self, event ): def clickNode( self, event ):
"Node click handler." "Node click handler."
@@ -383,26 +398,26 @@ class MiniEdit( Frame ):
else: else:
self.selectNode( event ) self.selectNode( event )
return 'break' return 'break'
def dragNode( self, event ): def dragNode( self, event ):
"Node drag handler." "Node drag handler."
if self.active is 'Link': if self.active is 'Link':
self.dragLink( event ) self.dragLink( event )
else: else:
self.dragNodeAround( event ) self.dragNodeAround( event )
def releaseNode( self, event ): def releaseNode( self, event ):
"Node release handler." "Node release handler."
if self.active is 'Link': if self.active is 'Link':
self.finishLink( event ) self.finishLink( event )
# Specific node handlers # Specific node handlers
def selectNode( self, event ): def selectNode( self, event ):
"Select the node that was clicked on." "Select the node that was clicked on."
item = self.widgetToItem.get( event.widget, None ) item = self.widgetToItem.get( event.widget, None )
self.selectItem( item ) self.selectItem( item )
def dragNodeAround( self, event ): def dragNodeAround( self, event ):
"Drag a node around on the canvas." "Drag a node around on the canvas."
c = self.canvas c = self.canvas
@@ -429,25 +444,31 @@ class MiniEdit( Frame ):
w = event.widget w = event.widget
item = self.widgetToItem[ w ] item = self.widgetToItem[ w ]
x, y = self.canvas.coords( item ) x, y = self.canvas.coords( item )
self.link = self.canvas.create_line( x, y, x, y, width=4, self.link = self.canvas.create_line( x, y, x, y, width=4,
fill='blue', tag='link' ) fill='blue', tag='link' )
self.linkx, self.linky = x, y self.linkx, self.linky = x, y
self.linkWidget = w self.linkWidget = w
self.linkItem = item self.linkItem = item
# Link bindings # Link bindings
# Selection still needs a bit of work overall # Selection still needs a bit of work overall
# Callbacks ignore event
# pylint: disable-msg=W0613
def select( event, link=self.link ): def select( event, link=self.link ):
"Select item on mouse entry."
self.selectItem( link ) self.selectItem( link )
def highlight( event, link=self.link ): def highlight( event, link=self.link ):
"Highlight item on mouse entry."
# self.selectItem( link ) # self.selectItem( link )
self.canvas.itemconfig( link, fill='green' ) self.canvas.itemconfig( link, fill='green' )
def unhighlight( event, link=self.link ): def unhighlight( event, link=self.link ):
"Unhighlight item on mouse exit."
self.canvas.itemconfig( link, fill='blue' ) self.canvas.itemconfig( link, fill='blue' )
# self.selectItem( None ) # self.selectItem( None )
# pylint: disable-msg=W0613
self.canvas.tag_bind( self.link, '<Enter>', highlight ) self.canvas.tag_bind( self.link, '<Enter>', highlight )
self.canvas.tag_bind( self.link, '<Leave>', unhighlight ) self.canvas.tag_bind( self.link, '<Leave>', unhighlight )
self.canvas.tag_bind( self.link, '<ButtonPress-1>', select ) self.canvas.tag_bind( self.link, '<ButtonPress-1>', select )
def finishLink( self, event ): def finishLink( self, event ):
"Finish creating a link" "Finish creating a link"
if self.link is None: if self.link is None:
@@ -471,13 +492,11 @@ class MiniEdit( Frame ):
x, y = c.coords( target ) x, y = c.coords( target )
c.coords( self.link, self.linkx, self.linky, x, y ) c.coords( self.link, self.linkx, self.linky, x, y )
self.addLink( source, dest ) self.addLink( source, dest )
# We're done # We're done
self.link = self.linkWidget = None self.link = self.linkWidget = None
# Menu handlers # Menu handlers
def about( self ): def about( self ):
"Display about box." "Display about box."
about = self.aboutBox about = self.aboutBox
@@ -494,28 +513,28 @@ class MiniEdit( Frame ):
line1.pack( padx=20, pady=10 ) line1.pack( padx=20, pady=10 )
line2.pack(pady=10 ) line2.pack(pady=10 )
line3.pack(pady=10 ) line3.pack(pady=10 )
hide = lambda about=about: about.withdraw() hide = ( lambda about=about: about.withdraw() )
self.aboutBox = about self.aboutBox = about
# Hide on close rather than destroying window # Hide on close rather than destroying window
Wm.wm_protocol( about, name='WM_DELETE_WINDOW', func=hide ) Wm.wm_protocol( about, name='WM_DELETE_WINDOW', func=hide )
# Show (existing) window # Show (existing) window
about.deiconify() about.deiconify()
def createToolImages( self ): def createToolImages( self ):
"Create toolbar (and icon) images." "Create toolbar (and icon) images."
# Model interface # Model interface
# #
# Ultimately we will either want to use a topo or # Ultimately we will either want to use a topo or
# mininet object here, probably. # mininet object here, probably.
def addLink( self, source, dest ): def addLink( self, source, dest ):
"Add link to model." "Add link to model."
source.links[ dest ] = self.link source.links[ dest ] = self.link
dest.links[ source ] = self.link dest.links[ source ] = self.link
self.links[ self.link ] = ( source, dest ) self.links[ self.link ] = ( source, dest )
def deleteLink( self, link ): def deleteLink( self, link ):
"Delete link from model." "Delete link from model."
pair = self.links.get( link, None ) pair = self.links.get( link, None )
@@ -539,7 +558,7 @@ class MiniEdit( Frame ):
"Build network based on our topology." "Build network based on our topology."
net = Mininet( topo=None ) net = Mininet( topo=None )
# Make controller # Make controller
net.addController( 'c0' ) net.addController( 'c0' )
# Make nodes # Make nodes
@@ -559,24 +578,26 @@ class MiniEdit( Frame ):
srcName, dstName = src[ 'text' ], dst[ 'text' ] srcName, dstName = src[ 'text' ], dst[ 'text' ]
src, dst = net.nameToNode[ srcName ], net.nameToNode[ dstName ] src, dst = net.nameToNode[ srcName ], net.nameToNode[ dstName ]
src.linkTo( dst ) src.linkTo( dst )
# Build network (we have to do this separately at the moment ) # Build network (we have to do this separately at the moment )
net.build() net.build()
return net return net
def start( self ): def start( self ):
"Start network."
if self.net is None: if self.net is None:
self.net = self.build()
self.net.start() self.net.start()
def stop( self ): def stop( self ):
"Stop network."
if self.net is not None: if self.net is not None:
self.net.stop() self.net.stop()
cleanUpScreens() cleanUpScreens()
self.net = None self.net = None
def xterm( self, ignore=None ): def xterm( self, ignore=None ):
"Make an xterm when a button is pressed."
if ( self.selection is None or if ( self.selection is None or
self.net is None or self.net is None or
self.selection not in self.itemToWidget ): self.selection not in self.itemToWidget ):
@@ -584,83 +605,108 @@ class MiniEdit( Frame ):
name = self.itemToWidget[ self.selection ][ 'text' ] name = self.itemToWidget[ self.selection ][ 'text' ]
if name not in self.net.nameToNode: if name not in self.net.nameToNode:
return return
self.net.terms.append( makeTerm( self.net.nameToNode[ name ], 'Host' ) ) term = makeTerm( self.net.nameToNode[ name ], 'Host' )
self.net.terms.append( term )
# Image data. Git will be unhappy.
def createImages( self ):
"Initialize button/icon images." def miniEditImages():
images = {} "Create and return images for MiniEdit."
images[ 'Select' ] = BitmapImage( file='/usr/include/X11/bitmaps/left_ptr' ) # Image data. Git will be unhappy. However, the alternative
# is to keep track of separate binary files, which is also
images[ 'Host' ] = PhotoImage( data=r""" # unappealing.
R0lGODlhIAAYAPcAMf//////zP//mf//Zv//M///AP/M///MzP/Mmf/MZv/MM//MAP+Z//+Z
zP+Zmf+ZZv+ZM/+ZAP9m//9mzP9mmf9mZv9mM/9mAP8z//8zzP8zmf8zZv8zM/8zAP8A//8A
zP8Amf8AZv8AM/8AAMz//8z/zMz/mcz/Zsz/M8z/AMzM/8zMzMzMmczMZszMM8zMAMyZ/8yZ
zMyZmcyZZsyZM8yZAMxm/8xmzMxmmcxmZsxmM8xmAMwz/8wzzMwzmcwzZswzM8wzAMwA/8wA
zMwAmcwAZswAM8wAAJn//5n/zJn/mZn/Zpn/M5n/AJnM/5nMzJnMmZnMZpnMM5nMAJmZ/5mZ
zJmZmZmZZpmZM5mZAJlm/5lmzJlmmZlmZplmM5lmAJkz/5kzzJkzmZkzZpkzM5kzAJkA/5kA
zJkAmZkAZpkAM5kAAGb//2b/zGb/mWb/Zmb/M2b/AGbM/2bMzGbMmWbMZmbMM2bMAGaZ/2aZ
zGaZmWaZZmaZM2aZAGZm/2ZmzGZmmWZmZmZmM2ZmAGYz/2YzzGYzmWYzZmYzM2YzAGYA/2YA
zGYAmWYAZmYAM2YAADP//zP/zDP/mTP/ZjP/MzP/ADPM/zPMzDPMmTPMZjPMMzPMADOZ/zOZ
zDOZmTOZZjOZMzOZADNm/zNmzDNmmTNmZjNmMzNmADMz/zMzzDMzmTMzZjMzMzMzADMA/zMA
zDMAmTMAZjMAMzMAAAD//wD/zAD/mQD/ZgD/MwD/AADM/wDMzADMmQDMZgDMMwDMAACZ/wCZ
zACZmQCZZgCZMwCZAABm/wBmzABmmQBmZgBmMwBmAAAz/wAzzAAzmQAzZgAzMwAzAAAA/wAA
zAAAmQAAZgAAM+4AAN0AALsAAKoAAIgAAHcAAFUAAEQAACIAABEAAADuAADdAAC7AACqAACI
AAB3AABVAABEAAAiAAARAAAA7gAA3QAAuwAAqgAAiAAAdwAAVQAARAAAIgAAEe7u7t3d3bu7
u6qqqoiIiHd3d1VVVURERCIiIhEREQAAACH5BAEAAAAALAAAAAAgABgAAAiNAAH8G0iwoMGD
CAcKTMiw4UBwBPXVm0ixosWLFvVBHFjPoUeC9Tb+6/jRY0iQ/8iVbHiS40CVKxG2HEkQZsyC
M0mmvGkw50uePUV2tEnOZkyfQA8iTYpTKNOgKJ+C3AhOp9SWVaVOfWj1KdauTL9q5UgVbFKs
EjGqXVtP40NwcBnCjXtw7tx/C8cSBBAQADs=""" )
images[ 'Switch' ] = PhotoImage( data=r"""
R0lGODlhIAAYAPcAMf//////zP//mf//Zv//M///AP/M///MzP/Mmf/MZv/MM//MAP+Z//+Z
zP+Zmf+ZZv+ZM/+ZAP9m//9mzP9mmf9mZv9mM/9mAP8z//8zzP8zmf8zZv8zM/8zAP8A//8A
zP8Amf8AZv8AM/8AAMz//8z/zMz/mcz/Zsz/M8z/AMzM/8zMzMzMmczMZszMM8zMAMyZ/8yZ
zMyZmcyZZsyZM8yZAMxm/8xmzMxmmcxmZsxmM8xmAMwz/8wzzMwzmcwzZswzM8wzAMwA/8wA
zMwAmcwAZswAM8wAAJn//5n/zJn/mZn/Zpn/M5n/AJnM/5nMzJnMmZnMZpnMM5nMAJmZ/5mZ
zJmZmZmZZpmZM5mZAJlm/5lmzJlmmZlmZplmM5lmAJkz/5kzzJkzmZkzZpkzM5kzAJkA/5kA
zJkAmZkAZpkAM5kAAGb//2b/zGb/mWb/Zmb/M2b/AGbM/2bMzGbMmWbMZmbMM2bMAGaZ/2aZ
zGaZmWaZZmaZM2aZAGZm/2ZmzGZmmWZmZmZmM2ZmAGYz/2YzzGYzmWYzZmYzM2YzAGYA/2YA
zGYAmWYAZmYAM2YAADP//zP/zDP/mTP/ZjP/MzP/ADPM/zPMzDPMmTPMZjPMMzPMADOZ/zOZ
zDOZmTOZZjOZMzOZADNm/zNmzDNmmTNmZjNmMzNmADMz/zMzzDMzmTMzZjMzMzMzADMA/zMA
zDMAmTMAZjMAMzMAAAD//wD/zAD/mQD/ZgD/MwD/AADM/wDMzADMmQDMZgDMMwDMAACZ/wCZ
zACZmQCZZgCZMwCZAABm/wBmzABmmQBmZgBmMwBmAAAz/wAzzAAzmQAzZgAzMwAzAAAA/wAA
zAAAmQAAZgAAM+4AAN0AALsAAKoAAIgAAHcAAFUAAEQAACIAABEAAADuAADdAAC7AACqAACI
AAB3AABVAABEAAAiAAARAAAA7gAA3QAAuwAAqgAAiAAAdwAAVQAARAAAIgAAEe7u7t3d3bu7
u6qqqoiIiHd3d1VVVURERCIiIhEREQAAACH5BAEAAAAALAAAAAAgABgAAAhwAAEIHEiwoMGD
CBMqXMiwocOHECNKnEixosWB3zJq3Mixo0eNAL7xG0mypMmTKPl9Cznyn8uWL/m5/AeTpsyY
I1eKlBnO5r+eLYHy9Ck0J8ubPmPOrMmUpM6UUKMa/Ui16saLWLNq3cq1q9evYB0GBAA7
""" )
images[ 'Link' ] = PhotoImage( data=r"""
R0lGODlhFgAWAPcAMf//////zP//mf//Zv//M///AP/M///MzP/Mmf/MZv/MM//MAP+Z//+Z
zP+Zmf+ZZv+ZM/+ZAP9m//9mzP9mmf9mZv9mM/9mAP8z//8zzP8zmf8zZv8zM/8zAP8A//8A
zP8Amf8AZv8AM/8AAMz//8z/zMz/mcz/Zsz/M8z/AMzM/8zMzMzMmczMZszMM8zMAMyZ/8yZ
zMyZmcyZZsyZM8yZAMxm/8xmzMxmmcxmZsxmM8xmAMwz/8wzzMwzmcwzZswzM8wzAMwA/8wA
zMwAmcwAZswAM8wAAJn//5n/zJn/mZn/Zpn/M5n/AJnM/5nMzJnMmZnMZpnMM5nMAJmZ/5mZ
zJmZmZmZZpmZM5mZAJlm/5lmzJlmmZlmZplmM5lmAJkz/5kzzJkzmZkzZpkzM5kzAJkA/5kA
zJkAmZkAZpkAM5kAAGb//2b/zGb/mWb/Zmb/M2b/AGbM/2bMzGbMmWbMZmbMM2bMAGaZ/2aZ
zGaZmWaZZmaZM2aZAGZm/2ZmzGZmmWZmZmZmM2ZmAGYz/2YzzGYzmWYzZmYzM2YzAGYA/2YA
zGYAmWYAZmYAM2YAADP//zP/zDP/mTP/ZjP/MzP/ADPM/zPMzDPMmTPMZjPMMzPMADOZ/zOZ
zDOZmTOZZjOZMzOZADNm/zNmzDNmmTNmZjNmMzNmADMz/zMzzDMzmTMzZjMzMzMzADMA/zMA
zDMAmTMAZjMAMzMAAAD//wD/zAD/mQD/ZgD/MwD/AADM/wDMzADMmQDMZgDMMwDMAACZ/wCZ
zACZmQCZZgCZMwCZAABm/wBmzABmmQBmZgBmMwBmAAAz/wAzzAAzmQAzZgAzMwAzAAAA/wAA
zAAAmQAAZgAAM+4AAN0AALsAAKoAAIgAAHcAAFUAAEQAACIAABEAAADuAADdAAC7AACqAACI
AAB3AABVAABEAAAiAAARAAAA7gAA3QAAuwAAqgAAiAAAdwAAVQAARAAAIgAAEe7u7t3d3bu7
u6qqqoiIiHd3d1VVVURERCIiIhEREQAAACH5BAEAAAAALAAAAAAWABYAAAhIAAEIHEiwoEGB
rhIeXEgwoUKGCx0+hGhQoiuKBy1irChxY0GNHgeCDAlgZEiTHlFuVImRJUWXEGEylBmxI8mS
Nknm1Dnx5sCAADs=
""" )
return images
return {
'Select': BitmapImage(
file='/usr/include/X11/bitmaps/left_ptr' ),
'Host': PhotoImage( data=r"""
R0lGODlhIAAYAPcAMf//////zP//mf//Zv//M///AP/M///MzP/M
mf/MZv/MM//MAP+Z//+ZzP+Zmf+ZZv+ZM/+ZAP9m//9mzP9mmf9m
Zv9mM/9mAP8z//8zzP8zmf8zZv8zM/8zAP8A//8AzP8Amf8AZv8A
M/8AAMz//8z/zMz/mcz/Zsz/M8z/AMzM/8zMzMzMmczMZszMM8zM
AMyZ/8yZzMyZmcyZZsyZM8yZAMxm/8xmzMxmmcxmZsxmM8xmAMwz
/8wzzMwzmcwzZswzM8wzAMwA/8wAzMwAmcwAZswAM8wAAJn//5n/
zJn/mZn/Zpn/M5n/AJnM/5nMzJnMmZnMZpnMM5nMAJmZ/5mZzJmZ
mZmZZpmZM5mZAJlm/5lmzJlmmZlmZplmM5lmAJkz/5kzzJkzmZkz
ZpkzM5kzAJkA/5kAzJkAmZkAZpkAM5kAAGb//2b/zGb/mWb/Zmb/
M2b/AGbM/2bMzGbMmWbMZmbMM2bMAGaZ/2aZzGaZmWaZZmaZM2aZ
AGZm/2ZmzGZmmWZmZmZmM2ZmAGYz/2YzzGYzmWYzZmYzM2YzAGYA
/2YAzGYAmWYAZmYAM2YAADP//zP/zDP/mTP/ZjP/MzP/ADPM/zPM
zDPMmTPMZjPMMzPMADOZ/zOZzDOZmTOZZjOZMzOZADNm/zNmzDNm
mTNmZjNmMzNmADMz/zMzzDMzmTMzZjMzMzMzADMA/zMAzDMAmTMA
ZjMAMzMAAAD//wD/zAD/mQD/ZgD/MwD/AADM/wDMzADMmQDMZgDM
MwDMAACZ/wCZzACZmQCZZgCZMwCZAABm/wBmzABmmQBmZgBmMwBm
AAAz/wAzzAAzmQAzZgAzMwAzAAAA/wAAzAAAmQAAZgAAM+4AAN0A
ALsAAKoAAIgAAHcAAFUAAEQAACIAABEAAADuAADdAAC7AACqAACI
AAB3AABVAABEAAAiAAARAAAA7gAA3QAAuwAAqgAAiAAAdwAAVQAA
RAAAIgAAEe7u7t3d3bu7u6qqqoiIiHd3d1VVVURERCIiIhEREQAA
ACH5BAEAAAAALAAAAAAgABgAAAiNAAH8G0iwoMGDCAcKTMiw4UBw
BPXVm0ixosWLFvVBHFjPoUeC9Tb+6/jRY0iQ/8iVbHiS40CVKxG2
HEkQZsyCM0mmvGkw50uePUV2tEnOZkyfQA8iTYpTKNOgKJ+C3AhO
p9SWVaVOfWj1KdauTL9q5UgVbFKsEjGqXVtP40NwcBnCjXtw7tx/
C8cSBBAQADs=
""" ),
'Switch': PhotoImage( data=r"""
R0lGODlhIAAYAPcAMf//////zP//mf//Zv//M///AP/M///MzP/M
mf/MZv/MM//MAP+Z//+ZzP+Zmf+ZZv+ZM/+ZAP9m//9mzP9mmf9m
Zv9mM/9mAP8z//8zzP8zmf8zZv8zM/8zAP8A//8AzP8Amf8AZv8A
M/8AAMz//8z/zMz/mcz/Zsz/M8z/AMzM/8zMzMzMmczMZszMM8zM
AMyZ/8yZzMyZmcyZZsyZM8yZAMxm/8xmzMxmmcxmZsxmM8xmAMwz
/8wzzMwzmcwzZswzM8wzAMwA/8wAzMwAmcwAZswAM8wAAJn//5n/
zJn/mZn/Zpn/M5n/AJnM/5nMzJnMmZnMZpnMM5nMAJmZ/5mZzJmZ
mZmZZpmZM5mZAJlm/5lmzJlmmZlmZplmM5lmAJkz/5kzzJkzmZkz
ZpkzM5kzAJkA/5kAzJkAmZkAZpkAM5kAAGb//2b/zGb/mWb/Zmb/
M2b/AGbM/2bMzGbMmWbMZmbMM2bMAGaZ/2aZzGaZmWaZZmaZM2aZ
AGZm/2ZmzGZmmWZmZmZmM2ZmAGYz/2YzzGYzmWYzZmYzM2YzAGYA
/2YAzGYAmWYAZmYAM2YAADP//zP/zDP/mTP/ZjP/MzP/ADPM/zPM
zDPMmTPMZjPMMzPMADOZ/zOZzDOZmTOZZjOZMzOZADNm/zNmzDNm
mTNmZjNmMzNmADMz/zMzzDMzmTMzZjMzMzMzADMA/zMAzDMAmTMA
ZjMAMzMAAAD//wD/zAD/mQD/ZgD/MwD/AADM/wDMzADMmQDMZgDM
MwDMAACZ/wCZzACZmQCZZgCZMwCZAABm/wBmzABmmQBmZgBmMwBm
AAAz/wAzzAAzmQAzZgAzMwAzAAAA/wAAzAAAmQAAZgAAM+4AAN0A
ALsAAKoAAIgAAHcAAFUAAEQAACIAABEAAADuAADdAAC7AACqAACI
AAB3AABVAABEAAAiAAARAAAA7gAA3QAAuwAAqgAAiAAAdwAAVQAA
RAAAIgAAEe7u7t3d3bu7u6qqqoiIiHd3d1VVVURERCIiIhEREQAA
ACH5BAEAAAAALAAAAAAgABgAAAhwAAEIHEiwoMGDCBMqXMiwocOH
ECNKnEixosWB3zJq3Mixo0eNAL7xG0mypMmTKPl9Cznyn8uWL/m5
/AeTpsyYI1eKlBnO5r+eLYHy9Ck0J8ubPmPOrMmUpM6UUKMa/Ui1
6saLWLNq3cq1q9evYB0GBAA7
""" ),
'Link': PhotoImage( data=r"""
R0lGODlhFgAWAPcAMf//////zP//mf//Zv//M///AP/M///MzP/M
mf/MZv/MM//MAP+Z//+ZzP+Zmf+ZZv+ZM/+ZAP9m//9mzP9mmf9m
Zv9mM/9mAP8z//8zzP8zmf8zZv8zM/8zAP8A//8AzP8Amf8AZv8A
M/8AAMz//8z/zMz/mcz/Zsz/M8z/AMzM/8zMzMzMmczMZszMM8zM
AMyZ/8yZzMyZmcyZZsyZM8yZAMxm/8xmzMxmmcxmZsxmM8xmAMwz
/8wzzMwzmcwzZswzM8wzAMwA/8wAzMwAmcwAZswAM8wAAJn//5n/
zJn/mZn/Zpn/M5n/AJnM/5nMzJnMmZnMZpnMM5nMAJmZ/5mZzJmZ
mZmZZpmZM5mZAJlm/5lmzJlmmZlmZplmM5lmAJkz/5kzzJkzmZkz
ZpkzM5kzAJkA/5kAzJkAmZkAZpkAM5kAAGb//2b/zGb/mWb/Zmb/
M2b/AGbM/2bMzGbMmWbMZmbMM2bMAGaZ/2aZzGaZmWaZZmaZM2aZ
AGZm/2ZmzGZmmWZmZmZmM2ZmAGYz/2YzzGYzmWYzZmYzM2YzAGYA
/2YAzGYAmWYAZmYAM2YAADP//zP/zDP/mTP/ZjP/MzP/ADPM/zPM
zDPMmTPMZjPMMzPMADOZ/zOZzDOZmTOZZjOZMzOZADNm/zNmzDNm
mTNmZjNmMzNmADMz/zMzzDMzmTMzZjMzMzMzADMA/zMAzDMAmTMA
ZjMAMzMAAAD//wD/zAD/mQD/ZgD/MwD/AADM/wDMzADMmQDMZgDM
MwDMAACZ/wCZzACZmQCZZgCZMwCZAABm/wBmzABmmQBmZgBmMwBm
AAAz/wAzzAAzmQAzZgAzMwAzAAAA/wAAzAAAmQAAZgAAM+4AAN0A
ALsAAKoAAIgAAHcAAFUAAEQAACIAABEAAADuAADdAAC7AACqAACI
AAB3AABVAABEAAAiAAARAAAA7gAA3QAAuwAAqgAAiAAAdwAAVQAA
RAAAIgAAEe7u7t3d3bu7u6qqqoiIiHd3d1VVVURERCIiIhEREQAA
ACH5BAEAAAAALAAAAAAWABYAAAhIAAEIHEiwoEGBrhIeXEgwoUKG
Cx0+hGhQoiuKBy1irChxY0GNHgeCDAlgZEiTHlFuVImRJUWXEGEy
lBmxI8mSNknm1Dnx5sCAADs=
""" )
}
if __name__ == '__main__': if __name__ == '__main__':
setLogLevel( 'info' ) setLogLevel( 'info' )
app = MiniEdit() app = MiniEdit()
app.mainloop() app.mainloop()
+66 -68
View File
@@ -20,17 +20,17 @@ from mininet.topolib import TreeTopo
from mininet.util import quietRun from mininet.util import quietRun
# bwtest support # bwtest support
class Graph( Frame ): class Graph( Frame ):
"Graph that we can add bars to over time." "Graph that we can add bars to over time."
def __init__( self, master=None, def __init__( self, master=None,
bg = 'white', bg = 'white',
gheight=200, gwidth=500, gheight=200, gwidth=500,
barwidth=10, barwidth=10,
ymax=3.5,): ymax=3.5,):
Frame.__init__( self, master ) Frame.__init__( self, master )
self.bg = bg self.bg = bg
@@ -41,12 +41,11 @@ class Graph( Frame ):
self.xpos = 0 self.xpos = 0
# Create everything # Create everything
self.title = self.graph = None self.title, self.graph, self.scale = self.createWidgets()
self.createWidgets()
self.updateScrollRegions() self.updateScrollRegions()
self.yview( 'moveto', '1.0' ) self.yview( 'moveto', '1.0' )
def scale( self ): def createScale( self ):
"Create a and return a new canvas with scale markers." "Create a and return a new canvas with scale markers."
height = float( self.gheight ) height = float( self.gheight )
width = 25 width = 25
@@ -60,22 +59,22 @@ class Graph( Frame ):
ypos = height * ( 1 - float( y ) / ymax ) ypos = height * ( 1 - float( y ) / ymax )
scale.create_line( width, ypos, width - 10, ypos, fill=fill ) scale.create_line( width, ypos, width - 10, ypos, fill=fill )
scale.create_text( 10, ypos, text=str( y ), fill=fill ) scale.create_text( 10, ypos, text=str( y ), fill=fill )
return scale return scale
def updateScrollRegions( self ): def updateScrollRegions( self ):
"Update graph and scale scroll regions." "Update graph and scale scroll regions."
ofs = 20 ofs = 20
height = self.gheight + ofs height = self.gheight + ofs
self.graph.configure( scrollregion=( 0, -ofs, self.graph.configure( scrollregion=( 0, -ofs,
self.xpos * self.barwidth, height ) ) self.xpos * self.barwidth, height ) )
self.scale.configure( scrollregion=( 0, -ofs, 0, height ) ) self.scale.configure( scrollregion=( 0, -ofs, 0, height ) )
def yview( self, *args ): def yview( self, *args ):
"Scroll both scale and graph." "Scroll both scale and graph."
self.graph.yview( *args ) self.graph.yview( *args )
self.scale.yview( *args ) self.scale.yview( *args )
def createWidgets( self ): def createWidgets( self ):
"Create initial widget set." "Create initial widget set."
@@ -83,14 +82,14 @@ class Graph( Frame ):
title = Label( self, text="Bandwidth (Mb/s)", bg=self.bg ) title = Label( self, text="Bandwidth (Mb/s)", bg=self.bg )
width = self.gwidth width = self.gwidth
height = self.gheight height = self.gheight
scale = self.scale() scale = self.createScale()
graph = Canvas( self, width=width, height=height, background=self.bg) graph = Canvas( self, width=width, height=height, background=self.bg)
xbar = Scrollbar( self, orient='horizontal', command=graph.xview ) xbar = Scrollbar( self, orient='horizontal', command=graph.xview )
ybar = Scrollbar( self, orient='vertical', command=self.yview ) ybar = Scrollbar( self, orient='vertical', command=self.yview )
graph.configure( xscrollcommand=xbar.set, yscrollcommand=ybar.set, graph.configure( xscrollcommand=xbar.set, yscrollcommand=ybar.set,
scrollregion=(0, 0, width, height ) ) scrollregion=(0, 0, width, height ) )
scale.configure( yscrollcommand=ybar.set ) scale.configure( yscrollcommand=ybar.set )
# Layout # Layout
title.grid( row=0, columnspan=3, sticky='new') title.grid( row=0, columnspan=3, sticky='new')
scale.grid( row=1, column=0, sticky='nsew' ) scale.grid( row=1, column=0, sticky='nsew' )
@@ -99,13 +98,8 @@ class Graph( Frame ):
xbar.grid( row=2, column=0, columnspan=2, sticky='ew' ) xbar.grid( row=2, column=0, columnspan=2, sticky='ew' )
self.rowconfigure( 1, weight=1 ) self.rowconfigure( 1, weight=1 )
self.columnconfigure( 1, weight=1 ) self.columnconfigure( 1, weight=1 )
return title, graph, scale
# Save for future reference
self.title = title
self.scale = scale
self.graph = graph
return graph
def addBar( self, yval ): def addBar( self, yval ):
"Add a new bar to our graph." "Add a new bar to our graph."
percent = yval / self.ymax percent = yval / self.ymax
@@ -123,7 +117,7 @@ class Graph( Frame ):
"Add a bar for testing purposes." "Add a bar for testing purposes."
ms = 1000 ms = 1000
if self.xpos < 10: if self.xpos < 10:
self.addBar( self.xpos/10 * self.ymax ) self.addBar( self.xpos / 10 * self.ymax )
self.after( ms, self.test ) self.after( ms, self.test )
def setTitle( self, text ): def setTitle( self, text ):
@@ -134,52 +128,49 @@ class Graph( Frame ):
class Controls( Frame ): class Controls( Frame ):
"Handy controls for configuring test." "Handy controls for configuring test."
switches = { switches = {
'Kernel Switch': KernelSwitch, 'Kernel Switch': KernelSwitch,
'User Switch': UserSwitch, 'User Switch': UserSwitch,
'Open vSwitch': OVSKernelSwitch 'Open vSwitch': OVSKernelSwitch
} }
controllers = { controllers = {
'Reference Controller': Controller, 'Reference Controller': Controller,
'NOX': NOX 'NOX': NOX
} }
def optionMenu( self, name, dict, initval, opts ):
"Add a new option menu."
var = StringVar()
var.set( findKey( dict, initval ) )
menu = OptionMenu( self, var, *dict )
menu.config( **opts )
menu.pack( fill='x' )
def value():
return dict[ var.get() ]
return value
def __init__( self, master, start, stop, quit ): def __init__( self, master, startFn, stopFn, quitFn ):
Frame.__init__( self, master ) Frame.__init__( self, master )
# Option menus # Option menus
opts = { 'font': 'Geneva 7 bold' } opts = { 'font': 'Geneva 7 bold' }
self.switch = self.optionMenu( 'Switch', self.switches, self.switch = self.optionMenu( self.switches,
KernelSwitch, opts ) KernelSwitch, opts )
self.controller = self.optionMenu( 'Controller', self.controllers, self.controller = self.optionMenu( self.controllers,
Controller, opts) Controller, opts)
# Spacer # Spacer
pk = { 'fill': 'x' } pk = { 'fill': 'x' }
Label( self, **opts ).pack( **pk ) Label( self, **opts ).pack( **pk )
# Buttons # Buttons
self.start = Button( self, text='Start', command=start, **opts ) self.start = Button( self, text='Start', command=startFn, **opts )
self.stop = Button( self, text='Stop', command=stop, **opts ) self.stop = Button( self, text='Stop', command=stopFn, **opts )
self.quit = Button( self, text='Quit', command=quit, **opts ) self.quit = Button( self, text='Quit', command=quitFn, **opts )
for button in ( self.start, self.stop, self.quit ): for button in ( self.start, self.stop, self.quit ):
button.pack( **pk ) button.pack( **pk )
def optionMenu( self, menuItems, initval, opts ):
"Add a new option menu. Returns function to get value."
var = StringVar()
var.set( findKey( menuItems, initval ) )
menu = OptionMenu( self, var, *menuItems )
menu.config( **opts )
menu.pack( fill='x' )
return lambda: menuItems[ var.get() ]
def parsebwtest( line, def parsebwtest( line,
r=re.compile( r'(\d+) s: in ([\d\.]+) MB/s, out ([\d\.]+) MB/s' ) ): r=re.compile( r'(\d+) s: in ([\d\.]+) MB/s, out ([\d\.]+) MB/s' ) ):
"Parse udpbwtest.c output, returning seconds, inbw, outbw." "Parse udpbwtest.c output, returning seconds, inbw, outbw."
@@ -193,34 +184,41 @@ def parsebwtest( line,
class UdpBwTest( Frame ): class UdpBwTest( Frame ):
"Test and plot UDP bandwidth over time" "Test and plot UDP bandwidth over time"
def __init__( self, topo, seconds=60, master=None ): def __init__( self, topo, master=None ):
"Start up and monitor udpbwtest on each of our hosts." "Start up and monitor udpbwtest on each of our hosts."
Frame.__init__( self, master ) Frame.__init__( self, master )
self.controls = Controls( self, self.start, self.stop, self.quit ) self.controls = Controls( self, self.start, self.stop, self.quit )
self.graph = Graph( self ) self.graph = Graph( self )
# Layout # Layout
self.controls.pack( side='left', expand=False, fill='y' ) self.controls.pack( side='left', expand=False, fill='y' )
self.graph.pack( side='right', expand=True, fill='both' ) self.graph.pack( side='right', expand=True, fill='both' )
self.pack( expand=True, fill='both' ) self.pack( expand=True, fill='both' )
self.topo = topo
self.net = None
self.hosts = []
self.hostCount = 0
self.output = None
self.results = {}
self.running = False self.running = False
def start( self ): def start( self ):
print "start" "Start test."
if self.running: if self.running:
return return
switch = self.controls.switch() switch = self.controls.switch()
controller = self.controls.controller() controller = self.controls.controller()
self.net = Mininet( topo, switch=switch, controller=controller ) self.net = Mininet( self.topo, switch=switch,
controller=controller )
self.hosts = self.net.hosts self.hosts = self.net.hosts
self.hostCount = len( self.hosts ) self.hostCount = len( self.hosts )
print "*** Starting network" print "*** Starting network"
self.net.start() self.net.start()
@@ -229,7 +227,7 @@ class UdpBwTest( Frame ):
for host in hosts: for host in hosts:
ips = [ h.IP() for h in hosts if h != host ] ips = [ h.IP() for h in hosts if h != host ]
host.cmdPrint( './udpbwtest ' + ' '.join( ips ) + ' &' ) host.cmdPrint( './udpbwtest ' + ' '.join( ips ) + ' &' )
print "*** Monitoring hosts" print "*** Monitoring hosts"
self.output = self.net.monitor( hosts, timeoutms=1 ) self.output = self.net.monitor( hosts, timeoutms=1 )
self.results = {} self.results = {}
@@ -238,12 +236,12 @@ class UdpBwTest( Frame ):
# Pylint isn't smart enough to understand iterator.next() # Pylint isn't smart enough to understand iterator.next()
# pylint: disable-msg=E1101 # pylint: disable-msg=E1101
def updateGraph( self ): def updateGraph( self ):
"Graph input bandwidth." "Graph input bandwidth."
print "updateGraph" print "updateGraph"
if not self.running: if not self.running:
return return
@@ -269,20 +267,20 @@ class UdpBwTest( Frame ):
def stop( self ): def stop( self ):
"Stop test." "Stop test."
print "*** Stopping udpbwtest processes" print "*** Stopping udpbwtest processes"
# We *really* don't want these things hanging around! # We *really* don't want these things hanging around!
quietRun( 'killall -9 udpbwtest' ) quietRun( 'killall -9 udpbwtest' )
if not self.running: if not self.running:
return return
print "*** Stopping network" print "*** Stopping network"
self.running = False self.running = False
self.net.stop() self.net.stop()
def quit( self ): def quit( self ):
print "*** Quitting" "Quit app."
self.stop() self.stop()
Frame.quit( self ) Frame.quit( self )
@@ -290,9 +288,9 @@ class UdpBwTest( Frame ):
# Useful utilities # Useful utilities
def findKey( dict, value ): def findKey( d, value ):
"Find some key where dict[ key ] == value." "Find some key where d[ key ] == value."
return [ key for key, val in dict.items() if val == value ][ 0 ] return [ key for key, val in d.items() if val == value ][ 0 ]
def assign( obj, **kwargs): def assign( obj, **kwargs):
"Set a bunch of fields in an object." "Set a bunch of fields in an object."
@@ -306,7 +304,7 @@ class Object( object ):
if __name__ == '__main__': if __name__ == '__main__':
setLogLevel( 'info' ) setLogLevel( 'info' )
topo = TreeTopo( depth=1, fanout=2 ) app = UdpBwTest( topo=TreeTopo( depth=2, fanout=2 ) )
app = UdpBwTest( topo )
app.mainloop() app.mainloop()
+1 -1
View File
@@ -23,7 +23,7 @@ def sh( cmd ):
def cleanup(): def cleanup():
"""Clean up junk which might be left over from old runs; """Clean up junk which might be left over from old runs;
do fast stuff before slow dp and link removal!""" do fast stuff before slow dp and link removal!"""
info("*** Removing excess controllers/ofprotocols/ofdatapaths/pings/noxes" info("*** Removing excess controllers/ofprotocols/ofdatapaths/pings/noxes"
"\n") "\n")
zombies = 'controller ofprotocol ofdatapath ping nox_core lt-nox_core ' zombies = 'controller ofprotocol ofdatapath ping nox_core lt-nox_core '
+23 -17
View File
@@ -29,18 +29,18 @@ from subprocess import call
from cmd import Cmd from cmd import Cmd
from os import isatty from os import isatty
from select import poll, POLLIN from select import poll, POLLIN
from sys import stdin import sys
from mininet.log import info, output, error from mininet.log import info, output, error
from mininet.term import makeTerms from mininet.term import makeTerms
from mininet.util import quietRun, isShellBuiltin from mininet.util import quietRun, isShellBuiltin
class CLI( Cmd ): class CLI( Cmd ):
"Simple command-line interface to talk to nodes." "Simple command-line interface to talk to nodes."
prompt = 'mininet> ' prompt = 'mininet> '
def __init__( self, mininet, stdin=stdin ): def __init__( self, mininet, stdin=sys.stdin ):
self.mn = mininet self.mn = mininet
self.nodelist = self.mn.controllers + self.mn.switches + self.mn.hosts self.nodelist = self.mn.controllers + self.mn.switches + self.mn.hosts
self.nodemap = {} # map names to Node objects self.nodemap = {} # map names to Node objects
@@ -75,7 +75,7 @@ class CLI( Cmd ):
# must have the same interface # must have the same interface
# pylint: disable-msg=W0613,R0201 # pylint: disable-msg=W0613,R0201
helpStr = ( helpStr = (
'You may also send a command to a node using:\n' 'You may also send a command to a node using:\n'
' <node> command {args}\n' ' <node> command {args}\n'
'For example:\n' 'For example:\n'
@@ -110,7 +110,9 @@ class CLI( Cmd ):
for switch in self.mn.switches: for switch in self.mn.switches:
output( switch.name, '<->' ) output( switch.name, '<->' )
for intf in switch.intfs.values(): for intf in switch.intfs.values():
node, name = switch.connection.get( intf, ( None, 'Unknown ' ) ) # Ugly, but pylint wants it
name = switch.connection.get( intf,
( None, 'Unknown ' ) )[ 1 ]
output( ' %s' % name ) output( ' %s' % name )
output( '\n' ) output( '\n' )
@@ -209,7 +211,7 @@ class CLI( Cmd ):
def isatty( self ): def isatty( self ):
"Is our standard input a tty?" "Is our standard input a tty?"
return isatty( self.stdin.fileno() ) return isatty( self.stdin.fileno() )
def do_noecho( self, line ): def do_noecho( self, line ):
"Run an interactive command with echoing turned off." "Run an interactive command with echoing turned off."
if self.isatty(): if self.isatty():
@@ -236,20 +238,16 @@ class CLI( Cmd ):
for arg in rest ] for arg in rest ]
rest = ' '.join( rest ) rest = ' '.join( rest )
# Run cmd on node: # Run cmd on node:
node.sendCmd( rest ) builtin = isShellBuiltin( first )
self.waitForNode( node, isShellBuiltin( first ) ) print "builtin =", builtin
node.sendCmd( rest, printPid=( not builtin ) )
self.waitForNode( node )
else: else:
error( '*** Unknown command: %s\n' % first ) error( '*** Unknown command: %s\n' % first )
# pylint: enable-msg=W0613,R0201 # pylint: enable-msg=W0613,R0201
def isReadable( self, poller ): def waitForNode( self, node ):
"Check whether a single polled object is readable."
for fd, mask in poller.poll( 0 ):
if mask & POLLIN:
return True
def waitForNode( self, node, isShellBuiltin=False ):
"Wait for a node to finish, and print its output." "Wait for a node to finish, and print its output."
# Pollers # Pollers
nodePoller = poll() nodePoller = poll()
@@ -264,10 +262,10 @@ class CLI( Cmd ):
while True: while True:
try: try:
bothPoller.poll() bothPoller.poll()
if self.isReadable( self.inPoller ): if isReadable( self.inPoller ):
key = self.stdin.read( 1 ) key = self.stdin.read( 1 )
node.write( key ) node.write( key )
if self.isReadable( nodePoller ): if isReadable( nodePoller ):
data = node.monitor() data = node.monitor()
output( data ) output( data )
if not node.waiting: if not node.waiting:
@@ -275,3 +273,11 @@ class CLI( Cmd ):
except KeyboardInterrupt: except KeyboardInterrupt:
node.sendInt() node.sendInt()
# Helper functions
def isReadable( poller ):
"Check whether a Poll object has a readable fd."
for fdmask in poller.poll( 0 ):
mask = fdmask[ 1 ]
if mask & POLLIN:
return True
+28 -24
View File
@@ -100,26 +100,6 @@ from mininet.util import quietRun, fixLimits
from mininet.util import createLink, macColonHex, ipStr, ipParse from mininet.util import createLink, macColonHex, ipStr, ipParse
from mininet.term import cleanUpScreens, makeTerms from mininet.term import cleanUpScreens, makeTerms
DATAPATHS = [ 'kernel' ] # [ 'user', 'kernel' ]
def init():
"Initialize Mininet."
if init.inited:
return
if os.getuid() != 0:
# Note: this script must be run as root
# Perhaps we should do so automatically!
print "*** Mininet must run as root."
exit( 1 )
# If which produces no output, then mnexec is not in the path.
# May want to loosen this to handle mnexec in the current dir.
if not quietRun( 'which mnexec' ):
raise Exception( "Could not find mnexec - check $PATH" )
fixLimits()
init.inited = False
class Mininet( object ): class Mininet( object ):
"Network emulation with hosts spawned in network namespaces." "Network emulation with hosts spawned in network namespaces."
@@ -167,7 +147,7 @@ class Mininet( object ):
if topo and build: if topo and build:
self.build() self.build()
def addHost( self, name, mac=None, ip=None ): def addHost( self, name, mac=None, ip=None ):
"""Add host. """Add host.
name: name of host to add name: name of host to add
@@ -195,13 +175,13 @@ class Mininet( object ):
self.nameToNode[ name ] = sw self.nameToNode[ name ] = sw
return sw return sw
def addController( self, controller ): def addController( self, name='c0', **kwargs ):
"""Add controller. """Add controller.
controller: Controller class""" controller: Controller class"""
controller_new = self.controller( 'c0' ) controller_new = self.controller( name, **kwargs )
if controller_new: # allow controller-less setups if controller_new: # allow controller-less setups
self.controllers.append( controller_new ) self.controllers.append( controller_new )
self.nameToNode[ 'c0' ] = controller_new self.nameToNode[ name ] = controller_new
# Control network support: # Control network support:
# #
@@ -558,3 +538,27 @@ class Mininet( object ):
result = CLI( self ) result = CLI( self )
self.stop() self.stop()
return result return result
# pylint thinks inited is unused
# pylint: disable-msg=W0612
def init():
"Initialize Mininet."
if init.inited:
return
if os.getuid() != 0:
# Note: this script must be run as root
# Perhaps we should do so automatically!
print "*** Mininet must run as root."
exit( 1 )
# If which produces no output, then mnexec is not in the path.
# May want to loosen this to handle mnexec in the current dir.
if not quietRun( 'which mnexec' ):
raise Exception( "Could not find mnexec - check $PATH" )
fixLimits()
init.inited = True
init.inited = False
# pylint: enable-msg=W0612
+8 -14
View File
@@ -95,6 +95,8 @@ class Node( object ):
self.lastPid = None self.lastPid = None
self.readbuf = '' self.readbuf = ''
self.waiting = False self.waiting = False
# Stash additional information as desired
self.args = kwargs
@classmethod @classmethod
def fdToNode( cls, fd ): def fdToNode( cls, fd ):
@@ -186,7 +188,7 @@ class Node( object ):
if self.lastPid: if self.lastPid:
try: try:
os.kill( self.lastPid, sig ) os.kill( self.lastPid, sig )
except Exception: except OSError:
pass pass
def monitor( self, timeoutms=None ): def monitor( self, timeoutms=None ):
@@ -301,7 +303,7 @@ class Node( object ):
if port1 is None: if port1 is None:
port1 = node1.newPort() port1 = node1.newPort()
if port2 is None: if port2 is None:
port2 = node2.newPort() port2 = node2.newPort()
intf1 = node1.intfName( port1 ) intf1 = node1.intfName( port1 )
intf2 = node2.intfName( port2 ) intf2 = node2.intfName( port2 )
makeIntfPair( intf1, intf2 ) makeIntfPair( intf1, intf2 )
@@ -410,14 +412,6 @@ class Switch( Node ):
error( '*** Error: %s has execed and cannot accept commands' % error( '*** Error: %s has execed and cannot accept commands' %
self.name ) self.name )
def monitor( self, *args, **kwargs ):
"Monitor a switch."
if not self.execed:
return Node.monitor( self, *args, **kwargs )
else:
return True, ''
class UserSwitch( Switch ): class UserSwitch( Switch ):
"User-space switch." "User-space switch."
@@ -458,7 +452,7 @@ class UserSwitch( Switch ):
class KernelSwitch( Switch ): class KernelSwitch( Switch ):
"""Kernel-space switch. """Kernel-space switch.
Currently only works in root namespace.""" Currently only works in root namespace."""
def __init__( self, name, dp=None, **kwargs ): def __init__( self, name, dp=None, **kwargs ):
"""Init. """Init.
name: name for switch name: name for switch
@@ -496,7 +490,7 @@ class KernelSwitch( Switch ):
# Run protocol daemon # Run protocol daemon
controller = controllers[ 0 ] controller = controllers[ 0 ]
self.cmd( 'ofprotocol ' + self.dp + self.cmd( 'ofprotocol ' + self.dp +
' tcp:%s:%d' % ( controller.IP(), controller.port ) + ' tcp:%s:%d' % ( controller.IP(), controller.port ) +
' --fail=closed ' + self.opts + ' --fail=closed ' + self.opts +
' 1> ' + ofplog + ' 2>' + ofplog + ' &' ) ' 1> ' + ofplog + ' 2>' + ofplog + ' &' )
self.execed = False self.execed = False
@@ -550,8 +544,8 @@ class OVSKernelSwitch( Switch ):
# Run protocol daemon # Run protocol daemon
controller = controllers[ 0 ] controller = controllers[ 0 ]
self.cmd( 'ovs-openflowd ' + self.dp + self.cmd( 'ovs-openflowd ' + self.dp +
' tcp:%s:%i' % ( controller.IP(), controller.port ) + ' tcp:%s:%i' % ( controller.IP(), controller.port ) +
' --fail=closed ' + self.opts + ' --fail=closed ' + self.opts +
' 1>' + ofplog + ' 2>' + ofplog + '&' ) ' 1>' + ofplog + ' 2>' + ofplog + '&' )
self.execed = False self.execed = False
+3 -3
View File
@@ -15,7 +15,7 @@ from mininet.util import quietRun
def quoteArg( arg ): def quoteArg( arg ):
"Quote an argument if it contains spaces." "Quote an argument if it contains spaces."
return repr( arg ) if ' ' in arg else arg return repr( arg ) if ' ' in arg else arg
def makeTerm( node, title='Node', term='xterm' ): def makeTerm( node, title='Node', term='xterm' ):
"""Run screen on a node, and hook up a terminal. """Run screen on a node, and hook up a terminal.
node: Node object node: Node object
@@ -38,9 +38,9 @@ def makeTerm( node, title='Node', term='xterm' ):
else: else:
args = [ 'sh', '-c', 'exec tail -f /tmp/' + node.name + '*.log' ] args = [ 'sh', '-c', 'exec tail -f /tmp/' + node.name + '*.log' ]
if term == 'gterm': if term == 'gterm':
# Compress these for gnome-terminal, which expects one token # Compress these for gnome-terminal, which expects one token
# to follow the -e option # to follow the -e option
args = [ ' '.join( [ quoteArg( arg ) for arg in args ] ) ] args = [ ' '.join( [ quoteArg( arg ) for arg in args ] ) ]
return Popen( cmds[ term ] + args ) return Popen( cmds[ term ] + args )
def cleanUpScreens(): def cleanUpScreens():
+33 -21
View File
@@ -5,7 +5,9 @@ from resource import setrlimit, RLIMIT_NPROC, RLIMIT_NOFILE
import select import select
from subprocess import call, check_call, Popen, PIPE, STDOUT from subprocess import call, check_call, Popen, PIPE, STDOUT
from mininet.log import lg from mininet.log import error
# Command execution support
def run( cmd ): def run( cmd ):
"""Simple interface to subprocess.call() """Simple interface to subprocess.call()
@@ -17,13 +19,16 @@ def checkRun( cmd ):
cmd: list of command params""" cmd: list of command params"""
return check_call( cmd.split( ' ' ) ) return check_call( cmd.split( ' ' ) )
# pylint doesn't understand explicit type checking
# pylint: disable-msg=E1103
def quietRun( *cmd ): def quietRun( *cmd ):
"""Run a command, routing stderr to stdout, and return the output. """Run a command, routing stderr to stdout, and return the output.
cmd: list of command params""" cmd: list of command params"""
if len( cmd ) == 1: if len( cmd ) == 1:
cmd = cmd[ 0 ] cmd = cmd[ 0 ]
if isinstance( cmd, str ): if isinstance( cmd, str ):
cmd = cmd.split( ' ' ) cmd = cmd.split( ' ' )
popen = Popen( cmd, stdout=PIPE, stderr=STDOUT ) popen = Popen( cmd, stdout=PIPE, stderr=STDOUT )
# We can't use Popen.communicate() because it uses # We can't use Popen.communicate() because it uses
# select(), which can't handle # select(), which can't handle
@@ -42,6 +47,22 @@ def quietRun( *cmd ):
break break
return output return output
# pylint: enable-msg=E1103
# pylint: disable-msg=E1101,W0612
def isShellBuiltin( cmd ):
"Return True if cmd is a bash builtin."
if isShellBuiltin.builtIns is None:
isShellBuiltin.builtIns = quietRun( 'bash -c enable' )
space = cmd.find( ' ' )
if space > 0:
cmd = cmd[ :space]
return cmd in isShellBuiltin.builtIns
isShellBuiltin.builtIns = None
# pylint: enable-msg=E1101,W0612
# Interface management # Interface management
# #
# Interfaces are managed as strings which are simply the # Interfaces are managed as strings which are simply the
@@ -78,7 +99,7 @@ def retry( retries, delaySecs, fn, *args, **keywords ):
sleep( delaySecs ) sleep( delaySecs )
tries += 1 tries += 1
if tries >= retries: if tries >= retries:
lg.error( "*** gave up after %i retries\n" % tries ) error( "*** gave up after %i retries\n" % tries )
exit( 1 ) exit( 1 )
def moveIntfNoRetry( intf, node, printError=False ): def moveIntfNoRetry( intf, node, printError=False ):
@@ -91,7 +112,7 @@ def moveIntfNoRetry( intf, node, printError=False ):
links = node.cmd( 'ip link show' ) links = node.cmd( 'ip link show' )
if not ( ' %s:' % intf ) in links: if not ( ' %s:' % intf ) in links:
if printError: if printError:
lg.error( '*** Error: moveIntf: ' + intf + error( '*** Error: moveIntf: ' + intf +
' not successfully moved to ' + node.name + '\n' ) ' not successfully moved to ' + node.name + '\n' )
return False return False
return True return True
@@ -112,10 +133,8 @@ def createLink( node1, node2, port1=None, port2=None ):
returns: intf1 name, intf2 name""" returns: intf1 name, intf2 name"""
return node1.linkTo( node2, port1, port2 ) return node1.linkTo( node2, port1, port2 )
def fixLimits():
"Fix ridiculously small resource limits." # IP and Mac address formatting and parsing
setrlimit( RLIMIT_NPROC, ( 4096, 8192 ) )
setrlimit( RLIMIT_NOFILE, ( 16384, 32768 ) )
def _colonHex( val, bytes ): def _colonHex( val, bytes ):
"""Generate colon-hex string. """Generate colon-hex string.
@@ -181,17 +200,10 @@ def makeNumeric( s ):
else: else:
return s return s
# pylint: disable-msg=E1101,W0612
def isShellBuiltin( cmd ): # Other stuff we use
"Return True if cmd is a bash builtin."
if isShellBuiltin.builtIns is None:
isShellBuiltin.builtIns = quietRun( 'bash -c enable' )
space = cmd.find( ' ' )
if space > 0:
cmd = cmd[ :space]
return cmd in isShellBuiltin.builtIns
isShellBuiltin.builtIns = None def fixLimits():
"Fix ridiculously small resource limits."
# pylint: enable-msg=E1101,W0612 setrlimit( RLIMIT_NPROC, ( 4096, 8192 ) )
setrlimit( RLIMIT_NOFILE, ( 16384, 32768 ) )