Committed by
Yuta HIGUCHI
Script for emulating a four-domain metro network.
ref: https://wiki.onosproject.org/display/ONOS/Metro+Network+Emulation Change-Id: Icb8dd7a12587398af49e9acea23a7cb85fb84b88
Showing
1 changed file
with
217 additions
and
0 deletions
tools/test/topos/metro.py
0 → 100755
| 1 | +#!/usr/bin/env python | ||
| 2 | + | ||
| 3 | +from mininet.net import Mininet | ||
| 4 | +from mininet.node import UserSwitch, DefaultController, RemoteController, Host | ||
| 5 | +from mininet.topo import Topo | ||
| 6 | +from mininet.log import setLogLevel, info | ||
| 7 | +from mininet.cli import CLI | ||
| 8 | +from mininet.link import OVSIntf | ||
| 9 | + | ||
| 10 | +from opticalUtils import LINCSwitch, LINCLink | ||
| 11 | + | ||
| 12 | +class Domain(object): | ||
| 13 | + """ | ||
| 14 | + A container for switch, host, link, and controller information to be dumped | ||
| 15 | + into the Mininet mid-level API. | ||
| 16 | + """ | ||
| 17 | + | ||
| 18 | + def __init__ (self, did=0): | ||
| 19 | + # each Domain has a numeric ID for sanity/convenience | ||
| 20 | + self.__dId = did | ||
| 21 | + | ||
| 22 | + # information about network elements - for calling the "mid-level" APIs | ||
| 23 | + self.__ctrls = {} | ||
| 24 | + self.__switches = {} | ||
| 25 | + self.__hosts = {} | ||
| 26 | + self.__links = {} | ||
| 27 | + # maps of devices, hosts, and controller names to actual objects | ||
| 28 | + self.__smap = {} | ||
| 29 | + self.__hmap = {} | ||
| 30 | + self.__cmap = {} | ||
| 31 | + | ||
| 32 | + def addController(self, name, **args): | ||
| 33 | + self.__ctrls[name] = args if args else {} | ||
| 34 | + return name | ||
| 35 | + | ||
| 36 | + def addSwitch(self, name, **args): | ||
| 37 | + self.__switches[name] = args if args else {} | ||
| 38 | + return name | ||
| 39 | + | ||
| 40 | + def addHost(self, name, **args): | ||
| 41 | + self.__hosts[name] = args if args else {} | ||
| 42 | + return name | ||
| 43 | + | ||
| 44 | + def addLink(self, src, dst, **args): | ||
| 45 | + self.__links[(src, dst)] = args if args else {} | ||
| 46 | + return (src, dst) | ||
| 47 | + | ||
| 48 | + def getId( self): | ||
| 49 | + return self.__dId | ||
| 50 | + | ||
| 51 | + def getControllers(self, name=None): | ||
| 52 | + return self.__cmap.values() if not name else self.__cmap.get(name) | ||
| 53 | + | ||
| 54 | + def getSwitches(self, name=None): | ||
| 55 | + return self.__smap.values() if not name else self.__smap.get(name) | ||
| 56 | + | ||
| 57 | + def getHosts(self, name=None): | ||
| 58 | + return self.__hmap.values() if not name else self.__hmap.get(name) | ||
| 59 | + | ||
| 60 | + def injectInto(self, net): | ||
| 61 | + """ Adds available topology info to a supplied Mininet object. """ | ||
| 62 | + # add switches, hosts, then links to mininet object | ||
| 63 | + for sw, args in self.__switches.iteritems(): | ||
| 64 | + self.__smap[sw] = net.addSwitch(sw, **args) | ||
| 65 | + for h, args in self.__hosts.iteritems(): | ||
| 66 | + self.__hmap[h] = net.addHost(h, **args) | ||
| 67 | + for l, args in self.__links.iteritems(): | ||
| 68 | + src = self.__smap.get(l[0]) | ||
| 69 | + dst = self.__smap.get(l[1]) | ||
| 70 | + net.addLink(src if src else self.__hmap.get(l[0]), | ||
| 71 | + dst if dst else self.__hmap.get(l[1]), **args) | ||
| 72 | + # then controllers | ||
| 73 | + for c, args in self.__ctrls.iteritems(): | ||
| 74 | + self.__cmap[c] = net.addController(c, **args) | ||
| 75 | + | ||
| 76 | + def start(self): | ||
| 77 | + """ starts the switches with the correct controller. """ | ||
| 78 | + map(lambda c: c.start(), self.__cmap.values()) | ||
| 79 | + map(lambda s: s.start(self.__cmap.values()), self.__smap.values()) | ||
| 80 | + | ||
| 81 | + def build(self, *args): | ||
| 82 | + """ override for custom topology, similar to Topo """ | ||
| 83 | + pass | ||
| 84 | + | ||
| 85 | + | ||
| 86 | +class OpticalDomain(Domain): | ||
| 87 | + """ An emulated optical metro core. It is Domain 0. """ | ||
| 88 | + def build(self): | ||
| 89 | + oean = { "optical.regens": 0 } | ||
| 90 | + for i in range (1,4): | ||
| 91 | + self.addSwitch('OE%s' % i, dpid='0000ffffffffff0%s' % i, annotations=oean, cls=LINCSwitch) | ||
| 92 | + | ||
| 93 | + an = { "optical.waves": 80, "optical.type": "WDM", "optical.kms": 1000, "durable": "true" } | ||
| 94 | + self.addLink('OE1', 'OE2', port1=50, port2=30, annotations=an, cls=LINCLink) | ||
| 95 | + self.addLink('OE2', 'OE3', port1=50, port2=30, annotations=an, cls=LINCLink) | ||
| 96 | + self.addLink('OE3', 'OE1', port1=50, port2=30, annotations=an, cls=LINCLink) | ||
| 97 | + | ||
| 98 | +class FabricDomain(Domain): | ||
| 99 | + """ | ||
| 100 | + An emulated CO fabric, which is basically a K(n,m) bipartite graph. | ||
| 101 | + | ||
| 102 | + Each FabricDomain should be given a unique Domain ID (did) to ensure unique | ||
| 103 | + names and addressing. | ||
| 104 | + """ | ||
| 105 | + def __init__(self, did): | ||
| 106 | + Domain.__init__(self, did) | ||
| 107 | + | ||
| 108 | + def build(self, n=2, m=3, f=2): | ||
| 109 | + # K(n,m) in bipartite graph | ||
| 110 | + l_nsw=[] | ||
| 111 | + l_msw=[] | ||
| 112 | + | ||
| 113 | + # create n spine switches | ||
| 114 | + for sw in range(n): | ||
| 115 | + l_nsw.append(self.addSwitch('swn%s%s' % (self.getId(), sw+1), cls=UserSwitch)) | ||
| 116 | + | ||
| 117 | + # create connection point to optical core (a leaf switch) | ||
| 118 | + tsw = self.addSwitch('swm%s01' % self.getId(), cls=UserSwitch) | ||
| 119 | + self.addTether(tsw, 'sw000%s' % self.getId(), '0000ffffffff000%s' % self.getId()) | ||
| 120 | + l_msw.append(tsw) | ||
| 121 | + | ||
| 122 | + # attach f hosts to last m-1 leaves | ||
| 123 | + for sw in range(1, m): | ||
| 124 | + msw = self.addSwitch('swm%s0%s' % (self.getId(), sw+1), cls=UserSwitch) | ||
| 125 | + l_msw.append(msw) | ||
| 126 | + for h in range(f): | ||
| 127 | + host = self.addHost('h%s%s' % (self.getId(), sw * f+h+1), cls=IpHost, | ||
| 128 | + ip='10.0.%s.%s/24' % ((self.getId()+sw+1), (f+1)), | ||
| 129 | + gateway='10.0.%s.254' % (self.getId()+sw+1)) | ||
| 130 | + self.addLink(host, msw) | ||
| 131 | + # link up spines and leaves | ||
| 132 | + for nsw in l_nsw: | ||
| 133 | + for msw in l_msw: | ||
| 134 | + self.addLink(nsw, msw) | ||
| 135 | + | ||
| 136 | + def addTether(self, name, tname, tdpid): | ||
| 137 | + """ | ||
| 138 | + add an OVS with name 'tname' and dpid 'tdpid' for connecting fabric | ||
| 139 | + domains to the core. name: the UserSwitch to connect the OVS to. | ||
| 140 | + """ | ||
| 141 | + self.__tether = self.addSwitch(tname, dpid=tdpid) | ||
| 142 | + self.addLink(tname, name, port1=1) | ||
| 143 | + | ||
| 144 | + def getTether(self): | ||
| 145 | + """ get connection point of this fabric to the core """ | ||
| 146 | + return self.__tether | ||
| 147 | + | ||
| 148 | + | ||
| 149 | +class IpHost(Host): | ||
| 150 | + def __init__(self, name, gateway, *args, **kwargs): | ||
| 151 | + super(IpHost, self).__init__(name, *args, **kwargs) | ||
| 152 | + self.gateway = gateway | ||
| 153 | + | ||
| 154 | + def config(self, **kwargs): | ||
| 155 | + Host.config(self, **kwargs) | ||
| 156 | + mtu = "ifconfig "+self.name+"-eth0 mtu 1490" | ||
| 157 | + self.cmd(mtu) | ||
| 158 | + self.cmd('ip route add default via %s' % self.gateway) | ||
| 159 | + | ||
| 160 | +# fixed port numbers for attachment points (APs) between CORD and metro domains | ||
| 161 | +OVS_AP=2 | ||
| 162 | +OE_AP=10 | ||
| 163 | + | ||
| 164 | +def setup(argv): | ||
| 165 | + domains = [] | ||
| 166 | + ctlsets = sys.argv[1:] | ||
| 167 | + | ||
| 168 | + # the controllers for the optical domain | ||
| 169 | + d0 = OpticalDomain() | ||
| 170 | + domains.append(d0) | ||
| 171 | + ctls = ctlsets[0].split(',') | ||
| 172 | + for i in range (len(ctls)): | ||
| 173 | + d0.addController('c0%s' % i, controller=RemoteController, ip=ctls[i]) | ||
| 174 | + | ||
| 175 | + # the fabric domains - position 1 for domain 1, 2 for 2 ... | ||
| 176 | + for i in range (1,len(ctlsets)): | ||
| 177 | + f = FabricDomain(i) | ||
| 178 | + domains.append(f) | ||
| 179 | + ctls = ctlsets[i].split(',') | ||
| 180 | + for j in range (len(ctls)): | ||
| 181 | + f.addController('c%s%s' % (i,j), controller=RemoteController, ip=ctls[j]) | ||
| 182 | + | ||
| 183 | + # make/setup Mininet object | ||
| 184 | + net = Mininet() | ||
| 185 | + for d in domains: | ||
| 186 | + d.build() | ||
| 187 | + d.injectInto(net) | ||
| 188 | + | ||
| 189 | + # connect COs to core - sort of hard-wired at this moment | ||
| 190 | + for i in range(1,len(domains)): | ||
| 191 | + an = { "bandwidth": 100000, "optical.type": "cross-connect", "durable": "true" } | ||
| 192 | + net.addLink(domains[i].getTether(), d0.getSwitches('OE%s' % i), | ||
| 193 | + port1=OVS_AP, port2=OE_AP, speed=10000, annotations=an, cls=LINCLink) | ||
| 194 | + | ||
| 195 | + # fire everything up | ||
| 196 | + net.build() | ||
| 197 | + map(lambda x: x.start(), domains) | ||
| 198 | + | ||
| 199 | + # create a minimal copy of the network for configuring LINC. | ||
| 200 | + cfgnet = Mininet() | ||
| 201 | + cfgnet.switches = net.switches | ||
| 202 | + cfgnet.links = net.links | ||
| 203 | + cfgnet.controllers = d0.getControllers() | ||
| 204 | + LINCSwitch.bootOE(cfgnet, d0.getSwitches()) | ||
| 205 | + | ||
| 206 | + CLI(net) | ||
| 207 | + net.stop() | ||
| 208 | + LINCSwitch.shutdownOE() | ||
| 209 | + | ||
| 210 | +if __name__ == '__main__': | ||
| 211 | + setLogLevel('info') | ||
| 212 | + import sys | ||
| 213 | + if len(sys.argv) < 5: | ||
| 214 | + print ("Usage: sudo -E ./metro.py ctl-set1 ... ctl-set4\n\n", | ||
| 215 | + "Where ctl-set are comma-separated controller IP's") | ||
| 216 | + else: | ||
| 217 | + setup(sys.argv) |
-
Please register or login to post a comment