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)
+37 -32
View File
@@ -128,23 +128,28 @@ class Console( Frame ):
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?"
@@ -182,27 +187,25 @@ 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 ):
@@ -214,18 +217,18 @@ class Graph( Frame ):
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 )
@@ -241,11 +244,7 @@ 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."
@@ -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 ):
@@ -317,6 +318,8 @@ class ConsoleApp( Frame ):
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,8 +331,10 @@ 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:
@@ -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
@@ -352,11 +357,11 @@ class ConsoleApp( Frame ):
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 ):
@@ -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()
+138 -92
View File
@@ -48,10 +48,10 @@ class MiniEdit( Frame ):
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()
@@ -83,6 +83,10 @@ class MiniEdit( Frame ):
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
@@ -112,15 +116,13 @@ class MiniEdit( Frame ):
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 )
@@ -189,6 +191,7 @@ class MiniEdit( Frame ):
# 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' )
@@ -203,7 +206,7 @@ class MiniEdit( Frame ):
# 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 ] )
@@ -217,9 +220,9 @@ class MiniEdit( Frame ):
# 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
@@ -267,9 +270,9 @@ class MiniEdit( Frame ):
# 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
@@ -295,10 +298,14 @@ class MiniEdit( Frame ):
# Delete from view
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."
@@ -317,7 +324,8 @@ 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 )
@@ -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
# pylint: enable-msg=W0613
# Generic node handlers
def createBindings( self, bindings ):
l = Label()
for event, binding in bindings.items():
l.bind( event, binding )
return l
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."
@@ -436,14 +451,20 @@ class MiniEdit( Frame ):
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 )
@@ -471,8 +492,6 @@ 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
@@ -494,7 +513,7 @@ 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 )
@@ -566,17 +585,19 @@ class MiniEdit( Frame ):
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,77 +605,102 @@ 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' ) )
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."
images = {}
def miniEditImages():
"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
# unappealing.
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=""" )
return {
'Select': BitmapImage(
file='/usr/include/X11/bitmaps/left_ptr' ),
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
""" )
'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=
""" ),
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=
""" )
'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
""" ),
return images
'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' )
+35 -37
View File
@@ -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
@@ -83,7 +82,7 @@ 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 )
@@ -99,12 +98,7 @@ 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, graph, scale
def addBar( self, yval ):
"Add a new bar to our graph."
@@ -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 ):
@@ -146,26 +140,15 @@ class Controls( Frame ):
'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
@@ -173,12 +156,20 @@ class Controls( Frame ):
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' ) ):
@@ -193,7 +184,7 @@ 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 )
@@ -206,10 +197,16 @@ class UdpBwTest( Frame ):
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
@@ -217,7 +214,8 @@ class UdpBwTest( Frame ):
switch = self.controls.switch()
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 )
@@ -282,7 +280,7 @@ class UdpBwTest( Frame ):
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()
+20 -14
View File
@@ -29,7 +29,7 @@ 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
@@ -40,7 +40,7 @@ class CLI( Cmd ):
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
@@ -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' )
@@ -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
+27 -23
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."
@@ -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
+3 -9
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 ):
@@ -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."
+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 ) )