kelvin-onlab | d9e23de | 2015-08-06 10:34:44 -0700 | [diff] [blame] | 1 | #!/usr/bin/python |
| 2 | |
| 3 | """ |
Jeremy Ronquillo | b27ce4c | 2017-07-17 12:41:28 -0700 | [diff] [blame^] | 4 | Copyright 2015 Open Networking Foundation (ONF) |
| 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 |
| 13 | (at your option) any later version. |
| 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 | """ |
| 23 | |
| 24 | """ |
kelvin-onlab | d9e23de | 2015-08-06 10:34:44 -0700 | [diff] [blame] | 25 | Custom topology for Mininet |
| 26 | Author: kelvin@onlab.us |
| 27 | """ |
| 28 | from mininet.topo import Topo |
| 29 | from mininet.net import Mininet |
| 30 | from mininet.node import Host, RemoteController |
| 31 | from mininet.node import Node |
| 32 | from mininet.node import CPULimitedHost |
| 33 | from mininet.link import TCLink |
| 34 | from mininet.cli import CLI |
| 35 | from mininet.log import setLogLevel |
| 36 | from mininet.util import dumpNodeConnections |
| 37 | from mininet.node import ( UserSwitch, OVSSwitch, IVSSwitch ) |
| 38 | import sys |
| 39 | coreSwitches = {} |
| 40 | spineSwitches = {} |
| 41 | leafSwitches = {} |
| 42 | endSwitches = {} |
| 43 | allSwitches = {} |
| 44 | # Counters for nodes |
| 45 | totalSwitches = 0 |
| 46 | totalEndSwitches = 0 |
| 47 | totalHosts = 0 |
| 48 | endSwitchCount = 0 # total count of end switch in each row in gui |
| 49 | class spineTopo( Topo ): |
| 50 | |
| 51 | def __init__( self, **opts ): |
| 52 | "Create a topology." |
| 53 | Topo.__init__( self, **opts ) |
| 54 | |
| 55 | def build( self, s, l, c, e, h, **opts ): |
| 56 | """ |
| 57 | s = number of spine switches |
| 58 | l = number of leaf switches |
| 59 | c = number of core |
| 60 | e = number of end switch |
| 61 | h = number of end host |
| 62 | """ |
| 63 | global totalSwitches |
| 64 | global coreSwitches |
| 65 | global spineSwitches |
| 66 | global leafSwitches |
| 67 | global endSwitches |
| 68 | global totalEndSwitches |
| 69 | global totalHosts |
| 70 | global allSwitches |
| 71 | global endSwitchCount |
| 72 | endSwitchCount = e |
| 73 | |
| 74 | print "Creating topology with", s,"spine", l,"leaf", c,"core",\ |
| 75 | e,"end switches and",h,"host for each end switches" |
| 76 | |
| 77 | self.addCore( c ) |
| 78 | self.addSpine( s ) |
| 79 | self.addLeaf( l ) |
| 80 | self.linkLayer( coreSwitches, spineSwitches ) |
| 81 | self.linkLayer( spineSwitches, leafSwitches ) |
| 82 | self.linkEndSwitch( e, leafSwitches ) |
| 83 | self.linkHosts( h ) |
| 84 | |
| 85 | allSwitches = coreSwitches |
| 86 | allSwitches.update( spineSwitches ) |
| 87 | allSwitches.update( leafSwitches ) |
| 88 | allSwitches.update( endSwitches ) |
| 89 | deviceData = self.createSwitchDict() |
| 90 | self.genCfgJson( deviceData ) |
| 91 | |
| 92 | |
| 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 | |
| 137 | |
| 138 | def linkHosts( self, numHosts ): |
| 139 | global endSwitches |
| 140 | switches = sorted( endSwitches.values() ) |
| 141 | |
| 142 | for sw in switches: |
| 143 | for i in xrange( numHosts ): |
| 144 | self.addLink( sw, self.addEndHosts() ) |
| 145 | |
| 146 | |
| 147 | def linkLayer( self, topLayer, botLayer ): |
| 148 | """ |
| 149 | Description: |
| 150 | The top layer is the upper layer in the spine topology eg. top layer |
| 151 | can be the spine and the bottom layer is the leaf, another is the |
| 152 | core layer is the top layer and the spine is the bottom layer and |
| 153 | so on. |
| 154 | Required: |
| 155 | topLayer - Upper layer in the spine topology to be linked in the |
| 156 | layer below |
| 157 | botLater - Layer that is below the upper layer to be linked at |
| 158 | """ |
| 159 | |
| 160 | topSwitches = sorted( topLayer.keys() ) |
| 161 | botSwitches = sorted( botLayer.keys() ) |
| 162 | |
| 163 | for topSw in topSwitches: |
| 164 | for botSw in botSwitches: |
| 165 | self.addLink( topLayer.get( topSw ), botLayer.get( botSw ) ) |
| 166 | |
| 167 | |
| 168 | def linkEndSwitch( self, numSwitch, leafLayer ): |
| 169 | global totalSwitches |
| 170 | global totalEndSwitches |
| 171 | |
| 172 | leaf = sorted( leafLayer.values() ) |
| 173 | |
| 174 | for i in xrange( len( leafSwitches ) ): |
| 175 | if len( leafSwitches ) == 1: |
| 176 | for j in xrange( numSwitch ): |
| 177 | self.addLink( leaf[ 0 ], self.addEnd() ) |
| 178 | break |
| 179 | if len( leafSwitches ) == 2: |
| 180 | for j in xrange( numSwitch ): |
| 181 | endSw = self.addEnd() |
| 182 | self.addLink( leaf[ i ], endSw ) |
| 183 | self.addLink( leaf[ i + 1 ], endSw ) |
| 184 | break |
| 185 | if i == ( len( leafSwitches ) - 1 ) and len( leafSwitches )%2: |
| 186 | for j in xrange( numSwitch ): |
| 187 | self.addLink( leaf[ i ], self.addEnd() ) |
| 188 | break |
| 189 | if i == 0: |
| 190 | for j in xrange( numSwitch ): |
| 191 | endSw = self.addEnd() |
| 192 | self.addLink( leaf[ i ], endSw ) |
| 193 | self.addLink( leaf[ i + 1 ], endSw ) |
| 194 | continue |
| 195 | if i == 1: |
| 196 | continue |
| 197 | if i%2 == 0: |
| 198 | for j in xrange( numSwitch ): |
| 199 | endSw = self.addEnd() |
| 200 | self.addLink( leaf[ i ], endSw ) |
| 201 | self.addLink( leaf[ i + 1 ], endSw ) |
| 202 | |
| 203 | def genCfgJson( self, deviceData ): |
| 204 | import json |
| 205 | configJson = {} |
| 206 | configJson[ "devices" ] = deviceData |
| 207 | with open( 'spine.json', 'w+' ) as outfile: |
| 208 | json.dump( configJson, outfile ) |
| 209 | #cfgFile = open( "spine.json" , 'w+' ) |
| 210 | #cfgFile.write( configJson ) |
| 211 | #cfgFile.close() |
| 212 | |
| 213 | |
| 214 | |
| 215 | def createSwitchDict( self ): |
| 216 | global allSwitches |
| 217 | global endSwitchCount |
| 218 | |
| 219 | latitude = 0 |
| 220 | longitude = 0 |
| 221 | coreLong = -70 |
| 222 | spineLong = -80 |
| 223 | leafLong = -90 |
| 224 | endLat = 30 |
| 225 | rowCount = 0 # count of end switches or rows |
| 226 | colOffSet = 0 # off set for end switches; longitude |
| 227 | |
| 228 | #for i in xrange( len( allSwitches ) ): |
| 229 | deviceList = [] |
| 230 | deviceDict = {} |
| 231 | for sw in allSwitches: |
| 232 | tempSw = allSwitches.get( sw ) |
| 233 | uri = str( "{0:0>16}".format( str( hex( int( tempSw[ 1: ] ) )\ |
| 234 | ).split( "x" )[ 1 ] ) ) |
| 235 | mac = str( "{0:0>12}".format( str( hex( int( tempSw[ 1: ] ) )\ |
| 236 | ).split( "x" )[ 1 ] ) ) |
| 237 | |
| 238 | if "core" in sw: |
| 239 | latitude = 45 |
| 240 | longitude = coreLong |
| 241 | coreLong += 2.5 |
| 242 | elif "spine" in sw: |
| 243 | latitude = 40 |
| 244 | longitude = spineLong |
| 245 | spineLong += 1.5 |
| 246 | elif "leaf" in sw: |
| 247 | latitude = 35 |
| 248 | longitude = leafLong |
| 249 | leafLong += 1.5 |
| 250 | elif "end" in sw: |
| 251 | # Reset position and move to the right once every |
| 252 | # number of end switches |
| 253 | if rowCount == endSwitchCount: |
| 254 | colOffSet += 2.5 |
| 255 | rowCount = 0 |
| 256 | endLat = 30 |
| 257 | longitude = -80 + colOffSet |
| 258 | latitude = endLat |
| 259 | endLat -= 1 |
| 260 | rowCount += 1 |
| 261 | |
| 262 | tempItem = { "alias": allSwitches.get( sw ) , |
| 263 | "uri": "of:" + uri, |
| 264 | "mac": mac, |
| 265 | "annotations": { "name": sw, |
| 266 | "latitude": latitude, |
| 267 | "longitude": longitude }, |
| 268 | "type": "SWITCH" } |
| 269 | deviceList.append( tempItem ) |
| 270 | |
| 271 | return deviceList |
| 272 | #def createHostsJson( hostDict ): |
| 273 | |
| 274 | topos = { 'spine': ( lambda s=2, l=3, c=1, e=5, h=1: spineTopo( s=s, |
| 275 | l=l, |
| 276 | c=c, |
| 277 | e=e, |
| 278 | h=h) ) } |
| 279 | |
| 280 | # HERE THE CODE DEFINITION OF THE TOPOLOGY ENDS |
| 281 | |
| 282 | def setupNetwork(): |
| 283 | "Create network" |
| 284 | topo = spineTopo() |
| 285 | #if controller_ip == '': |
| 286 | #controller_ip = '10.0.2.2'; |
| 287 | # controller_ip = '127.0.0.1'; |
| 288 | network = Mininet( topo=topo, |
| 289 | autoSetMacs=True, |
| 290 | controller=None ) |
| 291 | network.start() |
| 292 | CLI( network ) |
| 293 | network.stop() |
| 294 | |
| 295 | if __name__ == '__main__': |
| 296 | setLogLevel( 'info' ) |
| 297 | #setLogLevel('debug') |
| 298 | setupNetwork() |