Skip to content
Snippets Groups Projects
Commit 8856d284 authored by Bob Lantz's avatar Bob Lantz
Browse files

Fix CLI commands.

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