blob: e1ad7d9b0f0621d30bebe51eacb719b835fc3656 [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
17
Carmelo Cascone977ae3f2016-06-23 19:28:28 -070018if 'ONOS_ROOT' not in os.environ:
19 print "Environment var $ONOS_ROOT not set"
20 exit()
21else:
Carmelo Cascone12e4d8d2016-07-05 15:55:15 -070022 ONOS_ROOT = os.environ["ONOS_ROOT"]
23 sys.path.append(ONOS_ROOT + "/tools/dev/mininet")
Devin Lim0d944e22017-06-23 15:17:53 -070024if 'RUN_PACK_PATH' not in os.environ:
25 print "Environment var $RUN_PACK_PATH not set"
26 exit()
27else:
28 RUN_PACK_PATH = os.environ["RUN_PACK_PATH"]
Carmelo Cascone12e4d8d2016-07-05 15:55:15 -070029
30from onos import ONOSCluster, ONOSCLI
Carmelo Cascone34433252017-08-25 20:27:18 +020031from bmv2 import ONOSBmv2Switch, ONOSHost
Carmelo Cascone977ae3f2016-06-23 19:28:28 -070032
Carmelo Cascone785fada2016-06-16 18:34:16 -070033from itertools import combinations
34from time import sleep
Carmelo Cascone12e4d8d2016-07-05 15:55:15 -070035from subprocess import call
Carmelo Cascone785fada2016-06-16 18:34:16 -070036
Carmelo Cascone785fada2016-06-16 18:34:16 -070037from mininet.cli import CLI
38from mininet.link import TCLink
39from mininet.log import setLogLevel
40from mininet.net import Mininet
41from mininet.node import RemoteController, Host
Yi Tseng7875cb72017-08-08 10:15:58 -070042from mininet.topo import Topo
Carmelo Cascone785fada2016-06-16 18:34:16 -070043
Carmelo Cascone73f6b6d2017-08-24 13:17:55 +020044
Carmelo Cascone6ec8f8f2017-11-22 14:27:06 -080045def getCmdBg(cmd, logfile="/dev/null"):
46 return "{} > {} 2>&1 &".format(cmd, logfile)
47
48
Carmelo Cascone785fada2016-06-16 18:34:16 -070049class ClosTopo(Topo):
Carmelo Cascone6ec8f8f2017-11-22 14:27:06 -080050 """2 stage Clos topology"""
Carmelo Cascone785fada2016-06-16 18:34:16 -070051
Carmelo Cascone34433252017-08-25 20:27:18 +020052 def __init__(self, args, **opts):
Carmelo Cascone785fada2016-06-16 18:34:16 -070053 # Initialize topology and default options
54 Topo.__init__(self, **opts)
55
Carmelo Cascone34433252017-08-25 20:27:18 +020056 bmv2SwitchIds = []
57 for row in (1, 2):
58 for col in range(1, args.size + 1):
59 bmv2SwitchIds.append("s%d%d" % (row, col))
60
Carmelo Cascone785fada2016-06-16 18:34:16 -070061 bmv2Switches = {}
62
Carmelo Cascone785fada2016-06-16 18:34:16 -070063 for switchId in bmv2SwitchIds:
Carmelo Cascone73f6b6d2017-08-24 13:17:55 +020064 deviceId = int(switchId[1:])
Carmelo Cascone6ec8f8f2017-11-22 14:27:06 -080065 # Use first number in device id to calculate latitude (row number),
66 # use second to calculate longitude (column number)
Yi Tseng7875cb72017-08-08 10:15:58 -070067 latitude = SWITCH_BASE_LATITUDE + (deviceId // 10) * BASE_SHIFT
Yi Tseng7875cb72017-08-08 10:15:58 -070068 longitude = BASE_LONGITUDE + (deviceId % 10) * BASE_SHIFT
Carmelo Cascone6ec8f8f2017-11-22 14:27:06 -080069
Carmelo Cascone785fada2016-06-16 18:34:16 -070070 bmv2Switches[switchId] = self.addSwitch(switchId,
71 cls=ONOSBmv2Switch,
Carmelo Cascone6ec8f8f2017-11-22 14:27:06 -080072 loglevel=args.log_level,
Yi Tseng7875cb72017-08-08 10:15:58 -070073 deviceId=deviceId,
Carmelo Cascone6ec8f8f2017-11-22 14:27:06 -080074 netcfg=True,
75 netcfgDelay=0.5,
Yi Tseng7875cb72017-08-08 10:15:58 -070076 longitude=longitude,
77 latitude=latitude,
Carmelo Cascone8ff3bc12018-04-15 18:15:02 -070078 pipeconf=args.pipeconf_id)
Carmelo Cascone785fada2016-06-16 18:34:16 -070079
Carmelo Cascone34433252017-08-25 20:27:18 +020080 for i in range(1, args.size + 1):
81 for j in range(1, args.size + 1):
Carmelo Cascone785fada2016-06-16 18:34:16 -070082 if i == j:
Carmelo Cascone6ec8f8f2017-11-22 14:27:06 -080083 self.addLink(bmv2Switches["s1%d" % i],
84 bmv2Switches["s2%d" % j],
Yi Tseng7875cb72017-08-08 10:15:58 -070085 cls=TCLink, bw=DEFAULT_SW_BW)
Carmelo Cascone34433252017-08-25 20:27:18 +020086 if args.with_imbalanced_striping:
Carmelo Cascone6ec8f8f2017-11-22 14:27:06 -080087 # 2 links
88 self.addLink(bmv2Switches["s1%d" % i],
89 bmv2Switches["s2%d" % j],
Carmelo Cascone34433252017-08-25 20:27:18 +020090 cls=TCLink, bw=DEFAULT_SW_BW)
Carmelo Cascone785fada2016-06-16 18:34:16 -070091 else:
Carmelo Cascone6ec8f8f2017-11-22 14:27:06 -080092 self.addLink(bmv2Switches["s1%d" % i],
93 bmv2Switches["s2%d" % j],
Yi Tseng7875cb72017-08-08 10:15:58 -070094 cls=TCLink, bw=DEFAULT_SW_BW)
Carmelo Cascone785fada2016-06-16 18:34:16 -070095
Carmelo Cascone34433252017-08-25 20:27:18 +020096 for hostId in range(1, args.size + 1):
Carmelo Cascone785fada2016-06-16 18:34:16 -070097 host = self.addHost("h%d" % hostId,
98 cls=DemoHost,
99 ip="10.0.0.%d/24" % hostId,
100 mac='00:00:00:00:00:%02x' % hostId)
Carmelo Cascone6ec8f8f2017-11-22 14:27:06 -0800101 self.addLink(host, bmv2Switches["s1%d" % hostId],
102 cls=TCLink, bw=DEFAULT_HOST_BW)
Carmelo Cascone785fada2016-06-16 18:34:16 -0700103
104
Carmelo Cascone34433252017-08-25 20:27:18 +0200105class DemoHost(ONOSHost):
Carmelo Cascone6ec8f8f2017-11-22 14:27:06 -0800106 """Demo host"""
Carmelo Cascone785fada2016-06-16 18:34:16 -0700107
Carmelo Cascone34433252017-08-25 20:27:18 +0200108 def __init__(self, name, **params):
109 ONOSHost.__init__(self, name, **params)
Carmelo Cascone785fada2016-06-16 18:34:16 -0700110 self.exectoken = "/tmp/mn-exec-token-host-%s" % name
111 self.cmd("touch %s" % self.exectoken)
112
Carmelo Cascone785fada2016-06-16 18:34:16 -0700113 def startPingBg(self, h):
114 self.cmd(self.getInfiniteCmdBg("ping -i0.5 %s" % h.IP()))
115 self.cmd(self.getInfiniteCmdBg("arping -w5000000 %s" % h.IP()))
116
117 def startIperfServer(self):
Carmelo Cascone6e854042017-09-11 21:37:53 +0200118 self.cmd(self.getInfiniteCmdBg("iperf -s -u"))
Carmelo Cascone785fada2016-06-16 18:34:16 -0700119
120 def startIperfClient(self, h, flowBw="512k", numFlows=5, duration=5):
Carmelo Cascone6ec8f8f2017-11-22 14:27:06 -0800121 iperfCmd = "iperf -c{} -u -b{} -P{} -t{}".format(
122 h.IP(), flowBw, numFlows, duration)
123 self.cmd(self.getInfiniteCmdBg(iperfCmd, delay=0))
Carmelo Cascone785fada2016-06-16 18:34:16 -0700124
Carmelo Cascone6ec8f8f2017-11-22 14:27:06 -0800125 def stop(self, **kwargs):
Carmelo Cascone6e854042017-09-11 21:37:53 +0200126 self.cmd("killall iperf")
Carmelo Cascone785fada2016-06-16 18:34:16 -0700127 self.cmd("killall ping")
128 self.cmd("killall arping")
129
130 def describe(self):
131 print "**********"
132 print self.name
133 print "default interface: %s\t%s\t%s" % (
134 self.defaultIntf().name,
135 self.defaultIntf().IP(),
136 self.defaultIntf().MAC()
137 )
138 print "**********"
139
Carmelo Cascone6ec8f8f2017-11-22 14:27:06 -0800140 def getInfiniteCmdBg(self, cmd, logfile="/dev/null", delay=1):
Carmelo Cascone785fada2016-06-16 18:34:16 -0700141 return "(while [ -e {} ]; " \
142 "do {}; " \
143 "sleep {}; " \
Carmelo Cascone6ec8f8f2017-11-22 14:27:06 -0800144 "done;) > {} 2>&1 &".format(self.exectoken, cmd, delay, logfile)
Carmelo Cascone785fada2016-06-16 18:34:16 -0700145
Carmelo Cascone73f6b6d2017-08-24 13:17:55 +0200146
147def generateNetcfg(onosIp, net, args):
Carmelo Cascone34433252017-08-25 20:27:18 +0200148 netcfg = OrderedDict()
Carmelo Cascone6ec8f8f2017-11-22 14:27:06 -0800149
150 netcfg['hosts'] = {}
Carmelo Cascone34433252017-08-25 20:27:18 +0200151 netcfg['devices'] = {}
152 netcfg['links'] = {}
Carmelo Cascone6ec8f8f2017-11-22 14:27:06 -0800153
154 if args.full_netcfg:
155 # Device configs
156 for sw in net.switches:
157 srcIp = sw.getSourceIp(onosIp)
158 netcfg['devices'][sw.onosDeviceId] = sw.getDeviceConfig(srcIp)
Yi Tseng7875cb72017-08-08 10:15:58 -0700159
160 hostLocations = {}
Yi Tseng7875cb72017-08-08 10:15:58 -0700161 for link in net.links:
162 switchPort = link.intf1.name.split('-')
Carmelo Cascone73f6b6d2017-08-24 13:17:55 +0200163 sw1Name = switchPort[0] # s11
164 port1Name = switchPort[1] # eth0
Yi Tseng7875cb72017-08-08 10:15:58 -0700165 port1 = port1Name[3:]
166 switchPort = link.intf2.name.split('-')
167 sw2Name = switchPort[0]
168 port2Name = switchPort[1]
169 port2 = port2Name[3:]
170 sw1 = net[sw1Name]
171 sw2 = net[sw2Name]
172 if isinstance(sw1, Host):
173 # record host location and ignore it
174 # e.g. {'h1': 'device:bmv2:11'}
175 hostLocations[sw1.name] = '%s/%s' % (sw2.onosDeviceId, port2)
176 continue
177
178 if isinstance(sw2, Host):
179 # record host location and ignore it
180 # e.g. {'h1': 'device:bmv2:11'}
181 hostLocations[sw2.name] = '%s/%s' % (sw1.onosDeviceId, port1)
182 continue
183
Carmelo Cascone6ec8f8f2017-11-22 14:27:06 -0800184 if args.full_netcfg:
185 # Link configs
186 for linkId in ('%s/%s-%s/%s' % (sw1.onosDeviceId, port1,
187 sw2.onosDeviceId, port2),
188 '%s/%s-%s/%s' % (sw2.onosDeviceId, port2,
189 sw1.onosDeviceId, port1)):
190 netcfg['links'][linkId] = {
191 'basic': {
192 'type': 'DIRECT',
193 'bandwidth': DEFAULT_SW_BW
194 }
Carmelo Cascone34433252017-08-25 20:27:18 +0200195 }
Yi Tseng7875cb72017-08-08 10:15:58 -0700196
197 # Host configs
198 longitude = BASE_LONGITUDE
199 for host in net.hosts:
200 longitude = longitude + BASE_SHIFT
201 hostDefaultIntf = host.defaultIntf()
202 hostMac = host.MAC(hostDefaultIntf)
203 hostIp = host.IP(hostDefaultIntf)
204 hostId = '%s/%d' % (hostMac, VLAN_NONE)
205 location = hostLocations[host.name]
206
207 # use host Id to generate host location
208 hostConfig = {
209 'basic': {
210 'locations': [location],
211 'ips': [hostIp],
212 'name': host.name,
213 'latitude': HOST_BASE_LATITUDE,
214 'longitude': longitude
215 }
216 }
217 netcfg['hosts'][hostId] = hostConfig
218
Carmelo Cascone6ec8f8f2017-11-22 14:27:06 -0800219 if args.full_netcfg:
220 netcfg["apps"] = {
221 "org.onosproject.core": {
222 "core": {
223 "linkDiscoveryMode": "STRICT"
224 }
Carmelo Cascone34433252017-08-25 20:27:18 +0200225 }
226 }
Carmelo Cascone34433252017-08-25 20:27:18 +0200227
Yi Tseng7875cb72017-08-08 10:15:58 -0700228 print "Writing network config to %s" % TEMP_NETCFG_FILE
229 with open(TEMP_NETCFG_FILE, 'w') as tempFile:
Carmelo Cascone73f6b6d2017-08-24 13:17:55 +0200230 json.dump(netcfg, tempFile, indent=4)
231
Carmelo Cascone785fada2016-06-16 18:34:16 -0700232
233def main(args):
Carmelo Cascone12e4d8d2016-07-05 15:55:15 -0700234 if not args.onos_ip:
235 controller = ONOSCluster('c0', 3)
236 onosIp = controller.nodes()[0].IP()
237 else:
Yi Tseng7875cb72017-08-08 10:15:58 -0700238 controller = RemoteController('c0', ip=args.onos_ip)
Carmelo Cascone12e4d8d2016-07-05 15:55:15 -0700239 onosIp = args.onos_ip
Carmelo Cascone785fada2016-06-16 18:34:16 -0700240
Carmelo Cascone34433252017-08-25 20:27:18 +0200241 topo = ClosTopo(args)
Yi Tseng7875cb72017-08-08 10:15:58 -0700242
Carmelo Cascone12e4d8d2016-07-05 15:55:15 -0700243 net = Mininet(topo=topo, build=False, controller=[controller])
Carmelo Cascone785fada2016-06-16 18:34:16 -0700244
245 net.build()
246 net.start()
247
Carmelo Cascone12e4d8d2016-07-05 15:55:15 -0700248 print "Network started"
Carmelo Cascone785fada2016-06-16 18:34:16 -0700249
Carmelo Cascone6ec8f8f2017-11-22 14:27:06 -0800250 # Always generate background pings.
Carmelo Cascone785fada2016-06-16 18:34:16 -0700251 sleep(3)
252 for (h1, h2) in combinations(net.hosts, 2):
253 h1.startPingBg(h2)
254 h2.startPingBg(h1)
255
Carmelo Cascone12e4d8d2016-07-05 15:55:15 -0700256 print "Background ping started"
257
Carmelo Cascone785fada2016-06-16 18:34:16 -0700258 for h in net.hosts:
259 h.startIperfServer()
260
Carmelo Cascone12e4d8d2016-07-05 15:55:15 -0700261 print "Iperf servers started"
Carmelo Cascone785fada2016-06-16 18:34:16 -0700262
Carmelo Cascone6ec8f8f2017-11-22 14:27:06 -0800263 if args.bg_traffic:
264 sleep(4)
265 print "Starting iperf clients..."
266 net.hosts[0].startIperfClient(net.hosts[-1], flowBw="400k",
267 numFlows=50, duration=10)
Carmelo Cascone785fada2016-06-16 18:34:16 -0700268
Carmelo Cascone73f6b6d2017-08-24 13:17:55 +0200269 generateNetcfg(onosIp, net, args)
Yi Tseng7875cb72017-08-08 10:15:58 -0700270
Carmelo Cascone6e854042017-09-11 21:37:53 +0200271 if args.netcfg_sleep > 0:
Carmelo Cascone6ec8f8f2017-11-22 14:27:06 -0800272 print "Waiting %d seconds before pushing config to ONOS..." \
273 % args.netcfg_sleep
Carmelo Cascone6e854042017-09-11 21:37:53 +0200274 sleep(args.netcfg_sleep)
275
276 print "Pushing config to ONOS..."
Yi Tseng7875cb72017-08-08 10:15:58 -0700277 call(("%s/onos-netcfg" % RUN_PACK_PATH, onosIp, TEMP_NETCFG_FILE))
Carmelo Cascone12e4d8d2016-07-05 15:55:15 -0700278
279 if not args.onos_ip:
280 ONOSCLI(net)
281 else:
282 CLI(net)
Carmelo Cascone785fada2016-06-16 18:34:16 -0700283
284 net.stop()
Yi Tseng7875cb72017-08-08 10:15:58 -0700285 call(("rm", "-f", TEMP_NETCFG_FILE))
Carmelo Cascone785fada2016-06-16 18:34:16 -0700286
287
288if __name__ == '__main__':
Carmelo Cascone12e4d8d2016-07-05 15:55:15 -0700289 parser = argparse.ArgumentParser(
290 description='BMv2 mininet demo script (2-stage Clos topology)')
Carmelo Cascone785fada2016-06-16 18:34:16 -0700291 parser.add_argument('--onos-ip', help='ONOS-BMv2 controller IP address',
Carmelo Cascone12e4d8d2016-07-05 15:55:15 -0700292 type=str, action="store", required=False)
Carmelo Cascone34433252017-08-25 20:27:18 +0200293 parser.add_argument('--size', help='Number of leaf/spine switches',
294 type=int, action="store", required=False, default=2)
Carmelo Cascone6ec8f8f2017-11-22 14:27:06 -0800295 parser.add_argument('--with-imbalanced-striping',
296 help='Topology with imbalanced striping',
297 type=bool, action="store", required=False,
298 default=False)
Yi Tseng7875cb72017-08-08 10:15:58 -0700299 parser.add_argument('--pipeconf-id', help='Pipeconf ID for switches',
300 type=str, action="store", required=False, default='')
Carmelo Cascone6ec8f8f2017-11-22 14:27:06 -0800301 parser.add_argument('--netcfg-sleep',
302 help='Seconds to wait before pushing config to ONOS',
Carmelo Cascone6e854042017-09-11 21:37:53 +0200303 type=int, action="store", required=False, default=5)
Carmelo Cascone6ec8f8f2017-11-22 14:27:06 -0800304 parser.add_argument('--log-level', help='BMv2 log level',
305 type=str, action="store", required=False,
306 default='warn')
307 parser.add_argument('--full-netcfg',
308 help='Generate full netcfg JSON with links and devices',
309 type=bool, action="store", required=False,
310 default=False)
311 parser.add_argument('--bg-traffic',
312 help='Starts background traffic',
313 type=bool, action="store", required=False,
314 default=False)
Carmelo Cascone785fada2016-06-16 18:34:16 -0700315 setLogLevel('info')
Carmelo Cascone6ec8f8f2017-11-22 14:27:06 -0800316 main(parser.parse_args())