Skip to content
Snippets Groups Projects
Commit 114dcd56 authored by Brandon Heller's avatar Brandon Heller
Browse files

Improve CLI line editing capabilities

Convert CLI to use Python Cmd object, which provides line editing.
parent 83097ff9
No related branches found
No related tags found
No related merge requests found
...@@ -33,21 +33,18 @@ ...@@ -33,21 +33,18 @@
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 @@ def __init__( self, mininet ): ...@@ -57,24 +54,27 @@ def __init__( self, mininet ):
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 @@ def help( self, args ): ...@@ -82,14 +82,14 @@ def help( self, args ):
'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,81 +99,76 @@ def net( self, args ): ...@@ -99,81 +99,76 @@ def net( self, args ):
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 != []:
node = self.nodemap[ first ] if first in self.nodemap:
# Substitute IP addresses for node names in command node = self.nodemap[ first ]
rest = [ self.nodemap[ arg ].IP() # Substitute IP addresses for node names in command
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 node.sendCmd( rest )
lg.warn( '*** %s: running %s\n' % ( node.name, rest ) ) while True:
node.sendCmd( rest ) try:
while True: done, data = node.monitor()
try: lg.info( '%s\n' % data )
done, data = node.monitor() if done:
lg.info( '%s\n' % data ) break
if done: except KeyboardInterrupt:
break node.sendInt()
except KeyboardInterrupt: else:
node.sendInt() self.stdout.write('*** Unknown syntax: %s\n'%line)
elif first == '':
pass # Re-enable pylint "Unused argument: 'arg's'" messages.
elif first in [ 'exit', 'quit' ]: # pylint: enable-msg=W0613
break
elif first == '?':
self.help( rest )
else:
lg.error( 'CLI: unknown node or command: < %s >\n' % first )
#lg.info( '*** CLI: command complete\n' )
return 'exited by user command'
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment