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 @@
For now, we recommend limiting CLI use to non-interactive
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
import sys
from cmd import Cmd
from mininet.log import lg
class CLI( object ):
class CLI( Cmd ):
"Simple command-line interface to talk to nodes."
cmds = [ '?', 'help', 'nodes', 'net', 'sh', 'pingAll', 'exit',
'pingPair', 'iperf', 'iperfUdp', 'intfs', 'dump' ]
prompt = 'mininet> '
def __init__( self, mininet ):
self.mn = mininet
......@@ -57,24 +54,27 @@ def __init__( self, mininet ):
for cname, cnode in self.mn.controllers.iteritems():
self.nodemap[ cname ] = cnode
self.nodelist = self.nodemap.values()
self.run()
Cmd.__init__( self )
lg.warn( '*** Starting CLI:\n' )
self.cmdloop()
# Disable pylint "Unused argument: 'arg's'" messages.
# Each CLI function needs the same interface.
# pylint: disable-msg=W0613
# Commands
def help( self, args ):
"Semi-useful help for CLI."
helpStr = ( 'Available commands are:' + str( self.cmds ) + '\n'
'You may also send a command to a node using:\n'
def do_help(self, arg):
"Describe available CLI commands."
Cmd.do_help(self, arg)
helpStr = ( 'You may also send a command to a node using:\n'
' <node> command {args}\n'
'For example:\n'
' mininet> h0 ifconfig\n'
'\n'
'The interpreter automatically substitutes IP '
'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'
'should work.\n'
'\n\n'
......@@ -82,14 +82,14 @@ def help( self, args ):
'so please limit commands to ones that do not\n'
'require user interaction and will terminate\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."
nodes = ' '.join( [ node.name for node in sorted( self.nodelist ) ] )
lg.info( 'available nodes are: \n%s\n' % nodes )
def net( self, args ):
def do_net( self, args ):
"List network connections."
for switchDpid in self.mn.topo.switches():
switch = self.mn.nodes[ switchDpid ]
......@@ -99,81 +99,76 @@ def net( self, args ):
lg.info( ' %s' % node.name )
lg.info( '\n' )
def sh( self, args ):
def do_sh( self, args ):
"Run an external shell command"
call( [ 'sh', '-c' ] + args )
def pingAll( self, args ):
def do_pingAll( self, args ):
"Ping between all hosts."
self.mn.pingAll()
def pingPair( self, args ):
def do_pingPair( self, args ):
"Ping between first two hosts, useful for testing."
self.mn.pingPair()
def iperf( self, args ):
def do_iperf( self, args ):
"Simple iperf TCP test between two hosts."
self.mn.iperf()
def iperfUdp( self, args ):
def do_iperfUdp( self, args ):
"Simple iperf UDP test between two hosts."
udpBw = args[ 0 ] if len( args ) else '10M'
self.mn.iperfUdp( udpBw )
def intfs( self, args ):
def do_intfs( self, args ):
"List interfaces."
for node in self.mn.nodes.values():
lg.info( '%s: %s\n' % ( node.name, ' '.join( node.intfs ) ) )
def dump( self, args ):
def do_dump( self, args ):
"Dump node info."
for node in self.mn.nodes.values():
lg.info( '%s\n' % node )
# Re-enable pylint "Unused argument: 'arg's'" messages.
# pylint: enable-msg=W0613
def do_exit( self, args ):
"Exit"
return 'exited by user command'
def run( self ):
"Read and execute commands."
lg.warn( '*** Starting CLI:\n' )
while True:
lg.warn( 'mininet> ' )
inputLine = sys.stdin.readline()
if inputLine == '':
break
if inputLine[ -1 ] == '\n':
inputLine = inputLine[ :-1 ]
cmd = inputLine.split( ' ' )
first = cmd[ 0 ]
rest = cmd[ 1: ]
if first in self.cmds and hasattr( self, first ):
getattr( self, first )( rest )
elif first in self.nodemap and rest != []:
node = self.nodemap[ first ]
# Substitute IP addresses for node names in command
rest = [ self.nodemap[ arg ].IP()
def do_quit( self, args ):
"Exit"
self.do_exit()
def default( self, line ):
"""Called on an input line when the command prefix is not recognized.
Overridden to run shell commands when a node is the first CLI argument.
Past the first CLI argument, node names are automatically replaced with
corresponding IP addrs.
"""
first, args, line = self.parseline( line )
if len(args) > 0 and args[ -1 ] == '\n':
args = args[ :-1 ]
rest = args.split( ' ' )
if first in self.nodemap:
node = self.nodemap[ first ]
# Substitute IP addresses for node names in command
rest = [ self.nodemap[ arg ].IP()
if arg in self.nodemap else arg
for arg in rest ]
rest = ' '.join( rest )
# Interactive commands don't work yet, and
# there are still issues with control-c
lg.warn( '*** %s: running %s\n' % ( node.name, rest ) )
node.sendCmd( rest )
while True:
try:
done, data = node.monitor()
lg.info( '%s\n' % data )
if done:
break
except KeyboardInterrupt:
node.sendInt()
elif first == '':
pass
elif first in [ 'exit', 'quit' ]:
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'
rest = ' '.join( rest )
# Run cmd on node:
node.sendCmd( rest )
while True:
try:
done, data = node.monitor()
lg.info( '%s\n' % data )
if done:
break
except KeyboardInterrupt:
node.sendInt()
else:
self.stdout.write('*** Unknown syntax: %s\n'%line)
# Re-enable pylint "Unused argument: 'arg's'" messages.
# pylint: enable-msg=W0613
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