Improve CLI line editing capabilities

Convert CLI to use Python Cmd object, which provides line editing.
This commit is contained in:
Brandon Heller
2010-01-29 14:42:53 -08:00
parent 83097ff942
commit 114dcd5653
+48 -53
View File
@@ -33,21 +33,18 @@ Bugs/limitations:
For now, we recommend limiting CLI use to non-interactive For now, we recommend limiting CLI use to non-interactive
commands which terminate in a reasonable amount of time. commands which terminate in a reasonable amount of time.
- We don't (yet) support command line history editing. This is
coming soon.
""" """
from subprocess import call from subprocess import call
import sys import sys
from cmd import Cmd
from mininet.log import lg from mininet.log import lg
class CLI( object ): class CLI( Cmd ):
"Simple command-line interface to talk to nodes." "Simple command-line interface to talk to nodes."
cmds = [ '?', 'help', 'nodes', 'net', 'sh', 'pingAll', 'exit', prompt = 'mininet> '
'pingPair', 'iperf', 'iperfUdp', 'intfs', 'dump' ]
def __init__( self, mininet ): def __init__( self, mininet ):
self.mn = mininet self.mn = mininet
@@ -57,24 +54,27 @@ class CLI( object ):
for cname, cnode in self.mn.controllers.iteritems(): for cname, cnode in self.mn.controllers.iteritems():
self.nodemap[ cname ] = cnode self.nodemap[ cname ] = cnode
self.nodelist = self.nodemap.values() self.nodelist = self.nodemap.values()
self.run() Cmd.__init__( self )
lg.warn( '*** Starting CLI:\n' )
self.cmdloop()
# Disable pylint "Unused argument: 'arg's'" messages. # Disable pylint "Unused argument: 'arg's'" messages.
# Each CLI function needs the same interface. # Each CLI function needs the same interface.
# pylint: disable-msg=W0613 # pylint: disable-msg=W0613
# Commands def do_help(self, arg):
def help( self, args ): "Describe available CLI commands."
"Semi-useful help for CLI." Cmd.do_help(self, arg)
helpStr = ( 'Available commands are:' + str( self.cmds ) + '\n' 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'
' mininet> h0 ifconfig\n' ' mininet> h0 ifconfig\n'
'\n' '\n'
'The interpreter automatically substitutes IP ' 'The interpreter automatically substitutes IP '
'addresses\n' 'addresses\n'
'for node names, so commands like\n' 'for node names when a node is the first arg, so command'
' like\n'
' mininet> h0 ping -c1 h1\n' ' mininet> h0 ping -c1 h1\n'
'should work.\n' 'should work.\n'
'\n\n' '\n\n'
@@ -82,14 +82,14 @@ class CLI( object ):
'so please limit commands to ones that do not\n' 'so please limit commands to ones that do not\n'
'require user interaction and will terminate\n' 'require user interaction and will terminate\n'
'after a reasonable amount of time.\n' ) 'after a reasonable amount of time.\n' )
print( helpStr ) self.stdout.write(helpStr)
def nodes( self, args ): def do_nodes( self, args ):
"List all nodes." "List all nodes."
nodes = ' '.join( [ node.name for node in sorted( self.nodelist ) ] ) nodes = ' '.join( [ node.name for node in sorted( self.nodelist ) ] )
lg.info( 'available nodes are: \n%s\n' % nodes ) lg.info( 'available nodes are: \n%s\n' % nodes )
def net( self, args ): def do_net( self, args ):
"List network connections." "List network connections."
for switchDpid in self.mn.topo.switches(): for switchDpid in self.mn.topo.switches():
switch = self.mn.nodes[ switchDpid ] switch = self.mn.nodes[ switchDpid ]
@@ -99,65 +99,65 @@ class CLI( object ):
lg.info( ' %s' % node.name ) lg.info( ' %s' % node.name )
lg.info( '\n' ) lg.info( '\n' )
def sh( self, args ): def do_sh( self, args ):
"Run an external shell command" "Run an external shell command"
call( [ 'sh', '-c' ] + args ) call( [ 'sh', '-c' ] + args )
def pingAll( self, args ): def do_pingAll( self, args ):
"Ping between all hosts." "Ping between all hosts."
self.mn.pingAll() self.mn.pingAll()
def pingPair( self, args ): def do_pingPair( self, args ):
"Ping between first two hosts, useful for testing." "Ping between first two hosts, useful for testing."
self.mn.pingPair() self.mn.pingPair()
def iperf( self, args ): def do_iperf( self, args ):
"Simple iperf TCP test between two hosts." "Simple iperf TCP test between two hosts."
self.mn.iperf() self.mn.iperf()
def iperfUdp( self, args ): def do_iperfUdp( self, args ):
"Simple iperf UDP test between two hosts." "Simple iperf UDP test between two hosts."
udpBw = args[ 0 ] if len( args ) else '10M' udpBw = args[ 0 ] if len( args ) else '10M'
self.mn.iperfUdp( udpBw ) self.mn.iperfUdp( udpBw )
def intfs( self, args ): def do_intfs( self, args ):
"List interfaces." "List interfaces."
for node in self.mn.nodes.values(): for node in self.mn.nodes.values():
lg.info( '%s: %s\n' % ( node.name, ' '.join( node.intfs ) ) ) lg.info( '%s: %s\n' % ( node.name, ' '.join( node.intfs ) ) )
def dump( self, args ): def do_dump( self, args ):
"Dump node info." "Dump node info."
for node in self.mn.nodes.values(): for node in self.mn.nodes.values():
lg.info( '%s\n' % node ) lg.info( '%s\n' % node )
# Re-enable pylint "Unused argument: 'arg's'" messages. def do_exit( self, args ):
# pylint: enable-msg=W0613 "Exit"
return 'exited by user command'
def run( self ): def do_quit( self, args ):
"Read and execute commands." "Exit"
lg.warn( '*** Starting CLI:\n' ) self.do_exit()
while True:
lg.warn( 'mininet> ' ) def default( self, line ):
inputLine = sys.stdin.readline() """Called on an input line when the command prefix is not recognized.
if inputLine == '':
break Overridden to run shell commands when a node is the first CLI argument.
if inputLine[ -1 ] == '\n': Past the first CLI argument, node names are automatically replaced with
inputLine = inputLine[ :-1 ] corresponding IP addrs.
cmd = inputLine.split( ' ' ) """
first = cmd[ 0 ] first, args, line = self.parseline( line )
rest = cmd[ 1: ] if len(args) > 0 and args[ -1 ] == '\n':
if first in self.cmds and hasattr( self, first ): args = args[ :-1 ]
getattr( self, first )( rest ) rest = args.split( ' ' )
elif first in self.nodemap and rest != []:
if first in self.nodemap:
node = self.nodemap[ first ] node = self.nodemap[ first ]
# Substitute IP addresses for node names in command # Substitute IP addresses for node names in command
rest = [ self.nodemap[ arg ].IP() rest = [ self.nodemap[ arg ].IP()
if arg in self.nodemap else arg if arg in self.nodemap else arg
for arg in rest ] for arg in rest ]
rest = ' '.join( rest ) rest = ' '.join( rest )
# Interactive commands don't work yet, and # Run cmd on node:
# there are still issues with control-c
lg.warn( '*** %s: running %s\n' % ( node.name, rest ) )
node.sendCmd( rest ) node.sendCmd( rest )
while True: while True:
try: try:
@@ -167,13 +167,8 @@ class CLI( object ):
break break
except KeyboardInterrupt: except KeyboardInterrupt:
node.sendInt() node.sendInt()
elif first == '':
pass
elif first in [ 'exit', 'quit' ]:
break
elif first == '?':
self.help( rest )
else: else:
lg.error( 'CLI: unknown node or command: < %s >\n' % first ) self.stdout.write('*** Unknown syntax: %s\n'%line)
#lg.info( '*** CLI: command complete\n' )
return 'exited by user command' # Re-enable pylint "Unused argument: 'arg's'" messages.
# pylint: enable-msg=W0613