Tweaks for better interactive command handling.

This commit is contained in:
Bob Lantz
2010-04-12 18:34:52 -07:00
parent f24e70a435
commit f800e5124d
+74 -25
View File
@@ -32,32 +32,45 @@ Bugs/limitations:
from subprocess import call from subprocess import call
from cmd import Cmd from cmd import Cmd
from os import isatty
from select import poll, select, POLLIN
from sys import stdin
from tty import setcbreak
from mininet.log import info, output, error from mininet.log import info, output, error
from mininet.term import makeTerms from mininet.term import makeTerms
from mininet.util import quietRun, isShellBuiltin
class CLI( Cmd ): class CLI( Cmd ):
"Simple command-line interface to talk to nodes." "Simple command-line interface to talk to nodes."
prompt = 'mininet> ' prompt = 'mininet> '
def __init__( self, mininet ): def __init__( self, mininet, stdin=stdin ):
self.mn = mininet self.mn = mininet
self.nodelist = self.mn.controllers + self.mn.switches + self.mn.hosts self.nodelist = self.mn.controllers + self.mn.switches + self.mn.hosts
self.nodemap = {} # map names to Node objects self.nodemap = {} # map names to Node objects
for node in self.nodelist: for node in self.nodelist:
self.nodemap[ node.name ] = node self.nodemap[ node.name ] = node
# Attempt to handle input
self.stdin = stdin
self.inPoller = poll()
self.inPoller.register( stdin )
Cmd.__init__( self ) Cmd.__init__( self )
info( '*** Starting CLI:\n' ) info( '*** Starting CLI:\n' )
while True: while True:
try: try:
# Make sure no nodes are still waiting
for node in self.nodelist:
while node.waiting:
node.sendInt()
node.monitor()
if self.isatty():
quietRun( 'stty sane' )
self.cmdloop() self.cmdloop()
break break
except KeyboardInterrupt: except KeyboardInterrupt:
info( 'Interrupt\n' ) output( '\nInterrupt\n' )
for node in self.nodelist:
waitForNode( node )
def emptyline( self ): def emptyline( self ):
"Don't repeat last command when you hit return." "Don't repeat last command when you hit return."
@@ -71,21 +84,25 @@ class CLI( Cmd ):
def do_help( self, args ): def do_help( self, args ):
"Describe available CLI commands." "Describe available CLI commands."
Cmd.do_help( self, args ) Cmd.do_help( self, args )
helpStr = ( 'You may also send a command to a node using:\n' helpStr = (
'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> h1 ifconfig\n'
'\n' '\n'
'The interpreter automatically substitutes IP ' 'The interpreter automatically substitutes IP addresses\n'
'addresses\n' 'for node names when a node is the first arg, so commands\n'
'for node names when a node is the first arg, so commands'
'like\n' 'like\n'
' mininet> h2 ping h3\n' ' mininet> h2 ping h3\n'
'should work.\n' 'should work.\n'
'\n' '\n'
'Interactive commands that require user input are ' 'Some character-oriented interactive commands require\n'
'not (yet) supported.\n\n' ) 'noecho, e.g.\n'
if args is "": ' mininet> noecho h2 vi foo.py\n'
'However, starting up an xterm/gterm is generally better:\n'
' mininet> xterm h2\n\n'
)
if args is '':
self.stdout.write( helpStr ) self.stdout.write( helpStr )
def do_nodes( self, args ): def do_nodes( self, args ):
@@ -117,11 +134,11 @@ class CLI( Cmd ):
if not result: if not result:
return return
elif isinstance( result, str ): elif isinstance( result, str ):
info( result + '\n' ) output( result + '\n' )
else: else:
info( repr( result ) + '\n' ) output( repr( result ) + '\n' )
except Exception, e: except Exception, e:
info( str( e ) + '\n' ) output( str( e ) + '\n' )
# pylint: enable-msg=W0703 # pylint: enable-msg=W0703
@@ -167,7 +184,7 @@ class CLI( Cmd ):
"Spawn xterm(s) for the given node(s)." "Spawn xterm(s) for the given node(s)."
args = args.split() args = args.split()
if not args: if not args:
info( 'usage: %s node1 node2 ...\n' % term ) error( 'usage: %s node1 node2 ...\n' % term )
else: else:
for arg in args: for arg in args:
if arg not in self.nodemap: if arg not in self.nodemap:
@@ -190,8 +207,20 @@ class CLI( Cmd ):
def do_EOF( self, args ): def do_EOF( self, args ):
"Exit" "Exit"
output( '\n' )
return self.do_exit( args ) return self.do_exit( args )
def isatty( self ):
"Is our standard input a tty?"
return isatty( self.stdin.fileno() )
def do_noecho( self, line ):
"Run an interactive command with echoing turned off."
if self.isatty():
quietRun( 'stty -echo' )
self.default( line )
# default() fixes tty
def default( self, line ): def default( self, line ):
"""Called on an input line when the command prefix is not recognized. """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. Overridden to run shell commands when a node is the first CLI argument.
@@ -211,22 +240,42 @@ class CLI( Cmd ):
for arg in rest ] for arg in rest ]
rest = ' '.join( rest ) rest = ' '.join( rest )
# Run cmd on node: # Run cmd on node:
node.sendCmd( rest, printPid=True ) node.sendCmd( rest )
waitForNode( node ) self.waitForNode( node, isShellBuiltin( first ) )
else: else:
self.stdout.write( '*** Unknown syntax: %s\n' % line ) self.stdout.write( '*** Unknown command: %s\n' % first )
# pylint: enable-msg=W0613,R0201 # 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
# This function may be a candidate for util.py def waitForNode( self, node, isShellBuiltin=False ):
def waitForNode( node ):
"Wait for a node to finish, and print its output." "Wait for a node to finish, and print its output."
while node.waiting: # Pollers
nodePoller = poll()
nodePoller.register( node.stdout )
bothPoller = poll()
bothPoller.register( self.stdin )
bothPoller.register( node.stdout )
if self.isatty():
# Buffer by character, so that interactive
# commands sort of work
quietRun( 'stty -icanon min 1' )
while True:
try: try:
bothPoller.poll()
if self.isReadable( self.inPoller ):
key = self.stdin.read( 1 )
node.write( key )
if self.isReadable( nodePoller ):
data = node.monitor() data = node.monitor()
info( '%s' % data ) output( '%s' % data )
if not node.waiting:
break
except KeyboardInterrupt: except KeyboardInterrupt:
node.sendInt() node.sendInt()