Fix CLI commands.
This commit is contained in:
@@ -208,7 +208,7 @@ class MininetRunner( object ):
|
||||
'remote controller' )
|
||||
opts.add_option( '--innamespace', action='store_true',
|
||||
default=False, help='sw and ctrl in namespace?' )
|
||||
opts.add_option( '--listenport', type='int', default=6634,
|
||||
opts.add_option( '--listenport', type='int', default=6635,
|
||||
help='base port for passive switch listening' )
|
||||
opts.add_option( '--nolistenport', action='store_true',
|
||||
default=False, help="don't use passive listening port")
|
||||
|
||||
+18
-15
@@ -109,15 +109,20 @@ class CLI( Cmd ):
|
||||
nodes = ' '.join( [ node.name for node in sorted( self.nodelist ) ] )
|
||||
output( 'available nodes are: \n%s\n' % nodes )
|
||||
|
||||
@staticmethod
|
||||
def dump_connections( node ):
|
||||
"Helper method: dump connections to node"
|
||||
for intf in node.intfList():
|
||||
if intf.link:
|
||||
intfs = [ intf.link.intf1, intf.link.intf2 ]
|
||||
intfs.remove( intf )
|
||||
output( ' %s' % intfs[ 0 ].node )
|
||||
|
||||
def do_net( self, _line ):
|
||||
"List network connections."
|
||||
for switch in self.mn.switches:
|
||||
output( switch.name, '<->' )
|
||||
for intf in switch.intfs.values():
|
||||
# Ugly, but pylint wants it
|
||||
name = switch.connection.get( intf,
|
||||
( None, 'Unknown ' ) )[ 1 ]
|
||||
output( ' %s' % name )
|
||||
for node in self.nodelist:
|
||||
output( node.name, '<->' )
|
||||
self.dump_connections( node )
|
||||
output( '\n' )
|
||||
|
||||
def do_sh( self, line ):
|
||||
@@ -195,12 +200,12 @@ class CLI( Cmd ):
|
||||
"List interfaces."
|
||||
for node in self.nodelist:
|
||||
output( '%s: %s\n' %
|
||||
( node.name, ' '.join( sorted( node.intfs.values() ) ) ) )
|
||||
( node.name, ','.join( node.intfNames() ) ) )
|
||||
|
||||
def do_dump( self, _line ):
|
||||
"Dump node info."
|
||||
for node in self.nodelist:
|
||||
output( '%s\n' % node )
|
||||
output( '%s\n' % repr( node ) )
|
||||
|
||||
def do_link( self, line ):
|
||||
"Bring link(s) between two nodes up or down."
|
||||
@@ -275,16 +280,12 @@ class CLI( Cmd ):
|
||||
def do_dpctl( self, line ):
|
||||
"Run dpctl command on all switches."
|
||||
args = line.split()
|
||||
if len(args) == 0:
|
||||
if len(args) < 1:
|
||||
error( 'usage: dpctl command [arg1] [arg2] ...\n' )
|
||||
return
|
||||
if not self.mn.listenPort:
|
||||
error( "can't run dpctl w/no passive listening port\n")
|
||||
return
|
||||
for sw in self.mn.switches:
|
||||
output( '*** ' + sw.name + ' ' + ('-' * 72) + '\n' )
|
||||
output( sw.cmd( 'dpctl ' + ' '.join(args) +
|
||||
' tcp:127.0.0.1:%i' % sw.listenPort ) )
|
||||
output( sw.dpctl( *args ) )
|
||||
|
||||
def default( self, line ):
|
||||
"""Called on an input line when the command prefix is not recognized.
|
||||
@@ -293,6 +294,8 @@ class CLI( Cmd ):
|
||||
corresponding IP addrs."""
|
||||
|
||||
first, args, line = self.parseline( line )
|
||||
if not args:
|
||||
return
|
||||
if args and len(args) > 0 and args[ -1 ] == '\n':
|
||||
args = args[ :-1 ]
|
||||
rest = args.split( ' ' )
|
||||
|
||||
@@ -104,6 +104,14 @@ class Intf( object ):
|
||||
self.ifconfig( 'up' )
|
||||
return "UP" in self.ifconfig()
|
||||
|
||||
def rename( self, newname ):
|
||||
"Rename interface"
|
||||
self.ifconfig( 'down' )
|
||||
result = self.cmd( 'ip link set', self.name, 'name', newname )
|
||||
self.name = newname
|
||||
self.ifconfig( 'up' )
|
||||
return result
|
||||
|
||||
# The reason why we configure things in this way is so
|
||||
# That the parameters can be listed and documented in
|
||||
# the config method.
|
||||
@@ -145,6 +153,8 @@ class Intf( object ):
|
||||
self.setParam( r, 'setIP', ip=ip )
|
||||
self.setParam( r, 'isUp', up=up )
|
||||
self.setParam( r, 'ifconfig', ifconfig=ifconfig )
|
||||
self.updateIP()
|
||||
self.updateMAC()
|
||||
return r
|
||||
|
||||
def delete( self ):
|
||||
|
||||
+12
-8
@@ -484,10 +484,11 @@ class Mininet( object ):
|
||||
servout = ''
|
||||
while server.lastPid is None:
|
||||
servout += server.monitor()
|
||||
while 'Connected' not in client.cmd(
|
||||
'sh -c "echo A | telnet -e A %s 5001"' % server.IP()):
|
||||
output('waiting for iperf to start up...')
|
||||
sleep(.5)
|
||||
if l4Type == 'TCP':
|
||||
while 'Connected' not in client.cmd(
|
||||
'sh -c "echo A | telnet -e A %s 5001"' % server.IP()):
|
||||
output('waiting for iperf to start up...')
|
||||
sleep(.5)
|
||||
cliout = client.cmd( iperfArgs + '-t 5 -c ' + server.IP() + ' ' +
|
||||
bwArgs )
|
||||
debug( 'Client output: %s\n' % cliout )
|
||||
@@ -512,15 +513,18 @@ class Mininet( object ):
|
||||
elif dst not in self.nameToNode:
|
||||
error( 'dst not in network: %s\n' % dst )
|
||||
else:
|
||||
srcNode, dstNode = self.nameToNode[ src ], self.nameToNode[ dst ]
|
||||
connections = srcNode.connectionsTo( dstNode )
|
||||
if type( src ) is str:
|
||||
src = self.nameToNode[ src ]
|
||||
if type( dst ) is str:
|
||||
dst = self.nameToNode[ dst ]
|
||||
connections = src.connectionsTo( dst )
|
||||
if len( connections ) == 0:
|
||||
error( 'src and dst not connected: %s %s\n' % ( src, dst) )
|
||||
for srcIntf, dstIntf in connections:
|
||||
result = srcNode.cmd( 'ifconfig', srcIntf, status )
|
||||
result = srcIntf.ifconfig( status )
|
||||
if result:
|
||||
error( 'link src status change failed: %s\n' % result )
|
||||
result = dstNode.cmd( 'ifconfig', dstIntf, status )
|
||||
result = dstIntf.ifconfig( status )
|
||||
if result:
|
||||
error( 'link dst status change failed: %s\n' % result )
|
||||
|
||||
|
||||
+81
-43
@@ -54,7 +54,7 @@ from mininet.log import info, error, warn, debug
|
||||
from mininet.util import quietRun, errRun, errFail, moveIntf, isShellBuiltin
|
||||
from mininet.util import numCores
|
||||
from mininet.moduledeps import moduleDeps, pathCheck, OVS_KMOD, OF_KMOD, TUN
|
||||
from mininet.link import Link
|
||||
from mininet.link import Link, Intf
|
||||
|
||||
class Node( object ):
|
||||
"""A virtual network node is simply a shell in a network namespace.
|
||||
@@ -316,16 +316,19 @@ class Node( object ):
|
||||
else:
|
||||
return intf
|
||||
|
||||
def linksTo( self, node):
|
||||
"Return [ link1, link2...] for all links from self to node."
|
||||
def connectionsTo( self, node):
|
||||
"Return [ intf1, intf2... ] for all intfs that connect self to node."
|
||||
# We could optimize this if it is important
|
||||
links = []
|
||||
for intf in self.intfs:
|
||||
connections = []
|
||||
for intf in self.intfList():
|
||||
link = intf.link
|
||||
nodes = ( link.intf1.node, link.intf2.node )
|
||||
if self in nodes and node in nodes:
|
||||
links.append( link )
|
||||
return links
|
||||
if link:
|
||||
node1, node2 = link.intf1.node, link.intf2.node
|
||||
if node1 == self and node2 == node:
|
||||
connections += [ ( intf, link.intf2 ) ]
|
||||
elif node1 == node and node2 == self:
|
||||
connections += [ ( intf, link.intf1 ) ]
|
||||
return connections
|
||||
|
||||
def deleteIntfs( self ):
|
||||
"Delete all of our interfaces."
|
||||
@@ -418,8 +421,8 @@ class Node( object ):
|
||||
results[ name ] = result
|
||||
return result
|
||||
|
||||
def config( self, mac=None, ip=None, ifconfig=None,
|
||||
defaultRoute=None, **_params ):
|
||||
def config( self, mac=None, ip=None,
|
||||
defaultRoute=None, lo='up', **_params ):
|
||||
"""Configure Node according to (optional) parameters:
|
||||
mac: MAC address for default interface
|
||||
ip: IP address for default interface
|
||||
@@ -432,8 +435,9 @@ class Node( object ):
|
||||
r = {}
|
||||
self.setParam( r, 'setMAC', mac=mac )
|
||||
self.setParam( r, 'setIP', ip=ip )
|
||||
self.setParam( r, 'ifconfig', ifconfig=ifconfig )
|
||||
self.setParam( r, 'defaultRoute', defaultRoute=defaultRoute )
|
||||
# This should be examined
|
||||
self.cmd( 'ifconfig lo ' + lo )
|
||||
return r
|
||||
|
||||
def configDefault( self, **moreParams ):
|
||||
@@ -457,9 +461,16 @@ class Node( object ):
|
||||
"The names of our interfaces sorted by port number"
|
||||
return [ str( i ) for i in self.intfList() ]
|
||||
|
||||
def __repr__( self ):
|
||||
"More informative string representation"
|
||||
intfs = ( ','.join( [ '%s:%s' % ( i.name, i.IP() )
|
||||
for i in self.intfList() ] ) )
|
||||
return '<%s %s: %s pid=%s> ' % (
|
||||
self.__class__.__name__, self.name, intfs, self.pid )
|
||||
|
||||
def __str__( self ):
|
||||
return '%s: IP=%s intfs=%s pid=%s' % (
|
||||
self.name, self.IP(), ','.join( self.intfNames() ), self.pid )
|
||||
"Abbreviated string representation"
|
||||
return self.name
|
||||
|
||||
# Automatic class setup support
|
||||
|
||||
@@ -634,9 +645,8 @@ class Switch( Node ):
|
||||
self.dpid = dpid if dpid else self.defaultDpid()
|
||||
self.opts = opts
|
||||
self.listenPort = listenPort
|
||||
if self.listenPort:
|
||||
self.opts += ' --listen=ptcp:%i ' % self.listenPort
|
||||
self.controlIntf = None
|
||||
if not self.inNamespace:
|
||||
self.controlIntf = Intf( 'lo', self )
|
||||
|
||||
def defaultDpid( self ):
|
||||
"Derive dpid from switch name, s1 -> 1"
|
||||
@@ -646,11 +656,11 @@ class Switch( Node ):
|
||||
return dpid
|
||||
|
||||
def defaultIntf( self ):
|
||||
"Return control interface, if any"
|
||||
if not self.inNamespace:
|
||||
error( "error: tried to access control interface of "
|
||||
" switch %s in root namespace" % self.name )
|
||||
return self.controlIntf
|
||||
"Return control interface"
|
||||
if self.controlIntf:
|
||||
return self.controlIntf
|
||||
else:
|
||||
return Node.defaultIntf( self )
|
||||
|
||||
def sendCmd( self, *cmd, **kwargs ):
|
||||
"""Send command to Node.
|
||||
@@ -662,6 +672,13 @@ class Switch( Node ):
|
||||
error( '*** Error: %s has execed and cannot accept commands' %
|
||||
self.name )
|
||||
|
||||
def __repr__( self ):
|
||||
"More informative string representation"
|
||||
intfs = ( ','.join( [ '%s:%s' % ( i.name, i.IP() )
|
||||
for i in self.intfList() ] ) )
|
||||
return '<%s %s: %s pid=%s> ' % (
|
||||
self.__class__.__name__, self.name, intfs, self.pid )
|
||||
|
||||
class UserSwitch( Switch ):
|
||||
"User-space switch."
|
||||
|
||||
@@ -671,6 +688,8 @@ class UserSwitch( Switch ):
|
||||
Switch.__init__( self, name, **kwargs )
|
||||
pathCheck( 'ofdatapath', 'ofprotocol',
|
||||
moduleName='the OpenFlow reference user switch (openflow.org)' )
|
||||
if self.listenPort:
|
||||
self.opts += ' --listen=ptcp:%i ' % self.listenPort
|
||||
|
||||
@classmethod
|
||||
def setup( cls ):
|
||||
@@ -678,6 +697,13 @@ class UserSwitch( Switch ):
|
||||
if not os.path.exists( '/dev/net/tun' ):
|
||||
moduleDeps( add=TUN )
|
||||
|
||||
def dpctl( self, *args ):
|
||||
"Run dpctl command"
|
||||
if not self.listenPort:
|
||||
return "can't run dpctl w/no passive listening port"
|
||||
return self.cmdPrint( 'dpctl ' + ' '.join( args ) +
|
||||
' tcp:127.0.0.1:%i' % self.listenPort )
|
||||
|
||||
def start( self, controllers ):
|
||||
"""Start OpenFlow reference user datapath.
|
||||
Log to /tmp/sN-{ofd,ofp}.log.
|
||||
@@ -736,7 +762,7 @@ class OVSLegacyKernelSwitch( Switch ):
|
||||
quietRun( 'ifconfig lo up' )
|
||||
# Delete local datapath if it exists;
|
||||
# then create a new one monitoring the given interfaces
|
||||
quietRun( 'ovs-dpctl del-dp ' + self.dp )
|
||||
self.cmd( 'ovs-dpctl del-dp ' + self.dp )
|
||||
self.cmd( 'ovs-dpctl add-dp ' + self.dp )
|
||||
ports = sorted( self.ports.values() )
|
||||
if len( ports ) != ports[ -1 ] + 1 - self.portBase:
|
||||
@@ -768,15 +794,6 @@ class OVSSwitch( Switch ):
|
||||
name: name for switch
|
||||
defaultMAC: default MAC as unsigned int; random value if None"""
|
||||
Switch.__init__( self, name, **params )
|
||||
# self.dp is the text name for the datapath that
|
||||
# we use for ovs-vsctl. This is different from the
|
||||
# dpid, which is a 64-bit numerical value used by
|
||||
# the openflow protocol.
|
||||
self.dp = name
|
||||
if self.inNamespace:
|
||||
error( "OVSSwitch currently only works"
|
||||
" in the root namespace.\n" )
|
||||
exit( 1 )
|
||||
|
||||
@classmethod
|
||||
def setup( cls ):
|
||||
@@ -796,32 +813,47 @@ class OVSSwitch( Switch ):
|
||||
'"service openvswitch-switch start".\n' )
|
||||
exit( 1 )
|
||||
|
||||
def dpctl( self, *args ):
|
||||
"Run ovs-dpctl command"
|
||||
return self.cmd( 'ovs-dpctl', args[ 0 ], self, *args[ 1: ] )
|
||||
|
||||
def attach( self, intf ):
|
||||
"Connect a data port"
|
||||
self.cmd( 'ovs-vsctl add-port', self, intf )
|
||||
self.cmd( 'ifconfig', intf, 'up' )
|
||||
|
||||
def detach( self, intf ):
|
||||
"Disconnect a data port"
|
||||
self.cmd( 'ovs-vsctl del-port', self, intf )
|
||||
|
||||
def start( self, controllers ):
|
||||
"Start up a new OVS OpenFlow switch using ovs-vsctl"
|
||||
if self.inNamespace:
|
||||
raise Exception(
|
||||
raise Exception(
|
||||
'OVS kernel switch does not work in a namespace' )
|
||||
# We should probably call config instead, but this
|
||||
# requires some rethinking...
|
||||
self.cmd( 'ifconfig lo up' )
|
||||
# Annoyingly, --if-exists option seems not to work
|
||||
self.cmd( 'ovs-vsctl del-br ', self.dp )
|
||||
self.cmd( 'ovs-vsctl add-br', self.dp )
|
||||
self.cmd( 'ovs-vsctl set-fail-mode', self.dp, 'secure' )
|
||||
ports = sorted( self.ports.values() )
|
||||
intfs = [ self.intfs[ port ] for port in ports ]
|
||||
self.cmd( 'ovs-vsctl del-br', self )
|
||||
self.cmd( 'ovs-vsctl add-br', self )
|
||||
self.cmd( 'ovs-vsctl set-fail-mode', self, 'secure' )
|
||||
# XXX: Ugly check - we should probably fix this!
|
||||
ports = sorted( self.ports.values() )
|
||||
if ports and ( len( ports ) != ports[ -1 ] + 1 - self.portBase ):
|
||||
raise Exception( 'only contiguous, one-indexed port ranges '
|
||||
'supported: %s' % self.intfs )
|
||||
for intf in intfs:
|
||||
self.cmd( 'ovs-vsctl add-port', self.dp, intf )
|
||||
self.cmd( 'ifconfig', intf, 'up' )
|
||||
for intf in self.intfList():
|
||||
if not intf.IP():
|
||||
self.attach( intf )
|
||||
# Add controllers
|
||||
clist = ','.join( [ 'tcp:%s:%d' % ( c.IP(), c.port )
|
||||
for c in controllers ] )
|
||||
self.cmd( 'ovs-vsctl set-controller', self.dp, clist )
|
||||
self.cmd( 'ovs-vsctl set-controller', self, clist )
|
||||
|
||||
def stop( self ):
|
||||
"Terminate OVS switch."
|
||||
self.cmd( 'ovs-vsctl del-br', self.dp )
|
||||
self.cmd( 'ovs-vsctl del-br', self )
|
||||
|
||||
OVSKernelSwitch = OVSSwitch
|
||||
|
||||
@@ -865,6 +897,12 @@ class Controller( Node ):
|
||||
ip = self.ip
|
||||
return ip
|
||||
|
||||
def __repr__( self ):
|
||||
"More informative string representation"
|
||||
return '<%s %s: %s:%s pid=%s> ' % (
|
||||
self.__class__.__name__, self.name,
|
||||
self.IP(), self.port, self.pid )
|
||||
|
||||
|
||||
class OVSController( Controller ):
|
||||
"Open vSwitch controller"
|
||||
|
||||
Reference in New Issue
Block a user