kelvin-onlab | d9e23de | 2015-08-06 10:34:44 -0700 | [diff] [blame] | 1 | #!/usr/bin/python |
| 2 | |
| 3 | """ |
Jeremy Ronquillo | 23fb216 | 2017-09-15 14:59:57 -0700 | [diff] [blame] | 4 | Copyright 2015 Open Networking Foundation ( ONF ) |
Jeremy Ronquillo | b27ce4c | 2017-07-17 12:41:28 -0700 | [diff] [blame] | 5 | |
| 6 | Please refer questions to either the onos test mailing list at <onos-test@onosproject.org>, |
| 7 | the System Testing Plans and Results wiki page at <https://wiki.onosproject.org/x/voMg>, |
| 8 | or the System Testing Guide page at <https://wiki.onosproject.org/x/WYQg> |
| 9 | |
| 10 | TestON is free software: you can redistribute it and/or modify |
| 11 | it under the terms of the GNU General Public License as published by |
| 12 | the Free Software Foundation, either version 2 of the License, or |
Jeremy Ronquillo | 23fb216 | 2017-09-15 14:59:57 -0700 | [diff] [blame] | 13 | ( at your option ) any later version. |
Jeremy Ronquillo | b27ce4c | 2017-07-17 12:41:28 -0700 | [diff] [blame] | 14 | |
| 15 | TestON is distributed in the hope that it will be useful, |
| 16 | but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 17 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 18 | GNU General Public License for more details. |
| 19 | |
| 20 | You should have received a copy of the GNU General Public License |
| 21 | along with TestON. If not, see <http://www.gnu.org/licenses/>. |
| 22 | """ |
Jeremy Ronquillo | b27ce4c | 2017-07-17 12:41:28 -0700 | [diff] [blame] | 23 | """ |
kelvin-onlab | d9e23de | 2015-08-06 10:34:44 -0700 | [diff] [blame] | 24 | Custom topology for Mininet |
| 25 | Author: kelvin@onlab.us |
| 26 | """ |
| 27 | from mininet.topo import Topo |
| 28 | from mininet.net import Mininet |
| 29 | from mininet.node import Host, RemoteController |
| 30 | from mininet.node import Node |
| 31 | from mininet.node import CPULimitedHost |
| 32 | from mininet.link import TCLink |
| 33 | from mininet.cli import CLI |
| 34 | from mininet.log import setLogLevel |
| 35 | from mininet.util import dumpNodeConnections |
| 36 | from mininet.node import ( UserSwitch, OVSSwitch, IVSSwitch ) |
| 37 | import sys |
| 38 | coreSwitches = {} |
| 39 | spineSwitches = {} |
| 40 | leafSwitches = {} |
| 41 | endSwitches = {} |
| 42 | allSwitches = {} |
| 43 | # Counters for nodes |
| 44 | totalSwitches = 0 |
| 45 | totalEndSwitches = 0 |
| 46 | totalHosts = 0 |
Jeremy Ronquillo | 23fb216 | 2017-09-15 14:59:57 -0700 | [diff] [blame] | 47 | endSwitchCount = 0 # total count of end switch in each row in gui |
| 48 | |
| 49 | |
kelvin-onlab | d9e23de | 2015-08-06 10:34:44 -0700 | [diff] [blame] | 50 | class spineTopo( Topo ): |
| 51 | |
| 52 | def __init__( self, **opts ): |
| 53 | "Create a topology." |
| 54 | Topo.__init__( self, **opts ) |
| 55 | |
| 56 | def build( self, s, l, c, e, h, **opts ): |
| 57 | """ |
| 58 | s = number of spine switches |
| 59 | l = number of leaf switches |
| 60 | c = number of core |
| 61 | e = number of end switch |
| 62 | h = number of end host |
| 63 | """ |
| 64 | global totalSwitches |
| 65 | global coreSwitches |
| 66 | global spineSwitches |
| 67 | global leafSwitches |
| 68 | global endSwitches |
| 69 | global totalEndSwitches |
| 70 | global totalHosts |
| 71 | global allSwitches |
| 72 | global endSwitchCount |
| 73 | endSwitchCount = e |
| 74 | |
Jeremy Ronquillo | 23fb216 | 2017-09-15 14:59:57 -0700 | [diff] [blame] | 75 | print "Creating topology with", s, "spine", l, "leaf", c, "core",\ |
| 76 | e, "end switches and", h, "host for each end switches" |
kelvin-onlab | d9e23de | 2015-08-06 10:34:44 -0700 | [diff] [blame] | 77 | |
| 78 | self.addCore( c ) |
| 79 | self.addSpine( s ) |
| 80 | self.addLeaf( l ) |
| 81 | self.linkLayer( coreSwitches, spineSwitches ) |
| 82 | self.linkLayer( spineSwitches, leafSwitches ) |
| 83 | self.linkEndSwitch( e, leafSwitches ) |
| 84 | self.linkHosts( h ) |
| 85 | |
| 86 | allSwitches = coreSwitches |
| 87 | allSwitches.update( spineSwitches ) |
| 88 | allSwitches.update( leafSwitches ) |
| 89 | allSwitches.update( endSwitches ) |
| 90 | deviceData = self.createSwitchDict() |
| 91 | self.genCfgJson( deviceData ) |
| 92 | |
kelvin-onlab | d9e23de | 2015-08-06 10:34:44 -0700 | [diff] [blame] | 93 | def addCore( self, numSwitch ): |
| 94 | global totalSwitches |
| 95 | global coreSwitches |
| 96 | for i in range( numSwitch ): |
| 97 | coreSwitches[ 'core' + str( i + 1 ) ] = self.addSwitch( |
| 98 | 's' + str( totalSwitches + 1 ) ) |
| 99 | totalSwitches += 1 |
| 100 | |
| 101 | def addSpine( self, numSwitch ): |
| 102 | global totalSwitches |
| 103 | global spineSwitches |
| 104 | for i in range( numSwitch ): |
| 105 | spineSwitches[ 'spine' + str( i + 1 ) ] = self.addSwitch( |
| 106 | 's' + str( totalSwitches + 1 ) ) |
| 107 | totalSwitches += 1 |
| 108 | |
| 109 | def addLeaf( self, numSwitch ): |
| 110 | global totalSwitches |
| 111 | global leafSwitches |
| 112 | for i in range( numSwitch ): |
| 113 | leafSwitches[ 'leaf' + str( i + 1 ) ] = self.addSwitch( |
| 114 | 's' + str( totalSwitches + 1 ) ) |
| 115 | totalSwitches += 1 |
| 116 | |
| 117 | def addEnd( self ): |
| 118 | global totalSwitches |
| 119 | global totalEndSwitches |
| 120 | global endSwitches |
| 121 | |
| 122 | endSwitches[ 'end' + str( totalEndSwitches + 1 ) ] = self.addSwitch( |
| 123 | 's' + str( totalSwitches + 1 ) ) |
| 124 | totalSwitches += 1 |
| 125 | totalEndSwitches += 1 |
| 126 | |
| 127 | return endSwitches[ 'end' + str( totalEndSwitches ) ] |
| 128 | |
| 129 | def addEndHosts( self ): |
| 130 | global totalHosts |
| 131 | |
| 132 | totalHosts += 1 |
| 133 | host = self.addHost( 'h' + str( totalHosts ) ) |
| 134 | |
| 135 | return host |
| 136 | |
kelvin-onlab | d9e23de | 2015-08-06 10:34:44 -0700 | [diff] [blame] | 137 | def linkHosts( self, numHosts ): |
| 138 | global endSwitches |
| 139 | switches = sorted( endSwitches.values() ) |
| 140 | |
| 141 | for sw in switches: |
| 142 | for i in xrange( numHosts ): |
| 143 | self.addLink( sw, self.addEndHosts() ) |
| 144 | |
kelvin-onlab | d9e23de | 2015-08-06 10:34:44 -0700 | [diff] [blame] | 145 | def linkLayer( self, topLayer, botLayer ): |
| 146 | """ |
| 147 | Description: |
| 148 | The top layer is the upper layer in the spine topology eg. top layer |
| 149 | can be the spine and the bottom layer is the leaf, another is the |
| 150 | core layer is the top layer and the spine is the bottom layer and |
| 151 | so on. |
| 152 | Required: |
| 153 | topLayer - Upper layer in the spine topology to be linked in the |
| 154 | layer below |
| 155 | botLater - Layer that is below the upper layer to be linked at |
| 156 | """ |
kelvin-onlab | d9e23de | 2015-08-06 10:34:44 -0700 | [diff] [blame] | 157 | topSwitches = sorted( topLayer.keys() ) |
| 158 | botSwitches = sorted( botLayer.keys() ) |
| 159 | |
| 160 | for topSw in topSwitches: |
| 161 | for botSw in botSwitches: |
| 162 | self.addLink( topLayer.get( topSw ), botLayer.get( botSw ) ) |
| 163 | |
kelvin-onlab | d9e23de | 2015-08-06 10:34:44 -0700 | [diff] [blame] | 164 | def linkEndSwitch( self, numSwitch, leafLayer ): |
| 165 | global totalSwitches |
| 166 | global totalEndSwitches |
| 167 | |
| 168 | leaf = sorted( leafLayer.values() ) |
| 169 | |
| 170 | for i in xrange( len( leafSwitches ) ): |
| 171 | if len( leafSwitches ) == 1: |
| 172 | for j in xrange( numSwitch ): |
| 173 | self.addLink( leaf[ 0 ], self.addEnd() ) |
| 174 | break |
| 175 | if len( leafSwitches ) == 2: |
| 176 | for j in xrange( numSwitch ): |
| 177 | endSw = self.addEnd() |
| 178 | self.addLink( leaf[ i ], endSw ) |
| 179 | self.addLink( leaf[ i + 1 ], endSw ) |
| 180 | break |
Jeremy Ronquillo | 23fb216 | 2017-09-15 14:59:57 -0700 | [diff] [blame] | 181 | if i == ( len( leafSwitches ) - 1 ) and len( leafSwitches ) % 2: |
kelvin-onlab | d9e23de | 2015-08-06 10:34:44 -0700 | [diff] [blame] | 182 | for j in xrange( numSwitch ): |
| 183 | self.addLink( leaf[ i ], self.addEnd() ) |
| 184 | break |
| 185 | if i == 0: |
| 186 | for j in xrange( numSwitch ): |
| 187 | endSw = self.addEnd() |
| 188 | self.addLink( leaf[ i ], endSw ) |
| 189 | self.addLink( leaf[ i + 1 ], endSw ) |
| 190 | continue |
| 191 | if i == 1: |
| 192 | continue |
Jeremy Ronquillo | 23fb216 | 2017-09-15 14:59:57 -0700 | [diff] [blame] | 193 | if i % 2 == 0: |
kelvin-onlab | d9e23de | 2015-08-06 10:34:44 -0700 | [diff] [blame] | 194 | for j in xrange( numSwitch ): |
| 195 | endSw = self.addEnd() |
| 196 | self.addLink( leaf[ i ], endSw ) |
| 197 | self.addLink( leaf[ i + 1 ], endSw ) |
| 198 | |
| 199 | def genCfgJson( self, deviceData ): |
| 200 | import json |
| 201 | configJson = {} |
| 202 | configJson[ "devices" ] = deviceData |
| 203 | with open( 'spine.json', 'w+' ) as outfile: |
| 204 | json.dump( configJson, outfile ) |
Jeremy Ronquillo | 23fb216 | 2017-09-15 14:59:57 -0700 | [diff] [blame] | 205 | # cfgFile = open( "spine.json" , 'w+' ) |
| 206 | # cfgFile.write( configJson ) |
| 207 | # cfgFile.close() |
kelvin-onlab | d9e23de | 2015-08-06 10:34:44 -0700 | [diff] [blame] | 208 | |
| 209 | def createSwitchDict( self ): |
| 210 | global allSwitches |
| 211 | global endSwitchCount |
| 212 | |
| 213 | latitude = 0 |
| 214 | longitude = 0 |
| 215 | coreLong = -70 |
| 216 | spineLong = -80 |
| 217 | leafLong = -90 |
| 218 | endLat = 30 |
Jeremy Ronquillo | 23fb216 | 2017-09-15 14:59:57 -0700 | [diff] [blame] | 219 | rowCount = 0 # count of end switches or rows |
| 220 | colOffSet = 0 # off set for end switches; longitude |
kelvin-onlab | d9e23de | 2015-08-06 10:34:44 -0700 | [diff] [blame] | 221 | |
Jeremy Ronquillo | 23fb216 | 2017-09-15 14:59:57 -0700 | [diff] [blame] | 222 | # for i in xrange( len( allSwitches ) ): |
kelvin-onlab | d9e23de | 2015-08-06 10:34:44 -0700 | [diff] [blame] | 223 | deviceList = [] |
| 224 | deviceDict = {} |
| 225 | for sw in allSwitches: |
| 226 | tempSw = allSwitches.get( sw ) |
Jeremy Ronquillo | 23fb216 | 2017-09-15 14:59:57 -0700 | [diff] [blame] | 227 | uri = str( "{0:0>16}".format( str( hex( int( tempSw[ 1: ] ) ) |
| 228 | ).split( "x" )[ 1 ] ) ) |
| 229 | mac = str( "{0:0>12}".format( str( hex( int( tempSw[ 1: ] ) ) |
| 230 | ).split( "x" )[ 1 ] ) ) |
kelvin-onlab | d9e23de | 2015-08-06 10:34:44 -0700 | [diff] [blame] | 231 | |
| 232 | if "core" in sw: |
| 233 | latitude = 45 |
| 234 | longitude = coreLong |
| 235 | coreLong += 2.5 |
| 236 | elif "spine" in sw: |
| 237 | latitude = 40 |
| 238 | longitude = spineLong |
| 239 | spineLong += 1.5 |
| 240 | elif "leaf" in sw: |
| 241 | latitude = 35 |
| 242 | longitude = leafLong |
| 243 | leafLong += 1.5 |
| 244 | elif "end" in sw: |
| 245 | # Reset position and move to the right once every |
| 246 | # number of end switches |
| 247 | if rowCount == endSwitchCount: |
| 248 | colOffSet += 2.5 |
| 249 | rowCount = 0 |
| 250 | endLat = 30 |
| 251 | longitude = -80 + colOffSet |
| 252 | latitude = endLat |
| 253 | endLat -= 1 |
| 254 | rowCount += 1 |
| 255 | |
Jeremy Ronquillo | 23fb216 | 2017-09-15 14:59:57 -0700 | [diff] [blame] | 256 | tempItem = { "alias": allSwitches.get( sw ), |
kelvin-onlab | d9e23de | 2015-08-06 10:34:44 -0700 | [diff] [blame] | 257 | "uri": "of:" + uri, |
| 258 | "mac": mac, |
| 259 | "annotations": { "name": sw, |
| 260 | "latitude": latitude, |
| 261 | "longitude": longitude }, |
| 262 | "type": "SWITCH" } |
| 263 | deviceList.append( tempItem ) |
| 264 | |
| 265 | return deviceList |
Jeremy Ronquillo | 23fb216 | 2017-09-15 14:59:57 -0700 | [diff] [blame] | 266 | # def createHostsJson( hostDict ): |
kelvin-onlab | d9e23de | 2015-08-06 10:34:44 -0700 | [diff] [blame] | 267 | |
| 268 | topos = { 'spine': ( lambda s=2, l=3, c=1, e=5, h=1: spineTopo( s=s, |
| 269 | l=l, |
| 270 | c=c, |
| 271 | e=e, |
Jeremy Ronquillo | 23fb216 | 2017-09-15 14:59:57 -0700 | [diff] [blame] | 272 | h=h ) ) } |
kelvin-onlab | d9e23de | 2015-08-06 10:34:44 -0700 | [diff] [blame] | 273 | |
| 274 | # HERE THE CODE DEFINITION OF THE TOPOLOGY ENDS |
| 275 | |
Jeremy Ronquillo | 23fb216 | 2017-09-15 14:59:57 -0700 | [diff] [blame] | 276 | |
kelvin-onlab | d9e23de | 2015-08-06 10:34:44 -0700 | [diff] [blame] | 277 | def setupNetwork(): |
| 278 | "Create network" |
| 279 | topo = spineTopo() |
Jeremy Ronquillo | 23fb216 | 2017-09-15 14:59:57 -0700 | [diff] [blame] | 280 | # if controller_ip == '': |
| 281 | # controller_ip = '10.0.2.2'; |
kelvin-onlab | d9e23de | 2015-08-06 10:34:44 -0700 | [diff] [blame] | 282 | # controller_ip = '127.0.0.1'; |
| 283 | network = Mininet( topo=topo, |
| 284 | autoSetMacs=True, |
| 285 | controller=None ) |
| 286 | network.start() |
| 287 | CLI( network ) |
| 288 | network.stop() |
| 289 | |
| 290 | if __name__ == '__main__': |
| 291 | setLogLevel( 'info' ) |
Jeremy Ronquillo | 23fb216 | 2017-09-15 14:59:57 -0700 | [diff] [blame] | 292 | # setLogLevel( 'debug' ) |
kelvin-onlab | d9e23de | 2015-08-06 10:34:44 -0700 | [diff] [blame] | 293 | setupNetwork() |