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
PYSRC = $(MININET) $(TEST) $(EXAMPLES) $(BIN)
P8IGN = E251,E201,E302
P8IGN = E251,E201,E302,E202
codecheck: $(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
to its console window
- Second, each time a console window gets iperf output, it is parsed
and accumulated. Once we have output for all consoles, a bar is
added to the bandwidth graph.
@@ -36,7 +36,7 @@ from mininet.util import quietRun
class Console( Frame ):
"A simple console on a host."
def __init__( self, parent, net, node, height=10, width=32, title='Node' ):
Frame.__init__( self, parent )
@@ -44,10 +44,10 @@ class Console( Frame ):
self.node = node
self.prompt = node.name + '# '
self.height, self.width, self.title = height, width, title
# Initialize widget styles
self.buttonStyle = { 'font': 'Monaco 7' }
self.textStyle = {
self.textStyle = {
'font': 'Monaco 7',
'bg': 'black',
'fg': 'green',
@@ -58,22 +58,22 @@ class Console( Frame ):
'highlightcolor': 'green',
'selectforeground': 'black',
'selectbackground': 'green'
}
}
# Set up widgets
self.text = self.makeWidgets( )
self.bindEvents()
self.sendCmd( 'export TERM=dumb' )
self.outputHook = None
def makeWidgets( self ):
"Make a label, a text area, and a scroll bar."
def newTerm( net=self.net, node=self.node, title=self.title ):
"Pop up a new terminal window for a node."
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 )
label.pack( side='top', fill='x' )
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
# control characters other than [\b\n\r]
ignoreChars = re.compile( r'[\x00-\x07\x09\x0b\x0c\x0e-\x1f]+' )
def append( self, text ):
"Append something to our text frame."
text = self.ignoreChars.sub( '', text )
@@ -114,7 +114,7 @@ class Console( Frame ):
char = event.char
if self.node.waiting:
self.node.write( char )
def handleReturn( self, event ):
"Handle a carriage return."
cmd = self.text.get( 'insert linestart', 'insert lineend' )
@@ -127,24 +127,29 @@ class Console( Frame ):
if pos >= 0:
cmd = cmd[ pos + len( self.prompt ): ]
self.sendCmd( cmd )
# Callback ignores event
# pylint: disable-msg=W0613
def handleInt( self, event=None ):
"Handle control-c."
self.node.sendInt()
# pylint: enable-msg=W0613
def sendCmd( self, cmd ):
"Send a command to our node."
text, node = self.text, self.node
if not node.waiting:
node.sendCmd( cmd )
if not self.node.waiting:
self.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."
data = self.node.monitor( timeoutms )
self.append( data )
if not self.node.waiting:
# Print prompt
self.append( self.prompt )
# pylint: enable-msg=W0613
def waiting( self ):
"Are we waiting for output?"
@@ -161,17 +166,17 @@ class Console( Frame ):
"Clear all of our text."
self.text.delete( '1.0', 'end' )
class Graph( Frame ):
"Graph that we can add bars to over time."
def __init__( self, parent=None,
bg = 'white',
gheight=200, gwidth=500,
barwidth=10,
ymax=3.5,):
Frame.__init__( self, parent )
self.bg = bg
@@ -182,57 +187,55 @@ class Graph( Frame ):
self.xpos = 0
# Create everything
self.title = self.graph = None
self.createWidgets()
self.title, self.scale, self.graph = self.createWidgets()
self.updateScrollRegions()
self.yview( 'moveto', '1.0' )
def scale( self ):
def createScale( self ):
"Create a and return a new canvas with scale markers."
height = float( self.gheight )
width = 25
ymax = self.ymax
scale = Canvas( self, width=width, height=height, background=self.bg )
fill = 'red'
scale = Canvas( self, width=width, height=height,
background=self.bg )
opts = { 'fill': 'red' }
# 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
for y in range( 0, int( ymax + 1 ) ):
ypos = height * (1 - float( y ) / ymax )
scale.create_line( width, ypos, width - 10, ypos, fill=fill )
scale.create_text( 10, ypos, text=str( y ), fill=fill )
scale.create_line( width, ypos, width - 10, ypos, **opts )
scale.create_text( 10, ypos, text=str( y ), **opts )
return scale
def updateScrollRegions( self ):
"Update graph and scale scroll regions."
ofs = 20
height = self.gheight + ofs
self.graph.configure( scrollregion=( 0, -ofs,
self.graph.configure( scrollregion=( 0, -ofs,
self.xpos * self.barwidth, height ) )
self.scale.configure( scrollregion=( 0, -ofs, 0, height ) )
def yview( self, *args ):
"Scroll both scale and graph."
self.graph.yview( *args )
self.scale.yview( *args )
"Scroll both scale and graph."
self.graph.yview( *args )
self.scale.yview( *args )
def createWidgets( self ):
"Create initial widget set."
# Objects
title = Label( self, text="Bandwidth (Gb/s)", bg=self.bg )
title = Label( self, text='Bandwidth (Gb/s)', bg=self.bg )
width = self.gwidth
height = self.gheight
scale = self.scale()
scale = self.createScale()
graph = Canvas( self, width=width, height=height, background=self.bg)
xbar = Scrollbar( self, orient='horizontal', command=graph.xview )
ybar = Scrollbar( self, orient='vertical', command=self.yview )
graph.configure( xscrollcommand=xbar.set, yscrollcommand=ybar.set,
scrollregion=(0, 0, width, height ) )
scale.configure( yscrollcommand=ybar.set )
# Layout
title.grid( row=0, columnspan=3, sticky='new')
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' )
self.rowconfigure( 1, weight=1 )
self.columnconfigure( 1, weight=1 )
# Save for future reference
self.title = title
self.scale = scale
self.graph = graph
return graph
return title, scale, graph
def addBar( self, yval ):
"Add a new bar to our graph."
percent = yval / self.ymax
@@ -279,6 +278,8 @@ class Graph( Frame ):
class ConsoleApp( Frame ):
"Simple Tk consoles for Mininet."
menuStyle = { 'font': 'Geneva 7 bold' }
def __init__( self, net, parent=None, width=4 ):
@@ -289,14 +290,14 @@ class ConsoleApp( Frame ):
self.menubar = self.createMenuBar()
cframe = self.cframe = Frame( self )
self.consoles = {} # consoles themselves
titles = {
'hosts': 'Host',
titles = {
'hosts': 'Host',
'switches': 'Switch',
'controllers': 'Controller'
}
for name in titles:
nodes = getattr( net, name )
frame, consoles = self.createConsoles(
frame, consoles = self.createConsoles(
cframe, nodes, width, titles[ name ] )
self.consoles[ name ] = Object( frame=frame, consoles=consoles )
self.selected = None
@@ -305,7 +306,7 @@ class ConsoleApp( Frame ):
cleanUpScreens()
# Close window gracefully
Wm.wm_protocol( self.top, name='WM_DELETE_WINDOW', func=self.quit )
# Initialize graph
graph = Graph( cframe )
self.consoles[ 'graph' ] = Object( frame=graph, consoles=[ graph ] )
@@ -316,7 +317,9 @@ class ConsoleApp( Frame ):
self.bw = 0
self.pack( expand=True, fill='both' )
# Update callback doesn't use console arg
# pylint: disable-msg=W0613
def updateGraph( self, console, output ):
"Update our graph."
m = re.search( r'(\d+) Mbits/sec', output )
@@ -328,13 +331,15 @@ class ConsoleApp( Frame ):
self.graph.addBar( self.bw )
self.bw = 0
self.updates = 0
# pylint: enable-msg=W0613
def setOutputHook( self, fn=None, consoles=None ):
"Register fn as output hook [on specific consoles.]"
if consoles is None:
consoles = self.consoles[ 'hosts' ].consoles
for console in consoles:
console.outputHook = fn
def createConsoles( self, parent, nodes, width, title ):
"Create a grid of consoles in a frame."
f = Frame( parent )
@@ -342,7 +347,7 @@ class ConsoleApp( Frame ):
consoles = []
index = 0
for node in nodes:
console = Console( f, net, node, title=title )
console = Console( f, self.net, node, title=title )
consoles.append( console )
row = index / width
column = index % width
@@ -351,12 +356,12 @@ class ConsoleApp( Frame ):
f.rowconfigure( row, weight=1 )
f.columnconfigure( column, weight=1 )
return f, consoles
def select( self, set ):
"Select a set of consoles to display."
def select( self, groupName ):
"Select a group of consoles to display."
if self.selected is not None:
self.selected.frame.pack_forget()
self.selected = self.consoles[ set ]
self.selected = self.consoles[ groupName ]
self.selected.frame.pack( expand=True, fill='both' )
def createMenuBar( self ):
@@ -365,8 +370,8 @@ class ConsoleApp( Frame ):
buttons = [
( 'Hosts', lambda: self.select( 'hosts' ) ),
( 'Switches', lambda: self.select( 'switches' ) ),
( 'Controllers', lambda: self.select( 'controllers' ) ),
( 'Graph', lambda: self.select( 'graph' ) ),
( 'Controllers', lambda: self.select( 'controllers' ) ),
( 'Graph', lambda: self.select( 'graph' ) ),
( 'Ping', self.ping ),
( 'Iperf', self.iperf ),
( 'Interrupt', self.stop ),
@@ -378,12 +383,12 @@ class ConsoleApp( Frame ):
b.pack( side='left' )
f.pack( padx=4, pady=4, fill='x' )
return f
def clear( self ):
"Clear selection."
for console in self.selected.consoles:
console.clear()
def waiting( self, consoles=None ):
"Are any of our hosts waiting for output?"
if consoles is None:
@@ -453,8 +458,8 @@ class Object( object ):
if __name__ == '__main__':
setLogLevel( 'info' )
net = TreeNet( depth=2, fanout=2 )
net.start()
app = ConsoleApp( net, width=4 )
network = TreeNet( depth=2, fanout=4 )
network.start()
app = ConsoleApp( network, width=4 )
app.mainloop()
net.stop()
network.stop()
+9 -9
View File
@@ -1,7 +1,7 @@
#!/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.
"""
@@ -13,32 +13,32 @@ from mininet.log import setLogLevel, info
def emptyNet():
"Create an empty network and add nodes to it."
net = Mininet( controller=Controller )
info( '*** Adding controller\n' )
net.addController( 'c0' )
info( '*** Adding hosts\n' )
h1 = net.addHost( 'h1', ip='10.0.0.1' )
h2 = net.addHost( 'h2', ip='10.0.0.2' )
info( '*** Adding switch\n' )
s3 = net.addSwitch( 's3' )
info( '*** Creating links\n' )
h1.linkTo( s3 )
h2.linkTo( s3 )
info( '*** Starting network\n')
net.start()
info( '*** Running CLI\n' )
CLI( net )
info( '*** Stopping network' )
net.stop()
if __name__ == '__main__':
setLogLevel( 'info' )
emptyNet()
+223 -177
View File
@@ -24,9 +24,9 @@ from mininet.term import makeTerm, cleanUpScreens
class MiniEdit( Frame ):
"A simple network editor for Mininet."
def __init__( self, parent=None, cheight=200, cwidth=500 ):
Frame.__init__( self, parent )
self.action = None
self.appName = 'MiniEdit'
@@ -39,19 +39,19 @@ class MiniEdit( Frame ):
# Title
self.top = self.winfo_toplevel()
self.top.title( self.appName )
# Menu bar
self.createMenubar()
# Editing canvas
self.cheight, self.cwidth = cheight, cwidth
self.cframe, self.canvas = self.createCanvas()
# Toolbar
self.images = miniEditImages()
self.buttons = {}
self.active = None
self.tools = ( 'Select', 'Host', 'Switch', 'Link' )
self.images = self.createImages()
self.customColors = { 'Switch': 'darkGreen', 'Host': 'blue' }
self.toolbar = self.createToolbar()
@@ -61,49 +61,53 @@ class MiniEdit( Frame ):
self.columnconfigure( 1, weight=1 )
self.rowconfigure( 0, weight=1 )
self.pack( expand=True, fill='both' )
# About box
self.aboutBox = None
# Initialize node data
self.nodeBindings = self.createNodeBindings()
self.nodePrefixes = { 'Switch': 's', 'Host': 'h' }
self.widgetToItem = {}
self.itemToWidget = {}
# Initialize link tool
self.link = self.linkWidget = None
# Selection support
self.selection = None
# Keyboard bindings
self.bind( '<Control-q>', lambda event: self.quit() )
self.bind( '<KeyPress-Delete>', self.deleteSelection )
self.bind( '<KeyPress-BackSpace>', self.deleteSelection )
self.focus()
# Event handling initalization
self.linkx = self.linky = self.linkItem = None
self.lastSelection = None
# Model initialization
self.links = {}
self.nodeCount = 0
self.net = None
# Close window gracefully
Wm.wm_protocol( self.top, name='WM_DELETE_WINDOW', func=self.quit )
def quit( self ):
"Stop our network, if any, then quit."
self.stop()
Frame.quit( self )
def createMenubar( self ):
"Create our menu bar."
font = self.font
mbar = Menu( self.top, font=font )
self.top.configure( menu=mbar )
# Application menu
appMenu = Menu( mbar, tearoff=False )
mbar.add_cascade( label=self.appName, font=font, menu=appMenu )
@@ -111,20 +115,18 @@ class MiniEdit( Frame ):
font=font)
appMenu.add_separator()
appMenu.add_command( label='Quit', command=self.quit, font=font )
"""
fileMenu = Menu( mbar, tearoff=False )
mbar.add_cascade( label="File", font=font, menu=fileMenu )
fileMenu.add_command( label="Load...", font=font )
fileMenu.add_separator()
fileMenu.add_command( label="Save", font=font )
fileMenu.add_separator()
fileMenu.add_command( label="Print", font=font )
"""
#fileMenu = Menu( mbar, tearoff=False )
#mbar.add_cascade( label="File", font=font, menu=fileMenu )
#fileMenu.add_command( label="Load...", font=font )
#fileMenu.add_separator()
#fileMenu.add_command( label="Save", font=font )
#fileMenu.add_separator()
#fileMenu.add_command( label="Print", font=font )
editMenu = Menu( mbar, tearoff=False )
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 ) )
runMenu = Menu( mbar, tearoff=False )
@@ -135,28 +137,28 @@ class MiniEdit( Frame ):
runMenu.add_command( label='Xterm', font=font, command=self.xterm )
# Canvas
def createCanvas( self ):
"Create and return our scrolling canvas frame."
f = Frame( self )
canvas = Canvas( f, width=self.cwidth, height=self.cheight,
canvas = Canvas( f, width=self.cwidth, height=self.cheight,
bg=self.bg )
# Scroll bars
xbar = Scrollbar( f, orient='horizontal', command=canvas.xview )
ybar = Scrollbar( f, orient='vertical', command=canvas.yview )
canvas.configure( xscrollcommand=xbar.set, yscrollcommand=ybar.set )
# Resize box
resize = Label( f, bg='white' )
# Layout
canvas.grid( row=0, column=1, sticky='nsew')
ybar.grid( row=0, column=2, sticky='ns')
xbar.grid( row=1, column=1, sticky='ew' )
resize.grid( row=1, column=2, sticky='nsew' )
# Resize behavior
f.rowconfigure( 0, weight=1 )
f.columnconfigure( 1, weight=1 )
@@ -167,7 +169,7 @@ class MiniEdit( Frame ):
canvas.bind( '<ButtonPress-1>', self.clickCanvas )
canvas.bind( '<B1-Motion>', self.dragCanvas )
canvas.bind( '<ButtonRelease-1>', self.releaseCanvas )
return f, canvas
def updateScrollRegion( self ):
@@ -175,75 +177,76 @@ class MiniEdit( Frame ):
bbox = self.canvas.bbox( 'all' )
if bbox is not None:
self.canvas.configure( scrollregion=( 0, 0, bbox[ 2 ], bbox[ 3 ] ) )
def canvasx( self, x_root ):
"Convert root x coordinate to canvas coordinate."
c = self.canvas
return c.canvasx( x_root ) - c.winfo_rootx()
def canvasy( self, y_root ):
"Convert root y coordinate to canvas coordinate."
c = self.canvas
return c.canvasy( y_root ) - c.winfo_rooty()
# Toolbar
def activate( self, toolName ):
"Activate a tool and press its button."
# Adjust button appearance
if self.active:
self.buttons[ self.active ].configure( relief='raised' )
self.buttons[ toolName ].configure( relief='sunken' )
# Activate dynamic bindings
self.active = toolName
def createToolbar( self ):
"Create and return our toolbar frame."
toolbar = Frame( 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)
if tool in self.images:
b.config( height=35, image=self.images[ tool ] )
# b.config( compound='top' )
b.pack( fill='x' )
self.buttons[ tool ] = b
self.buttons[ tool ] = b
self.activate( self.tools[ 0 ] )
# Spacer
Label( toolbar, text='' ).pack()
# Commands
for cmd, color in [ ( 'Stop', 'darkRed' ), ( 'Run', 'darkGreen' ) ]:
def doCmd( f=getattr( self, 'do' + cmd ) ):
f()
b = Button( toolbar, text=cmd, font=self.smallFont, fg=color, command=doCmd )
doCmd = getattr( self, 'do' + cmd )
b = Button( toolbar, text=cmd, font=self.smallFont,
fg=color, command=doCmd )
b.pack( fill='x', side='bottom' )
return toolbar
def doRun( self ):
"Run command."
self.activate( 'Select' )
for tool in self.tools:
self.buttons[ tool ].config( state='disabled' )
self.start()
def doStop( self ):
"Stop command."
self.stop()
for tool in self.tools:
self.buttons[ tool ].config( state='normal' )
# 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
# may actually require less code. In any case, it's an
# interesting introspection-based alternative to bindtags.
def canvasHandle( self, eventName, event ):
"Generic canvas event handler"
if self.active is None:
@@ -252,36 +255,36 @@ class MiniEdit( Frame ):
handler = getattr( self, eventName + toolName, None )
if handler is not None:
handler( event )
def clickCanvas( self, event ):
"Canvas click handler."
self.canvasHandle( 'click', event )
def dragCanvas( self, event ):
"Canvas drag handler."
self.canvasHandle( 'drag', event )
def releaseCanvas( self, event ):
"Canvas mouse up handler."
self.canvasHandle( 'release', event )
# Currently the only items we can select directly are
# links. Nodes are handled by bindings in the node icon.
# If we want to allow node deletion, we will
def findItem( self, x, y ):
"Find items at a location in our canvas."
items = self.canvas.find_overlapping( x, y, x, y )
if len( items ) == 0:
return None
else:
return items[ 0 ]
# Canvas bindings for Select, Host, Switch and Link tools
def clickSelect( self, event ):
"Select an item."
self.selectItem( self.findItem( event.x, event.y ) )
def deleteItem( self, item ):
"Delete an item."
# Don't delete while network is running
@@ -293,23 +296,27 @@ class MiniEdit( Frame ):
if item in self.itemToWidget:
self.deleteNode( item )
# Delete from view
self.canvas.delete( item )
self.canvas.delete( item )
# Callback ignores event
# pylint: disable-msg=W0613
def deleteSelection( self, event ):
"Delete the selected item."
if self.selection is not None:
self.deleteItem( self.selection )
self.selectItem( None )
# pylint: enable-msg=W0613
def nodeIcon( self, node, name ):
"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' )
# Unfortunately bindtags wants a tuple
bindtags = [ str( self.nodeBindings ) ]
bindtags += list( icon.bindtags() )
icon.bindtags( tuple( bindtags ) )
return icon
def newNode( self, node, event ):
"Add a new node to our canvas."
c = self.canvas
@@ -317,12 +324,13 @@ class MiniEdit( Frame ):
self.nodeCount += 1
name = self.nodePrefixes[ node ] + str( self.nodeCount )
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.itemToWidget[ item ] = icon
self.selectItem( item )
icon.links = {}
def clickHost( self, event ):
"Add a new host to our canvas."
self.newNode( 'Host', event )
@@ -330,7 +338,7 @@ class MiniEdit( Frame ):
def clickSwitch( self, event ):
"Add a new switch to our canvas."
self.newNode( 'Switch', event )
def dragLink( self, event ):
"Drag a link's endpoint to another node."
if self.link is None:
@@ -341,40 +349,47 @@ class MiniEdit( Frame ):
c = self.canvas
c.coords( self.link, self.linkx, self.linky, x, y )
# Callback ignores event
# pylint: disable-msg=W0613
def releaseLink( self, event ):
"Give up on the current link."
if self.link is not None:
self.canvas.delete( self.link )
self.linkWidget = self.linkItem = self.link = None
# Generic node handlers
def createBindings( self, bindings ):
l = Label()
for event, binding in bindings.items():
l.bind( event, binding )
return l
# pylint: enable-msg=W0613
# Generic node handlers
def createNodeBindings( self ):
"Create a set of bindings for nodes."
return self.createBindings( {
bindings = {
'<ButtonPress-1>': self.clickNode,
'<B1-Motion>': self.dragNode,
'<ButtonRelease-1>': self.releaseNode,
'<Enter>': self.enterNode,
'<Leave>': self.leaveNode,
'<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 ):
"Select an item and remember old selection."
self.lastSelection = self.selection
self.selection = item
def enterNode( self, event ):
"Select node on entry."
self.selectNode( event )
# Callback ignores event
# pylint: disable-msg=W0613
def leaveNode( self, event ):
"Restore old selection on exit."
self.selectItem( self.lastSelection )
# pylint: enable-msg=W0613
def clickNode( self, event ):
"Node click handler."
@@ -383,26 +398,26 @@ class MiniEdit( Frame ):
else:
self.selectNode( event )
return 'break'
def dragNode( self, event ):
"Node drag handler."
if self.active is 'Link':
self.dragLink( event )
else:
self.dragNodeAround( event )
def releaseNode( self, event ):
"Node release handler."
if self.active is 'Link':
self.finishLink( event )
# Specific node handlers
def selectNode( self, event ):
"Select the node that was clicked on."
item = self.widgetToItem.get( event.widget, None )
self.selectItem( item )
def dragNodeAround( self, event ):
"Drag a node around on the canvas."
c = self.canvas
@@ -429,25 +444,31 @@ class MiniEdit( Frame ):
w = event.widget
item = self.widgetToItem[ w ]
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' )
self.linkx, self.linky = x, y
self.linkWidget = w
self.linkItem = item
# Link bindings
# Selection still needs a bit of work overall
# Callbacks ignore event
# pylint: disable-msg=W0613
def select( event, link=self.link ):
"Select item on mouse entry."
self.selectItem( link )
def highlight( event, link=self.link ):
"Highlight item on mouse entry."
# self.selectItem( link )
self.canvas.itemconfig( link, fill='green' )
def unhighlight( event, link=self.link ):
"Unhighlight item on mouse exit."
self.canvas.itemconfig( link, fill='blue' )
# self.selectItem( None )
# pylint: disable-msg=W0613
self.canvas.tag_bind( self.link, '<Enter>', highlight )
self.canvas.tag_bind( self.link, '<Leave>', unhighlight )
self.canvas.tag_bind( self.link, '<ButtonPress-1>', select )
def finishLink( self, event ):
"Finish creating a link"
if self.link is None:
@@ -471,13 +492,11 @@ class MiniEdit( Frame ):
x, y = c.coords( target )
c.coords( self.link, self.linkx, self.linky, x, y )
self.addLink( source, dest )
# We're done
self.link = self.linkWidget = None
# Menu handlers
def about( self ):
"Display about box."
about = self.aboutBox
@@ -494,28 +513,28 @@ class MiniEdit( Frame ):
line1.pack( padx=20, pady=10 )
line2.pack(pady=10 )
line3.pack(pady=10 )
hide = lambda about=about: about.withdraw()
hide = ( lambda about=about: about.withdraw() )
self.aboutBox = about
# Hide on close rather than destroying window
Wm.wm_protocol( about, name='WM_DELETE_WINDOW', func=hide )
# Show (existing) window
about.deiconify()
def createToolImages( self ):
"Create toolbar (and icon) images."
# Model interface
#
# Ultimately we will either want to use a topo or
# mininet object here, probably.
def addLink( self, source, dest ):
"Add link to model."
source.links[ dest ] = self.link
dest.links[ source ] = self.link
self.links[ self.link ] = ( source, dest )
def deleteLink( self, link ):
"Delete link from model."
pair = self.links.get( link, None )
@@ -539,7 +558,7 @@ class MiniEdit( Frame ):
"Build network based on our topology."
net = Mininet( topo=None )
# Make controller
net.addController( 'c0' )
# Make nodes
@@ -559,24 +578,26 @@ class MiniEdit( Frame ):
srcName, dstName = src[ 'text' ], dst[ 'text' ]
src, dst = net.nameToNode[ srcName ], net.nameToNode[ dstName ]
src.linkTo( dst )
# Build network (we have to do this separately at the moment )
net.build()
return net
def start( self ):
"Start network."
if self.net is None:
self.net = self.build()
self.net.start()
def stop( self ):
"Stop network."
if self.net is not None:
self.net.stop()
cleanUpScreens()
self.net = None
def xterm( self, ignore=None ):
"Make an xterm when a button is pressed."
if ( self.selection is None or
self.net is None or
self.selection not in self.itemToWidget ):
@@ -584,83 +605,108 @@ class MiniEdit( Frame ):
name = self.itemToWidget[ self.selection ][ 'text' ]
if name not in self.net.nameToNode:
return
self.net.terms.append( makeTerm( self.net.nameToNode[ name ], 'Host' ) )
# Image data. Git will be unhappy.
term = makeTerm( self.net.nameToNode[ name ], 'Host' )
self.net.terms.append( term )
def createImages( self ):
"Initialize button/icon images."
images = {}
images[ 'Select' ] = BitmapImage( file='/usr/include/X11/bitmaps/left_ptr' )
images[ 'Host' ] = 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
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
def miniEditImages():
"Create and return images for MiniEdit."
# Image data. Git will be unhappy. However, the alternative
# is to keep track of separate binary files, which is also
# unappealing.
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__':
setLogLevel( 'info' )
app = MiniEdit()
app.mainloop()
+66 -68
View File
@@ -20,17 +20,17 @@ from mininet.topolib import TreeTopo
from mininet.util import quietRun
# bwtest support
class Graph( Frame ):
"Graph that we can add bars to over time."
def __init__( self, master=None,
bg = 'white',
gheight=200, gwidth=500,
barwidth=10,
ymax=3.5,):
Frame.__init__( self, master )
self.bg = bg
@@ -41,12 +41,11 @@ class Graph( Frame ):
self.xpos = 0
# Create everything
self.title = self.graph = None
self.createWidgets()
self.title, self.graph, self.scale = self.createWidgets()
self.updateScrollRegions()
self.yview( 'moveto', '1.0' )
def scale( self ):
def createScale( self ):
"Create a and return a new canvas with scale markers."
height = float( self.gheight )
width = 25
@@ -60,22 +59,22 @@ class Graph( Frame ):
ypos = height * ( 1 - float( y ) / ymax )
scale.create_line( width, ypos, width - 10, ypos, fill=fill )
scale.create_text( 10, ypos, text=str( y ), fill=fill )
return scale
def updateScrollRegions( self ):
"Update graph and scale scroll regions."
ofs = 20
height = self.gheight + ofs
self.graph.configure( scrollregion=( 0, -ofs,
self.graph.configure( scrollregion=( 0, -ofs,
self.xpos * self.barwidth, height ) )
self.scale.configure( scrollregion=( 0, -ofs, 0, height ) )
def yview( self, *args ):
"Scroll both scale and graph."
self.graph.yview( *args )
self.scale.yview( *args )
def createWidgets( self ):
"Create initial widget set."
@@ -83,14 +82,14 @@ class Graph( Frame ):
title = Label( self, text="Bandwidth (Mb/s)", bg=self.bg )
width = self.gwidth
height = self.gheight
scale = self.scale()
scale = self.createScale()
graph = Canvas( self, width=width, height=height, background=self.bg)
xbar = Scrollbar( self, orient='horizontal', command=graph.xview )
ybar = Scrollbar( self, orient='vertical', command=self.yview )
graph.configure( xscrollcommand=xbar.set, yscrollcommand=ybar.set,
scrollregion=(0, 0, width, height ) )
scale.configure( yscrollcommand=ybar.set )
# Layout
title.grid( row=0, columnspan=3, sticky='new')
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' )
self.rowconfigure( 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 ):
"Add a new bar to our graph."
percent = yval / self.ymax
@@ -123,7 +117,7 @@ class Graph( Frame ):
"Add a bar for testing purposes."
ms = 1000
if self.xpos < 10:
self.addBar( self.xpos/10 * self.ymax )
self.addBar( self.xpos / 10 * self.ymax )
self.after( ms, self.test )
def setTitle( self, text ):
@@ -134,52 +128,49 @@ class Graph( Frame ):
class Controls( Frame ):
"Handy controls for configuring test."
switches = {
switches = {
'Kernel Switch': KernelSwitch,
'User Switch': UserSwitch,
'Open vSwitch': OVSKernelSwitch
}
controllers = {
'Reference Controller': Controller,
'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 )
# Option menus
opts = { 'font': 'Geneva 7 bold' }
self.switch = self.optionMenu( 'Switch', self.switches,
self.switch = self.optionMenu( self.switches,
KernelSwitch, opts )
self.controller = self.optionMenu( 'Controller', self.controllers,
self.controller = self.optionMenu( self.controllers,
Controller, opts)
# Spacer
pk = { 'fill': 'x' }
Label( self, **opts ).pack( **pk )
# Buttons
self.start = Button( self, text='Start', command=start, **opts )
self.stop = Button( self, text='Stop', command=stop, **opts )
self.quit = Button( self, text='Quit', command=quit, **opts )
self.start = Button( self, text='Start', command=startFn, **opts )
self.stop = Button( self, text='Stop', command=stopFn, **opts )
self.quit = Button( self, text='Quit', command=quitFn, **opts )
for button in ( self.start, self.stop, self.quit ):
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,
r=re.compile( r'(\d+) s: in ([\d\.]+) MB/s, out ([\d\.]+) MB/s' ) ):
"Parse udpbwtest.c output, returning seconds, inbw, outbw."
@@ -193,34 +184,41 @@ def parsebwtest( line,
class UdpBwTest( Frame ):
"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."
Frame.__init__( self, master )
self.controls = Controls( self, self.start, self.stop, self.quit )
self.graph = Graph( self )
# Layout
self.controls.pack( side='left', expand=False, fill='y' )
self.graph.pack( side='right', 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
def start( self ):
print "start"
"Start test."
if self.running:
return
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.hostCount = len( self.hosts )
print "*** Starting network"
self.net.start()
@@ -229,7 +227,7 @@ class UdpBwTest( Frame ):
for host in hosts:
ips = [ h.IP() for h in hosts if h != host ]
host.cmdPrint( './udpbwtest ' + ' '.join( ips ) + ' &' )
print "*** Monitoring hosts"
self.output = self.net.monitor( hosts, timeoutms=1 )
self.results = {}
@@ -238,12 +236,12 @@ class UdpBwTest( Frame ):
# Pylint isn't smart enough to understand iterator.next()
# pylint: disable-msg=E1101
def updateGraph( self ):
"Graph input bandwidth."
print "updateGraph"
if not self.running:
return
@@ -269,20 +267,20 @@ class UdpBwTest( Frame ):
def stop( self ):
"Stop test."
print "*** Stopping udpbwtest processes"
# We *really* don't want these things hanging around!
quietRun( 'killall -9 udpbwtest' )
if not self.running:
return
print "*** Stopping network"
self.running = False
self.net.stop()
def quit( self ):
print "*** Quitting"
"Quit app."
self.stop()
Frame.quit( self )
@@ -290,9 +288,9 @@ class UdpBwTest( Frame ):
# Useful utilities
def findKey( dict, value ):
"Find some key where dict[ key ] == value."
return [ key for key, val in dict.items() if val == value ][ 0 ]
def findKey( d, value ):
"Find some key where d[ key ] == value."
return [ key for key, val in d.items() if val == value ][ 0 ]
def assign( obj, **kwargs):
"Set a bunch of fields in an object."
@@ -306,7 +304,7 @@ class Object( object ):
if __name__ == '__main__':
setLogLevel( 'info' )
topo = TreeTopo( depth=1, fanout=2 )
app = UdpBwTest( topo )
app = UdpBwTest( topo=TreeTopo( depth=2, fanout=2 ) )
app.mainloop()
+1 -1
View File
@@ -23,7 +23,7 @@ def sh( cmd ):
def cleanup():
"""Clean up junk which might be left over from old runs;
do fast stuff before slow dp and link removal!"""
info("*** Removing excess controllers/ofprotocols/ofdatapaths/pings/noxes"
"\n")
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 os import isatty
from select import poll, POLLIN
from sys import stdin
import sys
from mininet.log import info, output, error
from mininet.term import makeTerms
from mininet.util import quietRun, isShellBuiltin
class CLI( Cmd ):
"Simple command-line interface to talk to nodes."
prompt = 'mininet> '
def __init__( self, mininet, stdin=stdin ):
def __init__( self, mininet, stdin=sys.stdin ):
self.mn = mininet
self.nodelist = self.mn.controllers + self.mn.switches + self.mn.hosts
self.nodemap = {} # map names to Node objects
@@ -75,7 +75,7 @@ class CLI( Cmd ):
# must have the same interface
# pylint: disable-msg=W0613,R0201
helpStr = (
helpStr = (
'You may also send a command to a node using:\n'
' <node> command {args}\n'
'For example:\n'
@@ -110,7 +110,9 @@ class CLI( Cmd ):
for switch in self.mn.switches:
output( switch.name, '<->' )
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( '\n' )
@@ -209,7 +211,7 @@ class CLI( Cmd ):
def isatty( self ):
"Is our standard input a tty?"
return isatty( self.stdin.fileno() )
def do_noecho( self, line ):
"Run an interactive command with echoing turned off."
if self.isatty():
@@ -236,20 +238,16 @@ class CLI( Cmd ):
for arg in rest ]
rest = ' '.join( rest )
# Run cmd on node:
node.sendCmd( rest )
self.waitForNode( node, isShellBuiltin( first ) )
builtin = isShellBuiltin( first )
print "builtin =", builtin
node.sendCmd( rest, printPid=( not builtin ) )
self.waitForNode( node )
else:
error( '*** Unknown command: %s\n' % first )
# pylint: enable-msg=W0613,R0201
def isReadable( self, poller ):
"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 ):
def waitForNode( self, node ):
"Wait for a node to finish, and print its output."
# Pollers
nodePoller = poll()
@@ -264,10 +262,10 @@ class CLI( Cmd ):
while True:
try:
bothPoller.poll()
if self.isReadable( self.inPoller ):
if isReadable( self.inPoller ):
key = self.stdin.read( 1 )
node.write( key )
if self.isReadable( nodePoller ):
if isReadable( nodePoller ):
data = node.monitor()
output( data )
if not node.waiting:
@@ -275,3 +273,11 @@ class CLI( Cmd ):
except KeyboardInterrupt:
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.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 ):
"Network emulation with hosts spawned in network namespaces."
@@ -167,7 +147,7 @@ class Mininet( object ):
if topo and build:
self.build()
def addHost( self, name, mac=None, ip=None ):
"""Add host.
name: name of host to add
@@ -195,13 +175,13 @@ class Mininet( object ):
self.nameToNode[ name ] = sw
return sw
def addController( self, controller ):
def addController( self, name='c0', **kwargs ):
"""Add controller.
controller: Controller class"""
controller_new = self.controller( 'c0' )
controller_new = self.controller( name, **kwargs )
if controller_new: # allow controller-less setups
self.controllers.append( controller_new )
self.nameToNode[ 'c0' ] = controller_new
self.nameToNode[ name ] = controller_new
# Control network support:
#
@@ -558,3 +538,27 @@ class Mininet( object ):
result = CLI( self )
self.stop()
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.readbuf = ''
self.waiting = False
# Stash additional information as desired
self.args = kwargs
@classmethod
def fdToNode( cls, fd ):
@@ -186,7 +188,7 @@ class Node( object ):
if self.lastPid:
try:
os.kill( self.lastPid, sig )
except Exception:
except OSError:
pass
def monitor( self, timeoutms=None ):
@@ -301,7 +303,7 @@ class Node( object ):
if port1 is None:
port1 = node1.newPort()
if port2 is None:
port2 = node2.newPort()
port2 = node2.newPort()
intf1 = node1.intfName( port1 )
intf2 = node2.intfName( port2 )
makeIntfPair( intf1, intf2 )
@@ -410,14 +412,6 @@ class Switch( Node ):
error( '*** Error: %s has execed and cannot accept commands' %
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 ):
"User-space switch."
@@ -458,7 +452,7 @@ class UserSwitch( Switch ):
class KernelSwitch( Switch ):
"""Kernel-space switch.
Currently only works in root namespace."""
def __init__( self, name, dp=None, **kwargs ):
"""Init.
name: name for switch
@@ -496,7 +490,7 @@ class KernelSwitch( Switch ):
# Run protocol daemon
controller = controllers[ 0 ]
self.cmd( 'ofprotocol ' + self.dp +
' tcp:%s:%d' % ( controller.IP(), controller.port ) +
' tcp:%s:%d' % ( controller.IP(), controller.port ) +
' --fail=closed ' + self.opts +
' 1> ' + ofplog + ' 2>' + ofplog + ' &' )
self.execed = False
@@ -550,8 +544,8 @@ class OVSKernelSwitch( Switch ):
# Run protocol daemon
controller = controllers[ 0 ]
self.cmd( 'ovs-openflowd ' + self.dp +
' tcp:%s:%i' % ( controller.IP(), controller.port ) +
' --fail=closed ' + self.opts +
' tcp:%s:%i' % ( controller.IP(), controller.port ) +
' --fail=closed ' + self.opts +
' 1>' + ofplog + ' 2>' + ofplog + '&' )
self.execed = False
+3 -3
View File
@@ -15,7 +15,7 @@ from mininet.util import quietRun
def quoteArg( arg ):
"Quote an argument if it contains spaces."
return repr( arg ) if ' ' in arg else arg
def makeTerm( node, title='Node', term='xterm' ):
"""Run screen on a node, and hook up a terminal.
node: Node object
@@ -38,9 +38,9 @@ def makeTerm( node, title='Node', term='xterm' ):
else:
args = [ 'sh', '-c', 'exec tail -f /tmp/' + node.name + '*.log' ]
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
args = [ ' '.join( [ quoteArg( arg ) for arg in args ] ) ]
args = [ ' '.join( [ quoteArg( arg ) for arg in args ] ) ]
return Popen( cmds[ term ] + args )
def cleanUpScreens():
+33 -21
View File
@@ -5,7 +5,9 @@ from resource import setrlimit, RLIMIT_NPROC, RLIMIT_NOFILE
import select
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 ):
"""Simple interface to subprocess.call()
@@ -17,13 +19,16 @@ def checkRun( cmd ):
cmd: list of command params"""
return check_call( cmd.split( ' ' ) )
# pylint doesn't understand explicit type checking
# pylint: disable-msg=E1103
def quietRun( *cmd ):
"""Run a command, routing stderr to stdout, and return the output.
cmd: list of command params"""
if len( cmd ) == 1:
cmd = cmd[ 0 ]
if isinstance( cmd, str ):
cmd = cmd.split( ' ' )
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
@@ -42,6 +47,22 @@ def quietRun( *cmd ):
break
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
#
# Interfaces are managed as strings which are simply the
@@ -78,7 +99,7 @@ def retry( retries, delaySecs, fn, *args, **keywords ):
sleep( delaySecs )
tries += 1
if tries >= retries:
lg.error( "*** gave up after %i retries\n" % tries )
error( "*** gave up after %i retries\n" % tries )
exit( 1 )
def moveIntfNoRetry( intf, node, printError=False ):
@@ -91,7 +112,7 @@ def moveIntfNoRetry( intf, node, printError=False ):
links = node.cmd( 'ip link show' )
if not ( ' %s:' % intf ) in links:
if printError:
lg.error( '*** Error: moveIntf: ' + intf +
error( '*** Error: moveIntf: ' + intf +
' not successfully moved to ' + node.name + '\n' )
return False
return True
@@ -112,10 +133,8 @@ def createLink( node1, node2, port1=None, port2=None ):
returns: intf1 name, intf2 name"""
return node1.linkTo( node2, port1, port2 )
def fixLimits():
"Fix ridiculously small resource limits."
setrlimit( RLIMIT_NPROC, ( 4096, 8192 ) )
setrlimit( RLIMIT_NOFILE, ( 16384, 32768 ) )
# IP and Mac address formatting and parsing
def _colonHex( val, bytes ):
"""Generate colon-hex string.
@@ -181,17 +200,10 @@ def makeNumeric( s ):
else:
return s
# 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
# Other stuff we use
isShellBuiltin.builtIns = None
# pylint: enable-msg=E1101,W0612
def fixLimits():
"Fix ridiculously small resource limits."
setrlimit( RLIMIT_NPROC, ( 4096, 8192 ) )
setrlimit( RLIMIT_NOFILE, ( 16384, 32768 ) )