Move Node functions into their own file
Nodes include Switch, Host, and Controller; move these to a separate file. This file still could use some attention to hide private functions. Node seems like a primary class to extend, for adding stuff like Open vSwitch, so it could benefit from a simpler interface.
This commit is contained in:
@@ -13,7 +13,8 @@ from time import sleep
|
|||||||
|
|
||||||
from ripcord.topo import FatTreeTopo
|
from ripcord.topo import FatTreeTopo
|
||||||
|
|
||||||
from mininet.net import Switch, Controller, Host, init
|
from mininet.net import init
|
||||||
|
from mininet.node import Switch, Controller, Host
|
||||||
from mininet.logging_mod import lg, set_loglevel
|
from mininet.logging_mod import lg, set_loglevel
|
||||||
from mininet.util import make_veth_pair, move_intf, retry, quietRun
|
from mininet.util import make_veth_pair, move_intf, retry, quietRun
|
||||||
from mininet.util import MOVEINTF_DELAY
|
from mininet.util import MOVEINTF_DELAY
|
||||||
|
|||||||
+3
-225
@@ -74,234 +74,12 @@ flush = sys.stdout.flush
|
|||||||
from resource import setrlimit, RLIMIT_NPROC, RLIMIT_NOFILE
|
from resource import setrlimit, RLIMIT_NPROC, RLIMIT_NOFILE
|
||||||
|
|
||||||
from mininet.logging_mod import lg, set_loglevel
|
from mininet.logging_mod import lg, set_loglevel
|
||||||
|
from mininet.node import Node, Host, Controller, Switch
|
||||||
from mininet.util import run, checkRun, quietRun, makeIntfPair, moveIntf
|
from mininet.util import run, checkRun, quietRun, makeIntfPair, moveIntf
|
||||||
from mininet.util import createLink
|
from mininet.util import createLink
|
||||||
|
|
||||||
DATAPATHS = ['user', 'kernel']
|
DATAPATHS = ['user', 'kernel']
|
||||||
|
|
||||||
|
|
||||||
class Node( object ):
|
|
||||||
"""A virtual network node is simply a shell in a network namespace.
|
|
||||||
We communicate with it using pipes."""
|
|
||||||
inToNode = {}
|
|
||||||
outToNode = {}
|
|
||||||
def __init__( self, name, inNamespace=True ):
|
|
||||||
self.name = name
|
|
||||||
closeFds = False # speed vs. memory use
|
|
||||||
# xpg_echo is needed so we can echo our sentinel in sendCmd
|
|
||||||
cmd = [ '/bin/bash', '-O', 'xpg_echo' ]
|
|
||||||
self.inNamespace = inNamespace
|
|
||||||
if self.inNamespace: cmd = [ 'netns' ] + cmd
|
|
||||||
self.shell = Popen( cmd, stdin=PIPE, stdout=PIPE, stderr=STDOUT,
|
|
||||||
close_fds=closeFds )
|
|
||||||
self.stdin = self.shell.stdin
|
|
||||||
self.stdout = self.shell.stdout
|
|
||||||
self.pollOut = select.poll()
|
|
||||||
self.pollOut.register( self.stdout )
|
|
||||||
# Maintain mapping between file descriptors and nodes
|
|
||||||
# This could be useful for monitoring multiple nodes
|
|
||||||
# using select.poll()
|
|
||||||
self.outToNode[ self.stdout.fileno() ] = self
|
|
||||||
self.inToNode[ self.stdin.fileno() ] = self
|
|
||||||
self.pid = self.shell.pid
|
|
||||||
self.intfCount = 0
|
|
||||||
self.intfs = [] # list of interface names, as strings
|
|
||||||
self.ips = {}
|
|
||||||
self.connection = {}
|
|
||||||
self.waiting = False
|
|
||||||
self.execed = False
|
|
||||||
def fdToNode( self, f ):
|
|
||||||
node = self.outToNode.get( f )
|
|
||||||
return node or self.inToNode.get( f )
|
|
||||||
def cleanup( self ):
|
|
||||||
# Help python collect its garbage
|
|
||||||
self.shell = None
|
|
||||||
# Subshell I/O, commands and control
|
|
||||||
def read( self, max ): return os.read( self.stdout.fileno(), max )
|
|
||||||
def write( self, data ): os.write( self.stdin.fileno(), data )
|
|
||||||
def terminate( self ):
|
|
||||||
os.kill( self.pid, signal.SIGKILL )
|
|
||||||
self.cleanup()
|
|
||||||
def stop( self ): self.terminate()
|
|
||||||
def waitReadable( self ): self.pollOut.poll()
|
|
||||||
def sendCmd( self, cmd ):
|
|
||||||
"""Send a command, followed by a command to echo a sentinel,
|
|
||||||
and return without waiting for the command to complete."""
|
|
||||||
assert not self.waiting
|
|
||||||
if cmd[ -1 ] == '&':
|
|
||||||
separator = '&'
|
|
||||||
cmd = cmd[ : -1 ]
|
|
||||||
else: separator = ';'
|
|
||||||
if isinstance( cmd, list): cmd = ' '.join( cmd )
|
|
||||||
self.write( cmd + separator + " echo -n '\\0177' \n")
|
|
||||||
self.waiting = True
|
|
||||||
def monitor( self ):
|
|
||||||
"Monitor a command's output, returning (done, data)."
|
|
||||||
assert self.waiting
|
|
||||||
self.waitReadable()
|
|
||||||
data = self.read( 1024 )
|
|
||||||
if len( data ) > 0 and data[ -1 ] == chr( 0177 ):
|
|
||||||
self.waiting = False
|
|
||||||
return True, data[ : -1 ]
|
|
||||||
else:
|
|
||||||
return False, data
|
|
||||||
def sendInt( self ):
|
|
||||||
"Send ^C, hopefully interrupting a running subprocess."
|
|
||||||
self.write( chr( 3 ) )
|
|
||||||
def waitOutput( self ):
|
|
||||||
"""Wait for a command to complete (signaled by a sentinel
|
|
||||||
character, ASCII(127) appearing in the output stream) and return
|
|
||||||
the output, including trailing newline."""
|
|
||||||
assert self.waiting
|
|
||||||
output = ""
|
|
||||||
while True:
|
|
||||||
self.waitReadable()
|
|
||||||
data = self.read( 1024 )
|
|
||||||
if len(data) > 0 and data[ -1 ] == chr( 0177 ):
|
|
||||||
output += data[ : -1 ]
|
|
||||||
break
|
|
||||||
else: output += data
|
|
||||||
self.waiting = False
|
|
||||||
return output
|
|
||||||
def cmd( self, cmd ):
|
|
||||||
"Send a command, wait for output, and return it."
|
|
||||||
self.sendCmd( cmd )
|
|
||||||
return self.waitOutput()
|
|
||||||
def cmdPrint( self, cmd ):
|
|
||||||
"Call cmd, printing the command and output"
|
|
||||||
#lg.info("*** %s : %s", self.name, cmd)
|
|
||||||
result = self.cmd( cmd )
|
|
||||||
#lg.info("%s\n", result)
|
|
||||||
return result
|
|
||||||
# Interface management, configuration, and routing
|
|
||||||
def intfName( self, n):
|
|
||||||
"Construct a canonical interface name node-intf for interface N."
|
|
||||||
return self.name + '-eth' + `n`
|
|
||||||
def newIntf( self ):
|
|
||||||
"Reserve and return a new interface name for this node."
|
|
||||||
intfName = self.intfName( self.intfCount)
|
|
||||||
self.intfCount += 1
|
|
||||||
self.intfs += [ intfName ]
|
|
||||||
return intfName
|
|
||||||
def setIP( self, intf, ip, bits ):
|
|
||||||
"Set an interface's IP address."
|
|
||||||
result = self.cmd( [ 'ifconfig', intf, ip + bits, 'up' ] )
|
|
||||||
self.ips[ intf ] = ip
|
|
||||||
return result
|
|
||||||
def setHostRoute( self, ip, intf ):
|
|
||||||
"Add a route to the given IP address via intf."
|
|
||||||
return self.cmd( 'route add -host ' + ip + ' dev ' + intf )
|
|
||||||
def setDefaultRoute( self, intf ):
|
|
||||||
"Set the default route to go through intf."
|
|
||||||
self.cmd( 'ip route flush' )
|
|
||||||
return self.cmd( 'route add default ' + intf )
|
|
||||||
def IP( self ):
|
|
||||||
"Return IP address of first interface"
|
|
||||||
if len( self.intfs ) > 0:
|
|
||||||
return self.ips.get( self.intfs[ 0 ], None )
|
|
||||||
def intfIsUp( self, intf ):
|
|
||||||
"Check if one of our interfaces is up."
|
|
||||||
return 'UP' in self.cmd( 'ifconfig ' + self.intfs[ 0 ] )
|
|
||||||
# Other methods
|
|
||||||
def __str__( self ):
|
|
||||||
result = self.name + ":"
|
|
||||||
if self.IP():
|
|
||||||
result += " IP=" + self.IP()
|
|
||||||
result += " intfs=" + ','.join( self.intfs )
|
|
||||||
result += " waiting=" + `self.waiting`
|
|
||||||
return result
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class Host( Node ):
|
|
||||||
"""A host is simply a Node."""
|
|
||||||
pass
|
|
||||||
|
|
||||||
class Controller( Node ):
|
|
||||||
"""A Controller is a Node that is running (or has execed) an
|
|
||||||
OpenFlow controller."""
|
|
||||||
def __init__( self, name, kernel=True, controller='controller',
|
|
||||||
cargs='-v ptcp:', cdir=None ):
|
|
||||||
self.controller = controller
|
|
||||||
self.cargs = cargs
|
|
||||||
self.cdir = cdir
|
|
||||||
Node.__init__( self, name, inNamespace=( not kernel ) )
|
|
||||||
def start( self ):
|
|
||||||
"Start <controller> <args> on controller, logging to /tmp/cN.log"
|
|
||||||
cout = '/tmp/' + self.name + '.log'
|
|
||||||
if self.cdir is not None:
|
|
||||||
self.cmdPrint( 'cd ' + self.cdir )
|
|
||||||
self.cmdPrint( self.controller + ' ' + self.cargs +
|
|
||||||
' 1> ' + cout + ' 2> ' + cout + ' &' )
|
|
||||||
self.execed = False # XXX Until I fix it
|
|
||||||
def stop( self, controller='controller' ):
|
|
||||||
"Stop controller cprog on controller"
|
|
||||||
self.cmd( "kill %" + controller )
|
|
||||||
self.terminate()
|
|
||||||
|
|
||||||
class Switch( Node ):
|
|
||||||
"""A Switch is a Node that is running (or has execed)
|
|
||||||
an OpenFlow switch."""
|
|
||||||
def __init__( self, name, datapath=None ):
|
|
||||||
self.dp = datapath
|
|
||||||
Node.__init__( self, name, inNamespace=( datapath == None ) )
|
|
||||||
def startUserDatapath( self, controller ):
|
|
||||||
"""Start OpenFlow reference user datapath,
|
|
||||||
logging to /tmp/sN-{ofd,ofp}.log"""
|
|
||||||
ofdlog = '/tmp/' + self.name + '-ofd.log'
|
|
||||||
ofplog = '/tmp/' + self.name + '-ofp.log'
|
|
||||||
self.cmd( 'ifconfig lo up' )
|
|
||||||
intfs = self.intfs[ 1 : ] # 0 is mgmt interface
|
|
||||||
self.cmdPrint( 'ofdatapath -i ' + ','.join( intfs ) +
|
|
||||||
' ptcp: 1> ' + ofdlog + ' 2> '+ ofdlog + ' &' )
|
|
||||||
self.cmdPrint( 'ofprotocol tcp:' + controller.IP() +
|
|
||||||
' tcp:localhost --fail=closed 1> ' + ofplog + ' 2>' + ofplog + ' &' )
|
|
||||||
def stopUserDatapath( self ):
|
|
||||||
"Stop OpenFlow reference user datapath."
|
|
||||||
self.cmd( "kill %ofdatapath" )
|
|
||||||
self.cmd( "kill %ofprotocol" )
|
|
||||||
def startKernelDatapath( self, controller):
|
|
||||||
"Start up switch using OpenFlow reference kernel datapath."
|
|
||||||
ofplog = '/tmp/' + self.name + '-ofp.log'
|
|
||||||
quietRun( 'ifconfig lo up' )
|
|
||||||
# Delete local datapath if it exists;
|
|
||||||
# then create a new one monitoring the given interfaces
|
|
||||||
quietRun( 'dpctl deldp ' + self.dp )
|
|
||||||
self.cmdPrint( 'dpctl adddp ' + self.dp )
|
|
||||||
self.cmdPrint( 'dpctl addif ' + self.dp + ' ' + ' '.join( self.intfs ) )
|
|
||||||
# Run protocol daemon
|
|
||||||
self.cmdPrint( 'ofprotocol' +
|
|
||||||
' ' + self.dp + ' tcp:127.0.0.1 ' +
|
|
||||||
' --fail=closed 1> ' + ofplog + ' 2>' + ofplog + ' &' )
|
|
||||||
self.execed = False # XXX until I fix it
|
|
||||||
def stopKernelDatapath( self ):
|
|
||||||
"Terminate a switch using OpenFlow reference kernel datapath."
|
|
||||||
quietRun( 'dpctl deldp ' + self.dp )
|
|
||||||
# In theory the interfaces should go away after we shut down.
|
|
||||||
# However, this takes time, so we're better off to remove them
|
|
||||||
# explicitly so that we won't get errors if we run before they
|
|
||||||
# have been removed by the kernel. Unfortunately this is very slow.
|
|
||||||
self.cmd( 'kill %ofprotocol')
|
|
||||||
for intf in self.intfs:
|
|
||||||
quietRun( 'ip link del ' + intf )
|
|
||||||
lg.info('.')
|
|
||||||
def start( self, controller ):
|
|
||||||
if self.dp is None: self.startUserDatapath( controller )
|
|
||||||
else: self.startKernelDatapath( controller )
|
|
||||||
def stop( self ):
|
|
||||||
if self.dp is None: self.stopUserDatapath()
|
|
||||||
else: self.stopKernelDatapath()
|
|
||||||
def sendCmd( self, cmd ):
|
|
||||||
if not self.execed:
|
|
||||||
return Node.sendCmd( self, cmd )
|
|
||||||
else:
|
|
||||||
lg.error("*** Error: %s has execed and cannot accept commands" %
|
|
||||||
self.name)
|
|
||||||
def monitor( self ):
|
|
||||||
if not self.execed: return Node.monitor( self )
|
|
||||||
else: return True, ''
|
|
||||||
|
|
||||||
|
|
||||||
# Handy utilities
|
# Handy utilities
|
||||||
|
|
||||||
def dumpNodes( nodes ):
|
def dumpNodes( nodes ):
|
||||||
@@ -367,11 +145,11 @@ def configureRoutedControlNetwork( controller, switches, ips):
|
|||||||
switch.setHostRoute( cip, sintf )
|
switch.setHostRoute( cip, sintf )
|
||||||
lg.info("\n")
|
lg.info("\n")
|
||||||
lg.info("*** Testing control network\n")
|
lg.info("*** Testing control network\n")
|
||||||
while not controller.intfIsUp( controller.intfs[ 0 ] ):
|
while not controller.intfIsUp():
|
||||||
lg.info("*** Waiting for %s to come up\n", controller.intfs[ 0 ])
|
lg.info("*** Waiting for %s to come up\n", controller.intfs[ 0 ])
|
||||||
sleep( 1 )
|
sleep( 1 )
|
||||||
for switch in switches:
|
for switch in switches:
|
||||||
while not switch.intfIsUp( switch.intfs[ 0 ] ):
|
while not switch.intfIsUp():
|
||||||
lg.info("*** Waiting for %s to come up\n" % switch.intfs[ 0 ])
|
lg.info("*** Waiting for %s to come up\n" % switch.intfs[ 0 ])
|
||||||
sleep( 1 )
|
sleep( 1 )
|
||||||
if pingTest( hosts=[ switch, controller ] ) != 0:
|
if pingTest( hosts=[ switch, controller ] ) != 0:
|
||||||
|
|||||||
+338
@@ -0,0 +1,338 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
'''Node objects for Mininet.'''
|
||||||
|
|
||||||
|
from subprocess import Popen, PIPE, STDOUT
|
||||||
|
import os, signal, sys, select
|
||||||
|
flush = sys.stdout.flush
|
||||||
|
|
||||||
|
from mininet.logging_mod import lg
|
||||||
|
from mininet.util import quietRun
|
||||||
|
|
||||||
|
class Node(object):
|
||||||
|
'''A virtual network node is simply a shell in a network namespace.
|
||||||
|
We communicate with it using pipes.'''
|
||||||
|
inToNode = {}
|
||||||
|
outToNode = {}
|
||||||
|
|
||||||
|
def __init__(self, name, inNamespace = True):
|
||||||
|
self.name = name
|
||||||
|
closeFds = False # speed vs. memory use
|
||||||
|
# xpg_echo is needed so we can echo our sentinel in sendCmd
|
||||||
|
cmd = ['/bin/bash', '-O', 'xpg_echo']
|
||||||
|
self.inNamespace = inNamespace
|
||||||
|
if self.inNamespace:
|
||||||
|
cmd = ['netns'] + cmd
|
||||||
|
self.shell = Popen(cmd, stdin = PIPE, stdout = PIPE, stderr = STDOUT,
|
||||||
|
close_fds = closeFds)
|
||||||
|
self.stdin = self.shell.stdin
|
||||||
|
self.stdout = self.shell.stdout
|
||||||
|
self.pollOut = select.poll()
|
||||||
|
self.pollOut.register(self.stdout)
|
||||||
|
# Maintain mapping between file descriptors and nodes
|
||||||
|
# This could be useful for monitoring multiple nodes
|
||||||
|
# using select.poll()
|
||||||
|
self.outToNode[self.stdout.fileno()] = self
|
||||||
|
self.inToNode[self.stdin.fileno()] = self
|
||||||
|
self.pid = self.shell.pid
|
||||||
|
self.intfCount = 0
|
||||||
|
self.intfs = [] # list of interface names, as strings
|
||||||
|
self.ips = {}
|
||||||
|
self.connection = {}
|
||||||
|
self.waiting = False
|
||||||
|
self.execed = False
|
||||||
|
|
||||||
|
def fdToNode(self, f):
|
||||||
|
'''Insert docstring.
|
||||||
|
|
||||||
|
@param f unknown
|
||||||
|
@return bool unknown
|
||||||
|
'''
|
||||||
|
node = self.outToNode.get(f)
|
||||||
|
return node or self.inToNode.get(f)
|
||||||
|
|
||||||
|
def cleanup(self):
|
||||||
|
'''Help python collect its garbage.'''
|
||||||
|
self.shell = None
|
||||||
|
|
||||||
|
# Subshell I/O, commands and control
|
||||||
|
def read(self, fileno_max):
|
||||||
|
'''Insert docstring.
|
||||||
|
|
||||||
|
@param fileno_max unknown
|
||||||
|
'''
|
||||||
|
return os.read(self.stdout.fileno(), fileno_max)
|
||||||
|
|
||||||
|
def write(self, data):
|
||||||
|
'''Write data to node.
|
||||||
|
|
||||||
|
@param data string
|
||||||
|
'''
|
||||||
|
os.write(self.stdin.fileno(), data)
|
||||||
|
|
||||||
|
def terminate(self):
|
||||||
|
'''Send kill signal to Node and cleanup after it.'''
|
||||||
|
os.kill(self.pid, signal.SIGKILL)
|
||||||
|
self.cleanup()
|
||||||
|
|
||||||
|
def stop(self):
|
||||||
|
'''Stop node.'''
|
||||||
|
self.terminate()
|
||||||
|
|
||||||
|
def waitReadable(self):
|
||||||
|
'''Poll on node.'''
|
||||||
|
self.pollOut.poll()
|
||||||
|
|
||||||
|
def sendCmd(self, cmd):
|
||||||
|
'''Send a command, followed by a command to echo a sentinel,
|
||||||
|
and return without waiting for the command to complete.'''
|
||||||
|
assert not self.waiting
|
||||||
|
if cmd[-1] == '&':
|
||||||
|
separator = '&'
|
||||||
|
cmd = cmd[:-1]
|
||||||
|
else:
|
||||||
|
separator = ';'
|
||||||
|
if isinstance(cmd, list):
|
||||||
|
cmd = ' '.join(cmd)
|
||||||
|
self.write(cmd + separator + ' echo -n "\\0177" \n')
|
||||||
|
self.waiting = True
|
||||||
|
|
||||||
|
def monitor(self):
|
||||||
|
'''Monitor the output of a command, returning (done, data).'''
|
||||||
|
assert self.waiting
|
||||||
|
self.waitReadable()
|
||||||
|
data = self.read(1024)
|
||||||
|
if len(data) > 0 and data[-1] == chr(0177):
|
||||||
|
self.waiting = False
|
||||||
|
return True, data[:-1]
|
||||||
|
else:
|
||||||
|
return False, data
|
||||||
|
|
||||||
|
def sendInt(self):
|
||||||
|
'''Send ^C, hopefully interrupting a running subprocess.'''
|
||||||
|
self.write(chr(3))
|
||||||
|
|
||||||
|
def waitOutput(self):
|
||||||
|
'''Wait for a command to complete.
|
||||||
|
|
||||||
|
Completion is signaled by a sentinel character, ASCII(127) appearing in
|
||||||
|
the output stream. Wait for the sentinel and return the output,
|
||||||
|
including trailing newline.
|
||||||
|
'''
|
||||||
|
assert self.waiting
|
||||||
|
output = ''
|
||||||
|
while True:
|
||||||
|
self.waitReadable()
|
||||||
|
data = self.read(1024)
|
||||||
|
if len(data) > 0 and data[-1] == chr(0177):
|
||||||
|
output += data[:-1]
|
||||||
|
break
|
||||||
|
else: output += data
|
||||||
|
self.waiting = False
|
||||||
|
return output
|
||||||
|
|
||||||
|
def cmd(self, cmd):
|
||||||
|
'''Send a command, wait for output, and return it.
|
||||||
|
|
||||||
|
@param cmd string
|
||||||
|
'''
|
||||||
|
self.sendCmd(cmd)
|
||||||
|
return self.waitOutput()
|
||||||
|
|
||||||
|
def cmdPrint(self, cmd):
|
||||||
|
'''Call cmd and printing its output
|
||||||
|
|
||||||
|
@param cmd string
|
||||||
|
'''
|
||||||
|
#lg.info('*** %s : %s', self.name, cmd)
|
||||||
|
result = self.cmd(cmd)
|
||||||
|
#lg.info('%s\n', result)
|
||||||
|
return result
|
||||||
|
|
||||||
|
# Interface management, configuration, and routing
|
||||||
|
def intfName(self, n):
|
||||||
|
'''Construct a canonical interface name node-intf for interface N.'''
|
||||||
|
return self.name + '-eth' + repr(n)
|
||||||
|
|
||||||
|
def newIntf(self):
|
||||||
|
'''Reserve and return a new interface name.'''
|
||||||
|
intfName = self.intfName(self.intfCount)
|
||||||
|
self.intfCount += 1
|
||||||
|
self.intfs += [intfName]
|
||||||
|
return intfName
|
||||||
|
|
||||||
|
def setIP(self, intf, ip, bits):
|
||||||
|
'''Set the IP address for an interface.
|
||||||
|
|
||||||
|
@param intf string, interface name
|
||||||
|
@param ip IP address as integer
|
||||||
|
@param bits
|
||||||
|
'''
|
||||||
|
result = self.cmd(['ifconfig', intf, ip + bits, 'up'])
|
||||||
|
self.ips[intf] = ip
|
||||||
|
return result
|
||||||
|
|
||||||
|
def setHostRoute(self, ip, intf):
|
||||||
|
'''Add route to host.
|
||||||
|
|
||||||
|
@param ip IP address as dotted decimal
|
||||||
|
@param intf string, interface name
|
||||||
|
'''
|
||||||
|
return self.cmd('route add -host ' + ip + ' dev ' + intf)
|
||||||
|
|
||||||
|
def setDefaultRoute(self, intf):
|
||||||
|
'''Set the default route to go through intf.
|
||||||
|
|
||||||
|
@param intf string, interface name
|
||||||
|
'''
|
||||||
|
self.cmd('ip route flush')
|
||||||
|
return self.cmd('route add default ' + intf)
|
||||||
|
|
||||||
|
def IP(self):
|
||||||
|
'''Return IP address of first interface'''
|
||||||
|
if len(self.intfs) > 0:
|
||||||
|
return self.ips.get(self.intfs[ 0 ], None)
|
||||||
|
|
||||||
|
def intfIsUp(self):
|
||||||
|
'''Check if one of our interfaces is up.'''
|
||||||
|
return 'UP' in self.cmd('ifconfig ' + self.intfs[0])
|
||||||
|
|
||||||
|
# Other methods
|
||||||
|
def __str__(self):
|
||||||
|
result = self.name + ':'
|
||||||
|
if self.IP():
|
||||||
|
result += ' IP=' + self.IP()
|
||||||
|
result += ' intfs=' + ','.join(self.intfs)
|
||||||
|
result += ' waiting=' + repr(self.waiting)
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
class Host(Node):
|
||||||
|
'''A host is simply a Node.'''
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class Controller(Node):
|
||||||
|
'''A Controller is a Node that is running (or has execed) an
|
||||||
|
OpenFlow controller.'''
|
||||||
|
|
||||||
|
def __init__(self, name, kernel=True, controller='controller',
|
||||||
|
cargs='-v ptcp:', cdir=None):
|
||||||
|
self.controller = controller
|
||||||
|
self.cargs = cargs
|
||||||
|
self.cdir = cdir
|
||||||
|
Node.__init__(self, name, inNamespace=(not kernel))
|
||||||
|
|
||||||
|
def start(self):
|
||||||
|
'''Start <controller> <args> on controller.
|
||||||
|
|
||||||
|
Log to /tmp/cN.log
|
||||||
|
'''
|
||||||
|
cout = '/tmp/' + self.name + '.log'
|
||||||
|
if self.cdir is not None:
|
||||||
|
self.cmdPrint('cd ' + self.cdir)
|
||||||
|
self.cmdPrint(self.controller + ' ' + self.cargs +
|
||||||
|
' 1> ' + cout + ' 2> ' + cout + ' &')
|
||||||
|
self.execed = False # XXX Until I fix it
|
||||||
|
|
||||||
|
def stop(self):
|
||||||
|
'''Stop controller.'''
|
||||||
|
self.cmd('kill %' + self.controller)
|
||||||
|
self.terminate()
|
||||||
|
|
||||||
|
|
||||||
|
class Switch(Node):
|
||||||
|
'''A Switch is a Node that is running (or has execed)
|
||||||
|
an OpenFlow switch.'''
|
||||||
|
|
||||||
|
def __init__(self, name, datapath = None):
|
||||||
|
'''Init.
|
||||||
|
|
||||||
|
@param name
|
||||||
|
@param datapath string, datapath name
|
||||||
|
'''
|
||||||
|
self.dp = datapath
|
||||||
|
Node.__init__(self, name, inNamespace = (datapath == None))
|
||||||
|
|
||||||
|
def _startUserDatapath(self, controller):
|
||||||
|
'''Start OpenFlow reference user datapath.
|
||||||
|
|
||||||
|
Log to /tmp/sN-{ofd,ofp}.log.
|
||||||
|
|
||||||
|
@param controller Controller object.
|
||||||
|
'''
|
||||||
|
ofdlog = '/tmp/' + self.name + '-ofd.log'
|
||||||
|
ofplog = '/tmp/' + self.name + '-ofp.log'
|
||||||
|
self.cmd('ifconfig lo up')
|
||||||
|
intfs = self.intfs[1:] # 0 is mgmt interface
|
||||||
|
self.cmdPrint('ofdatapath -i ' + ','.join(intfs) +
|
||||||
|
' ptcp: 1> ' + ofdlog + ' 2> ' + ofdlog + ' &')
|
||||||
|
self.cmdPrint('ofprotocol tcp:' + controller.IP() +
|
||||||
|
' tcp:localhost --fail=closed 1> ' + ofplog + ' 2>' +
|
||||||
|
ofplog + ' &')
|
||||||
|
|
||||||
|
def _stopUserDatapath(self):
|
||||||
|
'''Stop OpenFlow reference user datapath.'''
|
||||||
|
self.cmd('kill %ofdatapath')
|
||||||
|
self.cmd('kill %ofprotocol')
|
||||||
|
|
||||||
|
def _startKernelDatapath(self):
|
||||||
|
'''Start up reference kernel datapath.'''
|
||||||
|
ofplog = '/tmp/' + self.name + '-ofp.log'
|
||||||
|
quietRun('ifconfig lo up')
|
||||||
|
# Delete local datapath if it exists;
|
||||||
|
# then create a new one monitoring the given interfaces
|
||||||
|
quietRun('dpctl deldp ' + self.dp)
|
||||||
|
self.cmdPrint('dpctl adddp ' + self.dp)
|
||||||
|
self.cmdPrint('dpctl addif ' + self.dp + ' ' + ' '.join(self.intfs))
|
||||||
|
# Run protocol daemon
|
||||||
|
self.cmdPrint('ofprotocol' +
|
||||||
|
' ' + self.dp + ' tcp:127.0.0.1 ' +
|
||||||
|
' --fail=closed 1> ' + ofplog + ' 2>' + ofplog + ' &')
|
||||||
|
self.execed = False # XXX until I fix it
|
||||||
|
|
||||||
|
def _stopKernelDatapath(self):
|
||||||
|
'''Terminate reference kernel datapath.'''
|
||||||
|
quietRun('dpctl deldp ' + self.dp)
|
||||||
|
# In theory the interfaces should go away after we shut down.
|
||||||
|
# However, this takes time, so we're better off to remove them
|
||||||
|
# explicitly so that we won't get errors if we run before they
|
||||||
|
# have been removed by the kernel. Unfortunately this is very slow.
|
||||||
|
self.cmd('kill %ofprotocol')
|
||||||
|
for intf in self.intfs:
|
||||||
|
quietRun('ip link del ' + intf)
|
||||||
|
lg.info('.')
|
||||||
|
|
||||||
|
def start(self, controller):
|
||||||
|
'''Start datapath.
|
||||||
|
|
||||||
|
@param controller Controller object
|
||||||
|
'''
|
||||||
|
if self.dp is None:
|
||||||
|
self._startUserDatapath(controller)
|
||||||
|
else:
|
||||||
|
self._startKernelDatapath()
|
||||||
|
|
||||||
|
def stop(self):
|
||||||
|
'''Stop datapath.'''
|
||||||
|
if self.dp is None:
|
||||||
|
self._stopUserDatapath()
|
||||||
|
else:
|
||||||
|
self._stopKernelDatapath()
|
||||||
|
|
||||||
|
def sendCmd(self, cmd):
|
||||||
|
'''Send command to Node.
|
||||||
|
|
||||||
|
@param cmd string
|
||||||
|
'''
|
||||||
|
if not self.execed:
|
||||||
|
return Node.sendCmd(self, cmd)
|
||||||
|
else:
|
||||||
|
lg.error('*** Error: %s has execed and cannot accept commands' %
|
||||||
|
self.name)
|
||||||
|
|
||||||
|
def monitor(self):
|
||||||
|
'''Monitor node.'''
|
||||||
|
if not self.execed:
|
||||||
|
return Node.monitor(self)
|
||||||
|
else:
|
||||||
|
return True, ''
|
||||||
Reference in New Issue
Block a user