blob: 949fe0c0ddf75c7081011fc59ea95a8c62c31a5b [file] [log] [blame]
Carmelo Cascone785fada2016-06-16 18:34:16 -07001#!/usr/bin/python
2
Carmelo Cascone977ae3f2016-06-23 19:28:28 -07003import os
4import sys
Yi Tseng7875cb72017-08-08 10:15:58 -07005import json
Carmelo Cascone785fada2016-06-16 18:34:16 -07006import argparse
Carmelo Cascone34433252017-08-25 20:27:18 +02007from collections import OrderedDict
Carmelo Cascone977ae3f2016-06-23 19:28:28 -07008
Yi Tseng7875cb72017-08-08 10:15:58 -07009TEMP_NETCFG_FILE = '/tmp/bmv2-demo-cfg.json'
10BASE_LONGITUDE = -115
11SWITCH_BASE_LATITUDE = 25
12HOST_BASE_LATITUDE = 28
13BASE_SHIFT = 8
14VLAN_NONE = -1
15DEFAULT_SW_BW = 50
16DEFAULT_HOST_BW = 25
Jonghwan Hyunc235d462019-01-30 23:31:48 +090017# Jumbo frame
18JUMBO_MTU=9000
Yi Tseng7875cb72017-08-08 10:15:58 -070019
Carmelo Cascone977ae3f2016-06-23 19:28:28 -070020if 'ONOS_ROOT' not in os.environ:
21 print "Environment var $ONOS_ROOT not set"
22 exit()
23else:
Carmelo Cascone12e4d8d2016-07-05 15:55:15 -070024 ONOS_ROOT = os.environ["ONOS_ROOT"]
25 sys.path.append(ONOS_ROOT + "/tools/dev/mininet")
Devin Lim0d944e22017-06-23 15:17:53 -070026if 'RUN_PACK_PATH' not in os.environ:
27 print "Environment var $RUN_PACK_PATH not set"
28 exit()
29else:
30 RUN_PACK_PATH = os.environ["RUN_PACK_PATH"]
Carmelo Cascone12e4d8d2016-07-05 15:55:15 -070031
32from onos import ONOSCluster, ONOSCLI
Carmelo Cascone34433252017-08-25 20:27:18 +020033from bmv2 import ONOSBmv2Switch, ONOSHost
Carmelo Cascone977ae3f2016-06-23 19:28:28 -070034
Carmelo Cascone785fada2016-06-16 18:34:16 -070035from itertools import combinations
36from time import sleep
Carmelo Cascone12e4d8d2016-07-05 15:55:15 -070037from subprocess import call
Carmelo Cascone785fada2016-06-16 18:34:16 -070038
Carmelo Cascone785fada2016-06-16 18:34:16 -070039from mininet.cli import CLI
40from mininet.link import TCLink
41from mininet.log import setLogLevel
42from mininet.net import Mininet
43from mininet.node import RemoteController, Host
Yi Tseng7875cb72017-08-08 10:15:58 -070044from mininet.topo import Topo
Carmelo Cascone785fada2016-06-16 18:34:16 -070045
Carmelo Cascone73f6b6d2017-08-24 13:17:55 +020046
Carmelo Cascone6ec8f8f2017-11-22 14:27:06 -080047def getCmdBg(cmd, logfile="/dev/null"):
48 return "{} > {} 2>&1 &".format(cmd, logfile)
49
50
Carmelo Cascone785fada2016-06-16 18:34:16 -070051class ClosTopo(Topo):
Carmelo Cascone6ec8f8f2017-11-22 14:27:06 -080052 """2 stage Clos topology"""
Carmelo Cascone785fada2016-06-16 18:34:16 -070053
Carmelo Cascone34433252017-08-25 20:27:18 +020054 def __init__(self, args, **opts):
Carmelo Cascone785fada2016-06-16 18:34:16 -070055 # Initialize topology and default options
56 Topo.__init__(self, **opts)
57
Carmelo Cascone34433252017-08-25 20:27:18 +020058 bmv2SwitchIds = []
59 for row in (1, 2):
60 for col in range(1, args.size + 1):
61 bmv2SwitchIds.append("s%d%d" % (row, col))
62
Carmelo Cascone785fada2016-06-16 18:34:16 -070063 bmv2Switches = {}
64
Carmelo Cascone785fada2016-06-16 18:34:16 -070065 for switchId in bmv2SwitchIds:
Carmelo Cascone73f6b6d2017-08-24 13:17:55 +020066 deviceId = int(switchId[1:])
Carmelo Cascone6ec8f8f2017-11-22 14:27:06 -080067 # Use first number in device id to calculate latitude (row number),
68 # use second to calculate longitude (column number)
Yi Tseng7875cb72017-08-08 10:15:58 -070069 latitude = SWITCH_BASE_LATITUDE + (deviceId // 10) * BASE_SHIFT
Yi Tseng7875cb72017-08-08 10:15:58 -070070 longitude = BASE_LONGITUDE + (deviceId % 10) * BASE_SHIFT
Carmelo Cascone6ec8f8f2017-11-22 14:27:06 -080071
Carmelo Cascone785fada2016-06-16 18:34:16 -070072 bmv2Switches[switchId] = self.addSwitch(switchId,
73 cls=ONOSBmv2Switch,
Carmelo Cascone6ec8f8f2017-11-22 14:27:06 -080074 loglevel=args.log_level,
Yi Tseng7875cb72017-08-08 10:15:58 -070075 deviceId=deviceId,
Carmelo Cascone6ec8f8f2017-11-22 14:27:06 -080076 netcfg=True,
77 netcfgDelay=0.5,
Yi Tseng7875cb72017-08-08 10:15:58 -070078 longitude=longitude,
79 latitude=latitude,
Carmelo Casconebf54baa2018-04-15 18:15:02 -070080 pipeconf=args.pipeconf_id)
Carmelo Cascone785fada2016-06-16 18:34:16 -070081
Carmelo Cascone34433252017-08-25 20:27:18 +020082 for i in range(1, args.size + 1):
83 for j in range(1, args.size + 1):
Carmelo Cascone785fada2016-06-16 18:34:16 -070084 if i == j:
Carmelo Cascone6ec8f8f2017-11-22 14:27:06 -080085 self.addLink(bmv2Switches["s1%d" % i],
86 bmv2Switches["s2%d" % j],
Yi Tseng7875cb72017-08-08 10:15:58 -070087 cls=TCLink, bw=DEFAULT_SW_BW)
Carmelo Cascone34433252017-08-25 20:27:18 +020088 if args.with_imbalanced_striping:
Carmelo Cascone6ec8f8f2017-11-22 14:27:06 -080089 # 2 links
90 self.addLink(bmv2Switches["s1%d" % i],
91 bmv2Switches["s2%d" % j],
Carmelo Cascone34433252017-08-25 20:27:18 +020092 cls=TCLink, bw=DEFAULT_SW_BW)
Carmelo Cascone785fada2016-06-16 18:34:16 -070093 else:
Carmelo Cascone6ec8f8f2017-11-22 14:27:06 -080094 self.addLink(bmv2Switches["s1%d" % i],
95 bmv2Switches["s2%d" % j],
Yi Tseng7875cb72017-08-08 10:15:58 -070096 cls=TCLink, bw=DEFAULT_SW_BW)
Carmelo Cascone785fada2016-06-16 18:34:16 -070097
Carmelo Cascone34433252017-08-25 20:27:18 +020098 for hostId in range(1, args.size + 1):
Carmelo Cascone785fada2016-06-16 18:34:16 -070099 host = self.addHost("h%d" % hostId,
100 cls=DemoHost,
101 ip="10.0.0.%d/24" % hostId,
102 mac='00:00:00:00:00:%02x' % hostId)
Carmelo Cascone6ec8f8f2017-11-22 14:27:06 -0800103 self.addLink(host, bmv2Switches["s1%d" % hostId],
104 cls=TCLink, bw=DEFAULT_HOST_BW)
Carmelo Cascone785fada2016-06-16 18:34:16 -0700105
106
Carmelo Cascone34433252017-08-25 20:27:18 +0200107class DemoHost(ONOSHost):
Carmelo Cascone6ec8f8f2017-11-22 14:27:06 -0800108 """Demo host"""
Carmelo Cascone785fada2016-06-16 18:34:16 -0700109
Carmelo Cascone34433252017-08-25 20:27:18 +0200110 def __init__(self, name, **params):
111 ONOSHost.__init__(self, name, **params)
Carmelo Cascone785fada2016-06-16 18:34:16 -0700112 self.exectoken = "/tmp/mn-exec-token-host-%s" % name
113 self.cmd("touch %s" % self.exectoken)
114
Carmelo Cascone785fada2016-06-16 18:34:16 -0700115 def startPingBg(self, h):
116 self.cmd(self.getInfiniteCmdBg("ping -i0.5 %s" % h.IP()))
117 self.cmd(self.getInfiniteCmdBg("arping -w5000000 %s" % h.IP()))
118
119 def startIperfServer(self):
Carmelo Cascone6e854042017-09-11 21:37:53 +0200120 self.cmd(self.getInfiniteCmdBg("iperf -s -u"))
Carmelo Cascone785fada2016-06-16 18:34:16 -0700121
122 def startIperfClient(self, h, flowBw="512k", numFlows=5, duration=5):
Carmelo Cascone6ec8f8f2017-11-22 14:27:06 -0800123 iperfCmd = "iperf -c{} -u -b{} -P{} -t{}".format(
124 h.IP(), flowBw, numFlows, duration)
125 self.cmd(self.getInfiniteCmdBg(iperfCmd, delay=0))
Carmelo Cascone785fada2016-06-16 18:34:16 -0700126
Carmelo Cascone6ec8f8f2017-11-22 14:27:06 -0800127 def stop(self, **kwargs):
Carmelo Cascone6e854042017-09-11 21:37:53 +0200128 self.cmd("killall iperf")
Carmelo Cascone785fada2016-06-16 18:34:16 -0700129 self.cmd("killall ping")
130 self.cmd("killall arping")
131
132 def describe(self):
133 print "**********"
134 print self.name
135 print "default interface: %s\t%s\t%s" % (
136 self.defaultIntf().name,
137 self.defaultIntf().IP(),
138 self.defaultIntf().MAC()
139 )
140 print "**********"
141
Carmelo Cascone6ec8f8f2017-11-22 14:27:06 -0800142 def getInfiniteCmdBg(self, cmd, logfile="/dev/null", delay=1):
Carmelo Cascone785fada2016-06-16 18:34:16 -0700143 return "(while [ -e {} ]; " \
144 "do {}; " \
145 "sleep {}; " \
Carmelo Cascone6ec8f8f2017-11-22 14:27:06 -0800146 "done;) > {} 2>&1 &".format(self.exectoken, cmd, delay, logfile)
Carmelo Cascone785fada2016-06-16 18:34:16 -0700147
Carmelo Cascone73f6b6d2017-08-24 13:17:55 +0200148
149def generateNetcfg(onosIp, net, args):
Carmelo Cascone34433252017-08-25 20:27:18 +0200150 netcfg = OrderedDict()
Carmelo Cascone6ec8f8f2017-11-22 14:27:06 -0800151
152 netcfg['hosts'] = {}
Carmelo Cascone34433252017-08-25 20:27:18 +0200153 netcfg['devices'] = {}
154 netcfg['links'] = {}
Carmelo Cascone6ec8f8f2017-11-22 14:27:06 -0800155
156 if args.full_netcfg:
157 # Device configs
158 for sw in net.switches:
159 srcIp = sw.getSourceIp(onosIp)
160 netcfg['devices'][sw.onosDeviceId] = sw.getDeviceConfig(srcIp)
Yi Tseng7875cb72017-08-08 10:15:58 -0700161
162 hostLocations = {}
Yi Tseng7875cb72017-08-08 10:15:58 -0700163 for link in net.links:
164 switchPort = link.intf1.name.split('-')
Carmelo Cascone73f6b6d2017-08-24 13:17:55 +0200165 sw1Name = switchPort[0] # s11
166 port1Name = switchPort[1] # eth0
Yi Tseng7875cb72017-08-08 10:15:58 -0700167 port1 = port1Name[3:]
168 switchPort = link.intf2.name.split('-')
169 sw2Name = switchPort[0]
170 port2Name = switchPort[1]
171 port2 = port2Name[3:]
172 sw1 = net[sw1Name]
173 sw2 = net[sw2Name]
174 if isinstance(sw1, Host):
175 # record host location and ignore it
176 # e.g. {'h1': 'device:bmv2:11'}
177 hostLocations[sw1.name] = '%s/%s' % (sw2.onosDeviceId, port2)
178 continue
179
180 if isinstance(sw2, Host):
181 # record host location and ignore it
182 # e.g. {'h1': 'device:bmv2:11'}
183 hostLocations[sw2.name] = '%s/%s' % (sw1.onosDeviceId, port1)
184 continue
185
Carmelo Cascone6ec8f8f2017-11-22 14:27:06 -0800186 if args.full_netcfg:
187 # Link configs
188 for linkId in ('%s/%s-%s/%s' % (sw1.onosDeviceId, port1,
189 sw2.onosDeviceId, port2),
190 '%s/%s-%s/%s' % (sw2.onosDeviceId, port2,
191 sw1.onosDeviceId, port1)):
192 netcfg['links'][linkId] = {
193 'basic': {
194 'type': 'DIRECT',
195 'bandwidth': DEFAULT_SW_BW
196 }
Carmelo Cascone34433252017-08-25 20:27:18 +0200197 }
Yi Tseng7875cb72017-08-08 10:15:58 -0700198
199 # Host configs
200 longitude = BASE_LONGITUDE
201 for host in net.hosts:
202 longitude = longitude + BASE_SHIFT
203 hostDefaultIntf = host.defaultIntf()
204 hostMac = host.MAC(hostDefaultIntf)
205 hostIp = host.IP(hostDefaultIntf)
206 hostId = '%s/%d' % (hostMac, VLAN_NONE)
207 location = hostLocations[host.name]
208
209 # use host Id to generate host location
210 hostConfig = {
211 'basic': {
212 'locations': [location],
213 'ips': [hostIp],
214 'name': host.name,
215 'latitude': HOST_BASE_LATITUDE,
216 'longitude': longitude
217 }
218 }
219 netcfg['hosts'][hostId] = hostConfig
220
Carmelo Cascone6ec8f8f2017-11-22 14:27:06 -0800221 if args.full_netcfg:
222 netcfg["apps"] = {
223 "org.onosproject.core": {
224 "core": {
225 "linkDiscoveryMode": "STRICT"
226 }
Carmelo Cascone34433252017-08-25 20:27:18 +0200227 }
228 }
Carmelo Cascone34433252017-08-25 20:27:18 +0200229
Yi Tseng7875cb72017-08-08 10:15:58 -0700230 print "Writing network config to %s" % TEMP_NETCFG_FILE
231 with open(TEMP_NETCFG_FILE, 'w') as tempFile:
Carmelo Cascone73f6b6d2017-08-24 13:17:55 +0200232 json.dump(netcfg, tempFile, indent=4)
233
Jonghwan Hyunc235d462019-01-30 23:31:48 +0900234def setMTU(net, mtu):
235 for link in net.links:
236 intf1 = link.intf1.name
237 switchPort = intf1.split('-')
238 sw1Name = switchPort[0] # s11
239 sw1 = net[sw1Name]
240
241 intf2 = link.intf2.name
242 switchPort = intf2.split('-')
243 sw2Name = switchPort[0]
244 sw2 = net[sw2Name]
245
246 if isinstance(sw1, Host):
247 continue
248
249 if isinstance(sw2, Host):
250 continue
251
252 call(('ifconfig', intf1, 'mtu', str(mtu)))
253 call(('ifconfig', intf2, 'mtu', str(mtu)))
Carmelo Cascone785fada2016-06-16 18:34:16 -0700254
255def main(args):
Carmelo Cascone12e4d8d2016-07-05 15:55:15 -0700256 if not args.onos_ip:
257 controller = ONOSCluster('c0', 3)
258 onosIp = controller.nodes()[0].IP()
259 else:
Yi Tseng7875cb72017-08-08 10:15:58 -0700260 controller = RemoteController('c0', ip=args.onos_ip)
Carmelo Cascone12e4d8d2016-07-05 15:55:15 -0700261 onosIp = args.onos_ip
Carmelo Cascone785fada2016-06-16 18:34:16 -0700262
Carmelo Cascone34433252017-08-25 20:27:18 +0200263 topo = ClosTopo(args)
Yi Tseng7875cb72017-08-08 10:15:58 -0700264
Carmelo Cascone12e4d8d2016-07-05 15:55:15 -0700265 net = Mininet(topo=topo, build=False, controller=[controller])
Carmelo Cascone785fada2016-06-16 18:34:16 -0700266
267 net.build()
268 net.start()
269
Carmelo Cascone12e4d8d2016-07-05 15:55:15 -0700270 print "Network started"
Carmelo Cascone785fada2016-06-16 18:34:16 -0700271
Carmelo Cascone6ec8f8f2017-11-22 14:27:06 -0800272 # Always generate background pings.
Carmelo Cascone785fada2016-06-16 18:34:16 -0700273 sleep(3)
274 for (h1, h2) in combinations(net.hosts, 2):
275 h1.startPingBg(h2)
276 h2.startPingBg(h1)
277
Carmelo Cascone12e4d8d2016-07-05 15:55:15 -0700278 print "Background ping started"
279
Jonghwan Hyunc235d462019-01-30 23:31:48 +0900280 # Increase the MTU size for INT operation
281 if args.pipeconf_id.endswith("int") or args.pipeconf_id.endswith("full"):
282 setMTU(net, JUMBO_MTU)
283
Carmelo Cascone785fada2016-06-16 18:34:16 -0700284 for h in net.hosts:
285 h.startIperfServer()
286
Carmelo Cascone12e4d8d2016-07-05 15:55:15 -0700287 print "Iperf servers started"
Carmelo Cascone785fada2016-06-16 18:34:16 -0700288
Carmelo Cascone6ec8f8f2017-11-22 14:27:06 -0800289 if args.bg_traffic:
290 sleep(4)
291 print "Starting iperf clients..."
292 net.hosts[0].startIperfClient(net.hosts[-1], flowBw="400k",
293 numFlows=50, duration=10)
Carmelo Cascone785fada2016-06-16 18:34:16 -0700294
Carmelo Cascone73f6b6d2017-08-24 13:17:55 +0200295 generateNetcfg(onosIp, net, args)
Yi Tseng7875cb72017-08-08 10:15:58 -0700296
Carmelo Cascone6e854042017-09-11 21:37:53 +0200297 if args.netcfg_sleep > 0:
Carmelo Cascone6ec8f8f2017-11-22 14:27:06 -0800298 print "Waiting %d seconds before pushing config to ONOS..." \
299 % args.netcfg_sleep
Carmelo Cascone6e854042017-09-11 21:37:53 +0200300 sleep(args.netcfg_sleep)
301
302 print "Pushing config to ONOS..."
Yi Tseng7875cb72017-08-08 10:15:58 -0700303 call(("%s/onos-netcfg" % RUN_PACK_PATH, onosIp, TEMP_NETCFG_FILE))
Carmelo Cascone12e4d8d2016-07-05 15:55:15 -0700304
305 if not args.onos_ip:
306 ONOSCLI(net)
307 else:
308 CLI(net)
Carmelo Cascone785fada2016-06-16 18:34:16 -0700309
310 net.stop()
Yi Tseng7875cb72017-08-08 10:15:58 -0700311 call(("rm", "-f", TEMP_NETCFG_FILE))
Carmelo Cascone785fada2016-06-16 18:34:16 -0700312
313
314if __name__ == '__main__':
Carmelo Cascone12e4d8d2016-07-05 15:55:15 -0700315 parser = argparse.ArgumentParser(
316 description='BMv2 mininet demo script (2-stage Clos topology)')
Carmelo Cascone785fada2016-06-16 18:34:16 -0700317 parser.add_argument('--onos-ip', help='ONOS-BMv2 controller IP address',
Carmelo Cascone12e4d8d2016-07-05 15:55:15 -0700318 type=str, action="store", required=False)
Carmelo Cascone34433252017-08-25 20:27:18 +0200319 parser.add_argument('--size', help='Number of leaf/spine switches',
320 type=int, action="store", required=False, default=2)
Carmelo Cascone6ec8f8f2017-11-22 14:27:06 -0800321 parser.add_argument('--with-imbalanced-striping',
322 help='Topology with imbalanced striping',
323 type=bool, action="store", required=False,
324 default=False)
Yi Tseng7875cb72017-08-08 10:15:58 -0700325 parser.add_argument('--pipeconf-id', help='Pipeconf ID for switches',
326 type=str, action="store", required=False, default='')
Carmelo Cascone6ec8f8f2017-11-22 14:27:06 -0800327 parser.add_argument('--netcfg-sleep',
328 help='Seconds to wait before pushing config to ONOS',
Carmelo Cascone6e854042017-09-11 21:37:53 +0200329 type=int, action="store", required=False, default=5)
Carmelo Cascone6ec8f8f2017-11-22 14:27:06 -0800330 parser.add_argument('--log-level', help='BMv2 log level',
331 type=str, action="store", required=False,
332 default='warn')
333 parser.add_argument('--full-netcfg',
334 help='Generate full netcfg JSON with links and devices',
335 type=bool, action="store", required=False,
336 default=False)
337 parser.add_argument('--bg-traffic',
338 help='Starts background traffic',
339 type=bool, action="store", required=False,
340 default=False)
Carmelo Cascone785fada2016-06-16 18:34:16 -0700341 setLogLevel('info')
Carmelo Cascone6ec8f8f2017-11-22 14:27:06 -0800342 main(parser.parse_args())