diff --git a/examples/consoles.py b/examples/consoles.py new file mode 100755 index 0000000..bde2307 --- /dev/null +++ b/examples/consoles.py @@ -0,0 +1,149 @@ +#!/usr/bin/python + +""" +consoles.py: bring up a bunch of miniature consoles on a virtual network + +This demo shows how to monitor a set of nodes by using +Tkinter's createfilehandler() and Node()'s monitor(). +""" + +from Tkinter import * + +from mininet.log import setLogLevel +from mininet.topolib import TreeNet + +class Console( Frame ): + "A simple console on a host." + + def __init__( self, parent, node, height=16, width=32 ): + Frame.__init__( self, parent ) + self.node = node + self.height, self.width = height, width + self.text = self.makeWidgets( ) + self.bindEvents() + + def makeWidgets( self ): + "Make a label, a text area, and a scroll bar." + labelStyle = { + 'font': 'Monaco 7', + 'relief': 'sunken' + } + textStyle = { + 'font': 'Monaco 7', + 'bg': 'black', + 'fg': 'green', + 'width': self.width, + 'height': self.height, + 'relief': 'sunken' + } + label = Label( self, text=self.node.name, **labelStyle ) + label.pack( side='top', fill='x' ) + text = Text( self, wrap='word', **textStyle ) + ybar = Scrollbar( self, orient='vertical', command=text.yview ) + text.configure( yscrollcommand=ybar.set ) + text.pack( side='left', expand=True, fill='both' ) + ybar.pack( side='right', fill='y' ) + return text + + def bindEvents( self ): + "Bind keyboard and file events." + self.text.bind( '', self.handleReturn ) + self.text.bind( '', self.handleInt ) + self.text.bind( '', self.handleKey ) + # This is not well-documented, but it is the correct + # way to trigger a file event handler from Tk's + # event loop! + self.tk.createfilehandler( self.node.stdout, READABLE, + self.handleReadable ) + + def append( self, text ): + "Append something to our text frame." + self.text.insert( 'end', text ) + self.text.mark_set( 'insert', 'end' ) + self.text.see( 'insert' ) + + def handleKey( self, event ): + "Handle a regular key press." + self.append( event.char ) + + def handleReturn( self, event ): + "Handle a carriage return." + print "handleReturn" + + def handleInt( self, event=None ): + "Handle control-c." + self.node.sendInt() + + def sendCmd( self, cmd ): + "Send a command to our node." + text, node = self.text, self.node + node.sendCmd( cmd ) + + def handleReadable( self, file, mask ): + data = self.node.monitor() + self.append( data ) + +class Consoles( Frame ): + + def __init__( self, nodes, parent=None, width=4 ): + Frame.__init__( self, parent ) + self.nodes = nodes + self.createMenuBar( font='Geneva 7' ) + self.consoles = self.createConsoles( nodes, width ) + self.pack( expand=True, fill='both' ) + + def createConsoles( self, nodes, width ): + "Create a grid of consoles in a frame." + f = Frame( self ) + # Create consoles + consoles = [] + index = 0 + for node in nodes: + console = Console( f, node ) + consoles.append( console ) + row = int( index / width ) + column = index % width + console.grid( row=row, column=column, sticky='nsew' ) + index += 1 + f.rowconfigure( row, weight=1 ) + f.columnconfigure( column, weight=1 ) + f.pack( expand=True, fill='both' ) + return consoles + + def createMenuBar( self, font ): + "Create and return a menu (really button) bar." + f = Frame( self ) + buttons = [ + ( 'Ping', self.ping ), + ( 'Interrupt', self.stop ), + ( 'Quit', self.quit ) + ] + for name, cmd in buttons: + b = Button( f, text=name, command=cmd, font=font ) + b.pack( side='left' ) + f.pack( padx=4, pady=4, fill='x' ) + return f + + def ping( self ): + "Tell each console to ping the next one." + consoles = self.consoles + count = len( consoles ) + i = 1 + for console in consoles: + ip = consoles[ i ].node.IP() + console.sendCmd( 'ping ' + ip ) + i = ( i + 1 ) % count + + def stop( self ): + "Interrupt all consoles." + for console in self.consoles: + console.handleInt() + + +if __name__ == '__main__': + setLogLevel( 'info' ) + net = TreeNet( depth=2, fanout=2 ) + net.start() + app = Consoles( net.hosts, width=2 ) + app.mainloop() + net.stop()