Skip to content

Commit 8856d28

Browse files
committed
Fix CLI commands.
1 parent 14ff3ad commit 8856d28

5 files changed

Lines changed: 122 additions & 67 deletions

File tree

bin/mn

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -208,7 +208,7 @@ class MininetRunner( object ):
208208
'remote controller' )
209209
opts.add_option( '--innamespace', action='store_true',
210210
default=False, help='sw and ctrl in namespace?' )
211-
opts.add_option( '--listenport', type='int', default=6634,
211+
opts.add_option( '--listenport', type='int', default=6635,
212212
help='base port for passive switch listening' )
213213
opts.add_option( '--nolistenport', action='store_true',
214214
default=False, help="don't use passive listening port")

mininet/cli.py

Lines changed: 18 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -109,15 +109,20 @@ def do_nodes( self, _line ):
109109
nodes = ' '.join( [ node.name for node in sorted( self.nodelist ) ] )
110110
output( 'available nodes are: \n%s\n' % nodes )
111111

112+
@staticmethod
113+
def dump_connections( node ):
114+
"Helper method: dump connections to node"
115+
for intf in node.intfList():
116+
if intf.link:
117+
intfs = [ intf.link.intf1, intf.link.intf2 ]
118+
intfs.remove( intf )
119+
output( ' %s' % intfs[ 0 ].node )
120+
112121
def do_net( self, _line ):
113122
"List network connections."
114-
for switch in self.mn.switches:
115-
output( switch.name, '<->' )
116-
for intf in switch.intfs.values():
117-
# Ugly, but pylint wants it
118-
name = switch.connection.get( intf,
119-
( None, 'Unknown ' ) )[ 1 ]
120-
output( ' %s' % name )
123+
for node in self.nodelist:
124+
output( node.name, '<->' )
125+
self.dump_connections( node )
121126
output( '\n' )
122127

123128
def do_sh( self, line ):
@@ -195,12 +200,12 @@ def do_intfs( self, _line ):
195200
"List interfaces."
196201
for node in self.nodelist:
197202
output( '%s: %s\n' %
198-
( node.name, ' '.join( sorted( node.intfs.values() ) ) ) )
203+
( node.name, ','.join( node.intfNames() ) ) )
199204

200205
def do_dump( self, _line ):
201206
"Dump node info."
202207
for node in self.nodelist:
203-
output( '%s\n' % node )
208+
output( '%s\n' % repr( node ) )
204209

205210
def do_link( self, line ):
206211
"Bring link(s) between two nodes up or down."
@@ -275,16 +280,12 @@ def do_source( self, line ):
275280
def do_dpctl( self, line ):
276281
"Run dpctl command on all switches."
277282
args = line.split()
278-
if len(args) == 0:
283+
if len(args) < 1:
279284
error( 'usage: dpctl command [arg1] [arg2] ...\n' )
280285
return
281-
if not self.mn.listenPort:
282-
error( "can't run dpctl w/no passive listening port\n")
283-
return
284286
for sw in self.mn.switches:
285287
output( '*** ' + sw.name + ' ' + ('-' * 72) + '\n' )
286-
output( sw.cmd( 'dpctl ' + ' '.join(args) +
287-
' tcp:127.0.0.1:%i' % sw.listenPort ) )
288+
output( sw.dpctl( *args ) )
288289

289290
def default( self, line ):
290291
"""Called on an input line when the command prefix is not recognized.
@@ -293,6 +294,8 @@ def default( self, line ):
293294
corresponding IP addrs."""
294295

295296
first, args, line = self.parseline( line )
297+
if not args:
298+
return
296299
if args and len(args) > 0 and args[ -1 ] == '\n':
297300
args = args[ :-1 ]
298301
rest = args.split( ' ' )

mininet/link.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,14 @@ def isUp( self, setUp=False ):
104104
self.ifconfig( 'up' )
105105
return "UP" in self.ifconfig()
106106

107+
def rename( self, newname ):
108+
"Rename interface"
109+
self.ifconfig( 'down' )
110+
result = self.cmd( 'ip link set', self.name, 'name', newname )
111+
self.name = newname
112+
self.ifconfig( 'up' )
113+
return result
114+
107115
# The reason why we configure things in this way is so
108116
# That the parameters can be listed and documented in
109117
# the config method.
@@ -145,6 +153,8 @@ def config( self, mac=None, ip=None, ifconfig=None,
145153
self.setParam( r, 'setIP', ip=ip )
146154
self.setParam( r, 'isUp', up=up )
147155
self.setParam( r, 'ifconfig', ifconfig=ifconfig )
156+
self.updateIP()
157+
self.updateMAC()
148158
return r
149159

150160
def delete( self ):

mininet/net.py

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -484,10 +484,11 @@ def iperf( self, hosts=None, l4Type='TCP', udpBw='10M' ):
484484
servout = ''
485485
while server.lastPid is None:
486486
servout += server.monitor()
487-
while 'Connected' not in client.cmd(
488-
'sh -c "echo A | telnet -e A %s 5001"' % server.IP()):
489-
output('waiting for iperf to start up...')
490-
sleep(.5)
487+
if l4Type == 'TCP':
488+
while 'Connected' not in client.cmd(
489+
'sh -c "echo A | telnet -e A %s 5001"' % server.IP()):
490+
output('waiting for iperf to start up...')
491+
sleep(.5)
491492
cliout = client.cmd( iperfArgs + '-t 5 -c ' + server.IP() + ' ' +
492493
bwArgs )
493494
debug( 'Client output: %s\n' % cliout )
@@ -512,15 +513,18 @@ def configLinkStatus( self, src, dst, status ):
512513
elif dst not in self.nameToNode:
513514
error( 'dst not in network: %s\n' % dst )
514515
else:
515-
srcNode, dstNode = self.nameToNode[ src ], self.nameToNode[ dst ]
516-
connections = srcNode.connectionsTo( dstNode )
516+
if type( src ) is str:
517+
src = self.nameToNode[ src ]
518+
if type( dst ) is str:
519+
dst = self.nameToNode[ dst ]
520+
connections = src.connectionsTo( dst )
517521
if len( connections ) == 0:
518522
error( 'src and dst not connected: %s %s\n' % ( src, dst) )
519523
for srcIntf, dstIntf in connections:
520-
result = srcNode.cmd( 'ifconfig', srcIntf, status )
524+
result = srcIntf.ifconfig( status )
521525
if result:
522526
error( 'link src status change failed: %s\n' % result )
523-
result = dstNode.cmd( 'ifconfig', dstIntf, status )
527+
result = dstIntf.ifconfig( status )
524528
if result:
525529
error( 'link dst status change failed: %s\n' % result )
526530

mininet/node.py

Lines changed: 81 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@
5454
from mininet.util import quietRun, errRun, errFail, moveIntf, isShellBuiltin
5555
from mininet.util import numCores
5656
from mininet.moduledeps import moduleDeps, pathCheck, OVS_KMOD, OF_KMOD, TUN
57-
from mininet.link import Link
57+
from mininet.link import Link, Intf
5858

5959
class Node( object ):
6060
"""A virtual network node is simply a shell in a network namespace.
@@ -316,16 +316,19 @@ def intf( self, intf='' ):
316316
else:
317317
return intf
318318

319-
def linksTo( self, node):
320-
"Return [ link1, link2...] for all links from self to node."
319+
def connectionsTo( self, node):
320+
"Return [ intf1, intf2... ] for all intfs that connect self to node."
321321
# We could optimize this if it is important
322-
links = []
323-
for intf in self.intfs:
322+
connections = []
323+
for intf in self.intfList():
324324
link = intf.link
325-
nodes = ( link.intf1.node, link.intf2.node )
326-
if self in nodes and node in nodes:
327-
links.append( link )
328-
return links
325+
if link:
326+
node1, node2 = link.intf1.node, link.intf2.node
327+
if node1 == self and node2 == node:
328+
connections += [ ( intf, link.intf2 ) ]
329+
elif node1 == node and node2 == self:
330+
connections += [ ( intf, link.intf1 ) ]
331+
return connections
329332

330333
def deleteIntfs( self ):
331334
"Delete all of our interfaces."
@@ -418,8 +421,8 @@ def setParam( self, results, method, **param ):
418421
results[ name ] = result
419422
return result
420423

421-
def config( self, mac=None, ip=None, ifconfig=None,
422-
defaultRoute=None, **_params ):
424+
def config( self, mac=None, ip=None,
425+
defaultRoute=None, lo='up', **_params ):
423426
"""Configure Node according to (optional) parameters:
424427
mac: MAC address for default interface
425428
ip: IP address for default interface
@@ -432,8 +435,9 @@ def config( self, mac=None, ip=None, ifconfig=None,
432435
r = {}
433436
self.setParam( r, 'setMAC', mac=mac )
434437
self.setParam( r, 'setIP', ip=ip )
435-
self.setParam( r, 'ifconfig', ifconfig=ifconfig )
436438
self.setParam( r, 'defaultRoute', defaultRoute=defaultRoute )
439+
# This should be examined
440+
self.cmd( 'ifconfig lo ' + lo )
437441
return r
438442

439443
def configDefault( self, **moreParams ):
@@ -457,9 +461,16 @@ def intfNames( self ):
457461
"The names of our interfaces sorted by port number"
458462
return [ str( i ) for i in self.intfList() ]
459463

464+
def __repr__( self ):
465+
"More informative string representation"
466+
intfs = ( ','.join( [ '%s:%s' % ( i.name, i.IP() )
467+
for i in self.intfList() ] ) )
468+
return '<%s %s: %s pid=%s> ' % (
469+
self.__class__.__name__, self.name, intfs, self.pid )
470+
460471
def __str__( self ):
461-
return '%s: IP=%s intfs=%s pid=%s' % (
462-
self.name, self.IP(), ','.join( self.intfNames() ), self.pid )
472+
"Abbreviated string representation"
473+
return self.name
463474

464475
# Automatic class setup support
465476

@@ -634,9 +645,8 @@ def __init__( self, name, dpid=None, opts='', listenPort=None, **params):
634645
self.dpid = dpid if dpid else self.defaultDpid()
635646
self.opts = opts
636647
self.listenPort = listenPort
637-
if self.listenPort:
638-
self.opts += ' --listen=ptcp:%i ' % self.listenPort
639-
self.controlIntf = None
648+
if not self.inNamespace:
649+
self.controlIntf = Intf( 'lo', self )
640650

641651
def defaultDpid( self ):
642652
"Derive dpid from switch name, s1 -> 1"
@@ -646,11 +656,11 @@ def defaultDpid( self ):
646656
return dpid
647657

648658
def defaultIntf( self ):
649-
"Return control interface, if any"
650-
if not self.inNamespace:
651-
error( "error: tried to access control interface of "
652-
" switch %s in root namespace" % self.name )
653-
return self.controlIntf
659+
"Return control interface"
660+
if self.controlIntf:
661+
return self.controlIntf
662+
else:
663+
return Node.defaultIntf( self )
654664

655665
def sendCmd( self, *cmd, **kwargs ):
656666
"""Send command to Node.
@@ -662,6 +672,13 @@ def sendCmd( self, *cmd, **kwargs ):
662672
error( '*** Error: %s has execed and cannot accept commands' %
663673
self.name )
664674

675+
def __repr__( self ):
676+
"More informative string representation"
677+
intfs = ( ','.join( [ '%s:%s' % ( i.name, i.IP() )
678+
for i in self.intfList() ] ) )
679+
return '<%s %s: %s pid=%s> ' % (
680+
self.__class__.__name__, self.name, intfs, self.pid )
681+
665682
class UserSwitch( Switch ):
666683
"User-space switch."
667684

@@ -671,13 +688,22 @@ def __init__( self, name, **kwargs ):
671688
Switch.__init__( self, name, **kwargs )
672689
pathCheck( 'ofdatapath', 'ofprotocol',
673690
moduleName='the OpenFlow reference user switch (openflow.org)' )
691+
if self.listenPort:
692+
self.opts += ' --listen=ptcp:%i ' % self.listenPort
674693

675694
@classmethod
676695
def setup( cls ):
677696
"Ensure any dependencies are loaded; if not, try to load them."
678697
if not os.path.exists( '/dev/net/tun' ):
679698
moduleDeps( add=TUN )
680699

700+
def dpctl( self, *args ):
701+
"Run dpctl command"
702+
if not self.listenPort:
703+
return "can't run dpctl w/no passive listening port"
704+
return self.cmdPrint( 'dpctl ' + ' '.join( args ) +
705+
' tcp:127.0.0.1:%i' % self.listenPort )
706+
681707
def start( self, controllers ):
682708
"""Start OpenFlow reference user datapath.
683709
Log to /tmp/sN-{ofd,ofp}.log.
@@ -736,7 +762,7 @@ def start( self, controllers ):
736762
quietRun( 'ifconfig lo up' )
737763
# Delete local datapath if it exists;
738764
# then create a new one monitoring the given interfaces
739-
quietRun( 'ovs-dpctl del-dp ' + self.dp )
765+
self.cmd( 'ovs-dpctl del-dp ' + self.dp )
740766
self.cmd( 'ovs-dpctl add-dp ' + self.dp )
741767
ports = sorted( self.ports.values() )
742768
if len( ports ) != ports[ -1 ] + 1 - self.portBase:
@@ -768,15 +794,6 @@ def __init__( self, name, **params ):
768794
name: name for switch
769795
defaultMAC: default MAC as unsigned int; random value if None"""
770796
Switch.__init__( self, name, **params )
771-
# self.dp is the text name for the datapath that
772-
# we use for ovs-vsctl. This is different from the
773-
# dpid, which is a 64-bit numerical value used by
774-
# the openflow protocol.
775-
self.dp = name
776-
if self.inNamespace:
777-
error( "OVSSwitch currently only works"
778-
" in the root namespace.\n" )
779-
exit( 1 )
780797

781798
@classmethod
782799
def setup( cls ):
@@ -796,32 +813,47 @@ def setup( cls ):
796813
'"service openvswitch-switch start".\n' )
797814
exit( 1 )
798815

816+
def dpctl( self, *args ):
817+
"Run ovs-dpctl command"
818+
return self.cmd( 'ovs-dpctl', args[ 0 ], self, *args[ 1: ] )
819+
820+
def attach( self, intf ):
821+
"Connect a data port"
822+
self.cmd( 'ovs-vsctl add-port', self, intf )
823+
self.cmd( 'ifconfig', intf, 'up' )
824+
825+
def detach( self, intf ):
826+
"Disconnect a data port"
827+
self.cmd( 'ovs-vsctl del-port', self, intf )
828+
799829
def start( self, controllers ):
800830
"Start up a new OVS OpenFlow switch using ovs-vsctl"
801831
if self.inNamespace:
802-
raise Exception(
832+
raise Exception(
803833
'OVS kernel switch does not work in a namespace' )
834+
# We should probably call config instead, but this
835+
# requires some rethinking...
836+
self.cmd( 'ifconfig lo up' )
804837
# Annoyingly, --if-exists option seems not to work
805-
self.cmd( 'ovs-vsctl del-br ', self.dp )
806-
self.cmd( 'ovs-vsctl add-br', self.dp )
807-
self.cmd( 'ovs-vsctl set-fail-mode', self.dp, 'secure' )
808-
ports = sorted( self.ports.values() )
809-
intfs = [ self.intfs[ port ] for port in ports ]
838+
self.cmd( 'ovs-vsctl del-br', self )
839+
self.cmd( 'ovs-vsctl add-br', self )
840+
self.cmd( 'ovs-vsctl set-fail-mode', self, 'secure' )
810841
# XXX: Ugly check - we should probably fix this!
842+
ports = sorted( self.ports.values() )
811843
if ports and ( len( ports ) != ports[ -1 ] + 1 - self.portBase ):
812844
raise Exception( 'only contiguous, one-indexed port ranges '
813845
'supported: %s' % self.intfs )
814-
for intf in intfs:
815-
self.cmd( 'ovs-vsctl add-port', self.dp, intf )
816-
self.cmd( 'ifconfig', intf, 'up' )
846+
for intf in self.intfList():
847+
if not intf.IP():
848+
self.attach( intf )
817849
# Add controllers
818850
clist = ','.join( [ 'tcp:%s:%d' % ( c.IP(), c.port )
819851
for c in controllers ] )
820-
self.cmd( 'ovs-vsctl set-controller', self.dp, clist )
852+
self.cmd( 'ovs-vsctl set-controller', self, clist )
821853

822854
def stop( self ):
823855
"Terminate OVS switch."
824-
self.cmd( 'ovs-vsctl del-br', self.dp )
856+
self.cmd( 'ovs-vsctl del-br', self )
825857

826858
OVSKernelSwitch = OVSSwitch
827859

@@ -865,6 +897,12 @@ def IP( self, intf=None ):
865897
ip = self.ip
866898
return ip
867899

900+
def __repr__( self ):
901+
"More informative string representation"
902+
return '<%s %s: %s:%s pid=%s> ' % (
903+
self.__class__.__name__, self.name,
904+
self.IP(), self.port, self.pid )
905+
868906

869907
class OVSController( Controller ):
870908
"Open vSwitch controller"

0 commit comments

Comments
 (0)