mirror of
https://github.com/opennetworkinglab/onos.git
synced 2025-10-14 17:01:02 +02:00
233 lines
7.9 KiB
Python
Executable File
233 lines
7.9 KiB
Python
Executable File
#!/usr/bin/env python
|
|
|
|
import json
|
|
|
|
from mininet.net import Mininet
|
|
from mininet.node import UserSwitch, DefaultController, RemoteController, Host
|
|
from mininet.topo import Topo
|
|
from mininet.log import setLogLevel, info, error, warn
|
|
from mininet.cli import CLI
|
|
from mininet.link import OVSIntf
|
|
from mininet.util import quietRun
|
|
|
|
from opticalUtils import LINCSwitch, LINCLink
|
|
|
|
"""XXX: separate out into domainlib"""
|
|
class Domain(object):
|
|
"""
|
|
A container for switch, host, link, and controller information to be dumped
|
|
into the Mininet mid-level API.
|
|
"""
|
|
|
|
def __init__ (self, did=0):
|
|
# each Domain has a numeric ID for sanity/convenience
|
|
self.__dId = did
|
|
|
|
# information about network elements - for calling the "mid-level" APIs
|
|
self.__ctrls = {}
|
|
self.__switches = {}
|
|
self.__hosts = {}
|
|
self.__links = {}
|
|
# maps of devices, hosts, and controller names to actual objects
|
|
self.__smap = {}
|
|
self.__hmap = {}
|
|
self.__cmap = {}
|
|
|
|
def addController(self, name, **args):
|
|
self.__ctrls[name] = args if args else {}
|
|
return name
|
|
|
|
# Note: This method will return the name of the swich, not the switch object
|
|
def addSwitch(self, name, **args):
|
|
self.__switches[name] = args if args else {}
|
|
return name
|
|
|
|
def addHost(self, name, **args):
|
|
self.__hosts[name] = args if args else {}
|
|
return name
|
|
|
|
def addLink(self, src, dst, **args):
|
|
self.__links[(src, dst)] = args if args else {}
|
|
return (src, dst)
|
|
|
|
def getId( self):
|
|
return self.__dId
|
|
|
|
def getControllers(self, name=None):
|
|
return self.__cmap.values() if not name else self.__cmap.get(name)
|
|
|
|
def getSwitches(self, name=None):
|
|
return self.__smap.values() if not name else self.__smap.get(name)
|
|
|
|
def getHosts(self, name=None):
|
|
return self.__hmap.values() if not name else self.__hmap.get(name)
|
|
|
|
def injectInto(self, net):
|
|
""" Adds available topology info to a supplied Mininet object. """
|
|
# add switches, hosts, then links to mininet object
|
|
for sw, args in self.__switches.iteritems():
|
|
self.__smap[sw] = net.addSwitch(sw, **args)
|
|
for h, args in self.__hosts.iteritems():
|
|
self.__hmap[h] = net.addHost(h, **args)
|
|
for l, args in self.__links.iteritems():
|
|
src = self.__smap.get(l[0])
|
|
dst = self.__smap.get(l[1])
|
|
net.addLink(src if src else self.__hmap.get(l[0]),
|
|
dst if dst else self.__hmap.get(l[1]), **args)
|
|
# then controllers
|
|
for c, args in self.__ctrls.iteritems():
|
|
self.__cmap[c] = net.addController(c, **args)
|
|
|
|
def start(self):
|
|
""" starts the switches with the correct controller. """
|
|
map(lambda c: c.start(), self.__cmap.values())
|
|
map(lambda s: s.start(self.__cmap.values()), self.__smap.values())
|
|
|
|
def build(self, *args):
|
|
""" override for custom topology, similar to Topo """
|
|
pass
|
|
|
|
|
|
class OpticalDomain(Domain):
|
|
""" An emulated optical metro core. It is Domain 0. """
|
|
def build(self):
|
|
for i in range (1,4):
|
|
oean = { "optical.regens": 0 }
|
|
self.addSwitch('OE%s' % i, dpid='0000ffffffffff0%s' % i, annotations=oean, cls=LINCSwitch)
|
|
|
|
# ROADM port number OE"1" -> OE'2' = "1"'2'00
|
|
# leaving port number up to 100 open for use by Och port
|
|
an = { "durable": "true" }
|
|
self.addLink('OE1', 'OE2', port1=1200, port2=2100, annotations=an, cls=LINCLink)
|
|
self.addLink('OE2', 'OE3', port1=2300, port2=3200, annotations=an, cls=LINCLink)
|
|
self.addLink('OE3', 'OE1', port1=3100, port2=1300, annotations=an, cls=LINCLink)
|
|
|
|
class FabricDomain(Domain):
|
|
"""
|
|
An emulated CO fabric, which is basically a K(n,m) bipartite graph.
|
|
|
|
Each FabricDomain should be given a unique Domain ID (did) to ensure unique
|
|
names and addressing.
|
|
"""
|
|
def __init__(self, did):
|
|
Domain.__init__(self, did)
|
|
|
|
def build(self):
|
|
|
|
# CpQD switches and OVS b/c brokenness.
|
|
sw1 = self.addSwitch('cpqd%s1' % self.getId(), cls=UserSwitch, dpopts='--no-local-port')
|
|
sw2 = self.addSwitch('ovs%s01' % self.getId())
|
|
|
|
# make sw2 the tether point
|
|
self.__tether = sw2
|
|
|
|
# sw1-sw2-> to metro core
|
|
self.addLink(sw1, sw2, port2=1)
|
|
|
|
# h-sw1
|
|
h = self.addHost('h%s' % self.getId(), cls=IpHost, ip='10.0.0.%s/24' % self.getId(),
|
|
gateway='10.0.0.254')
|
|
self.addLink(h, sw1)
|
|
|
|
def getTether(self):
|
|
""" get the switch name of this fabric facing the core """
|
|
return self.__tether
|
|
|
|
|
|
class IpHost(Host):
|
|
def __init__(self, name, gateway, *args, **kwargs):
|
|
super(IpHost, self).__init__(name, *args, **kwargs)
|
|
self.gateway = gateway
|
|
|
|
def config(self, **kwargs):
|
|
Host.config(self, **kwargs)
|
|
mtu = "ifconfig "+self.name+"-eth0 mtu 1490"
|
|
self.cmd(mtu)
|
|
self.cmd('ip route add default via %s' % self.gateway)
|
|
|
|
def setup(argv):
|
|
domains = []
|
|
ctlsets = sys.argv[1:]
|
|
|
|
# the controllers for the optical domain
|
|
d0 = OpticalDomain()
|
|
f0 = FabricDomain(1)
|
|
f1 = FabricDomain(2)
|
|
domains.extend([ d0, f0, f1 ])
|
|
|
|
for i in range(len(domains)):
|
|
ctls = ctlsets[i].split(',')
|
|
for c in range(len(ctls)):
|
|
domains[i].addController('c%s%s' % (i, c), controller=RemoteController, ip=ctls[c])
|
|
|
|
# netcfg for each domains
|
|
# Note: Separate netcfg for domain0 is created in opticalUtils
|
|
domainCfgs = []
|
|
for i in range (0,len(ctlsets)):
|
|
cfg = {}
|
|
cfg['devices'] = {}
|
|
cfg['ports'] = {}
|
|
cfg['links'] = {}
|
|
domainCfgs.append(cfg)
|
|
|
|
# make/setup Mininet object
|
|
net = Mininet()
|
|
for d in domains:
|
|
d.build()
|
|
d.injectInto(net)
|
|
|
|
# connect COs to core - sort of hard-wired at this moment
|
|
# adding cross-connect links
|
|
for i in range(1,len(domains)):
|
|
# add 10 cross-connect links between domains
|
|
xcPortNo=2
|
|
ochPortNo=10
|
|
|
|
an = { "bandwidth": 10, "durable": "true" }
|
|
net.addLink(domains[i].getTether(), d0.getSwitches('OE%s' % i),
|
|
port1=xcPortNo, port2=ochPortNo, speed=10000, annotations=an, cls=LINCLink)
|
|
|
|
xcId = 'of:' + domains[i].getSwitches(name=domains[i].getTether()).dpid + '/' + str(xcPortNo)
|
|
ochId = 'of:' + d0.getSwitches('OE%s' % i).dpid + '/' + str(ochPortNo)
|
|
domainCfgs[i]['ports'][xcId] = {'cross-connect': {'remote': ochId}}
|
|
|
|
# fire everything up
|
|
net.build()
|
|
map(lambda x: x.start(), domains)
|
|
|
|
# create a minimal copy of the network for configuring LINC.
|
|
cfgnet = Mininet()
|
|
cfgnet.switches = net.switches
|
|
cfgnet.links = net.links
|
|
cfgnet.controllers = d0.getControllers()
|
|
LINCSwitch.bootOE(cfgnet, d0.getSwitches())
|
|
|
|
# send netcfg json to each CO-ONOS
|
|
for i in range(1,len(domains)):
|
|
info('*** Pushing Topology.json to CO-ONOS %d\n' % i)
|
|
filename = 'Topology%d.json' % i
|
|
with open(filename, 'w') as outfile:
|
|
json.dump(domainCfgs[i], outfile, indent=4, separators=(',', ': '))
|
|
|
|
output = quietRun('%s/onos-netcfg %s %s &'\
|
|
% (LINCSwitch.runPackDir,
|
|
domains[i].getControllers()[0].ip,
|
|
filename), shell=True)
|
|
# successful output contains the two characters '{}'
|
|
# if there is more output than this, there is an issue
|
|
if output.strip('{}'):
|
|
warn('***WARNING: Could not push topology file to ONOS: %s\n' % output)
|
|
|
|
CLI(net)
|
|
net.stop()
|
|
LINCSwitch.shutdownOE()
|
|
|
|
if __name__ == '__main__':
|
|
setLogLevel('info')
|
|
import sys
|
|
if len(sys.argv) < 4:
|
|
print ("Usage: sudo -E ./ectest.py ctl-set1 ... ctl-set4\n\n",
|
|
"Where ctl-set are comma-separated controller IP's")
|
|
else:
|
|
setup(sys.argv)
|