blob: 9bccc5ca7d56430c71d6f0e7e6b93767b79b3d74 [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 Cascone785fada2016-06-16 18:34:16 -070045class ClosTopo(Topo):
46 "2 stage Clos topology"
47
Carmelo Cascone34433252017-08-25 20:27:18 +020048 def __init__(self, args, **opts):
Carmelo Cascone785fada2016-06-16 18:34:16 -070049 # Initialize topology and default options
50 Topo.__init__(self, **opts)
51
Carmelo Cascone34433252017-08-25 20:27:18 +020052 bmv2SwitchIds = []
53 for row in (1, 2):
54 for col in range(1, args.size + 1):
55 bmv2SwitchIds.append("s%d%d" % (row, col))
56
Carmelo Cascone785fada2016-06-16 18:34:16 -070057 bmv2Switches = {}
58
Carmelo Cascone785fada2016-06-16 18:34:16 -070059 for switchId in bmv2SwitchIds:
Carmelo Cascone73f6b6d2017-08-24 13:17:55 +020060 deviceId = int(switchId[1:])
Yi Tseng7875cb72017-08-08 10:15:58 -070061 # Use first number in device id to calculate latitude (row number)
62 latitude = SWITCH_BASE_LATITUDE + (deviceId // 10) * BASE_SHIFT
63
64 # Use second number in device id to calculate longitude (column number)
65 longitude = BASE_LONGITUDE + (deviceId % 10) * BASE_SHIFT
Carmelo Cascone785fada2016-06-16 18:34:16 -070066 bmv2Switches[switchId] = self.addSwitch(switchId,
67 cls=ONOSBmv2Switch,
68 loglevel="warn",
Yi Tseng7875cb72017-08-08 10:15:58 -070069 deviceId=deviceId,
70 netcfg=False,
71 longitude=longitude,
72 latitude=latitude,
Carmelo Cascone34433252017-08-25 20:27:18 +020073 pipeconfId=args.pipeconf_id)
Carmelo Cascone785fada2016-06-16 18:34:16 -070074
Carmelo Cascone34433252017-08-25 20:27:18 +020075 for i in range(1, args.size + 1):
76 for j in range(1, args.size + 1):
Carmelo Cascone785fada2016-06-16 18:34:16 -070077 if i == j:
78 # 2 links
79 self.addLink(bmv2Switches["s1%d" % i], bmv2Switches["s2%d" % j],
Yi Tseng7875cb72017-08-08 10:15:58 -070080 cls=TCLink, bw=DEFAULT_SW_BW)
Carmelo Cascone34433252017-08-25 20:27:18 +020081 if args.with_imbalanced_striping:
82 self.addLink(bmv2Switches["s1%d" % i], bmv2Switches["s2%d" % j],
83 cls=TCLink, bw=DEFAULT_SW_BW)
Carmelo Cascone785fada2016-06-16 18:34:16 -070084 else:
85 self.addLink(bmv2Switches["s1%d" % i], bmv2Switches["s2%d" % j],
Yi Tseng7875cb72017-08-08 10:15:58 -070086 cls=TCLink, bw=DEFAULT_SW_BW)
Carmelo Cascone785fada2016-06-16 18:34:16 -070087
Carmelo Cascone34433252017-08-25 20:27:18 +020088 for hostId in range(1, args.size + 1):
Carmelo Cascone785fada2016-06-16 18:34:16 -070089 host = self.addHost("h%d" % hostId,
90 cls=DemoHost,
91 ip="10.0.0.%d/24" % hostId,
92 mac='00:00:00:00:00:%02x' % hostId)
Yi Tseng7875cb72017-08-08 10:15:58 -070093 self.addLink(host, bmv2Switches["s1%d" % hostId], cls=TCLink, bw=DEFAULT_HOST_BW)
Carmelo Cascone785fada2016-06-16 18:34:16 -070094
95
Carmelo Cascone34433252017-08-25 20:27:18 +020096class DemoHost(ONOSHost):
Carmelo Cascone785fada2016-06-16 18:34:16 -070097 "Demo host"
98
Carmelo Cascone34433252017-08-25 20:27:18 +020099 def __init__(self, name, **params):
100 ONOSHost.__init__(self, name, **params)
Carmelo Cascone785fada2016-06-16 18:34:16 -0700101 self.exectoken = "/tmp/mn-exec-token-host-%s" % name
102 self.cmd("touch %s" % self.exectoken)
103
Carmelo Cascone785fada2016-06-16 18:34:16 -0700104 def startPingBg(self, h):
105 self.cmd(self.getInfiniteCmdBg("ping -i0.5 %s" % h.IP()))
106 self.cmd(self.getInfiniteCmdBg("arping -w5000000 %s" % h.IP()))
107
108 def startIperfServer(self):
Carmelo Cascone6e854042017-09-11 21:37:53 +0200109 self.cmd(self.getInfiniteCmdBg("iperf -s -u"))
Carmelo Cascone785fada2016-06-16 18:34:16 -0700110
111 def startIperfClient(self, h, flowBw="512k", numFlows=5, duration=5):
Carmelo Cascone6e854042017-09-11 21:37:53 +0200112 iperfCmd = "iperf -c{} -u -b{} -P{} -t{}".format(h.IP(), flowBw, numFlows, duration)
Carmelo Cascone785fada2016-06-16 18:34:16 -0700113 self.cmd(self.getInfiniteCmdBg(iperfCmd, sleep=0))
114
115 def stop(self):
Carmelo Cascone6e854042017-09-11 21:37:53 +0200116 self.cmd("killall iperf")
Carmelo Cascone785fada2016-06-16 18:34:16 -0700117 self.cmd("killall ping")
118 self.cmd("killall arping")
119
120 def describe(self):
121 print "**********"
122 print self.name
123 print "default interface: %s\t%s\t%s" % (
124 self.defaultIntf().name,
125 self.defaultIntf().IP(),
126 self.defaultIntf().MAC()
127 )
128 print "**********"
129
130 def getInfiniteCmdBg(self, cmd, logfile="/dev/null", sleep=1):
131 return "(while [ -e {} ]; " \
132 "do {}; " \
133 "sleep {}; " \
134 "done;) > {} 2>&1 &".format(self.exectoken, cmd, sleep, logfile)
135
136 def getCmdBg(self, cmd, logfile="/dev/null"):
137 return "{} > {} 2>&1 &".format(cmd, logfile)
138
Carmelo Cascone73f6b6d2017-08-24 13:17:55 +0200139
140def generateNetcfg(onosIp, net, args):
Carmelo Cascone34433252017-08-25 20:27:18 +0200141 netcfg = OrderedDict()
142 netcfg['devices'] = {}
143 netcfg['links'] = {}
144 netcfg['hosts'] = {}
Yi Tseng7875cb72017-08-08 10:15:58 -0700145 # Device configs
146 for sw in net.switches:
147 srcIp = sw.getSourceIp(onosIp)
148 netcfg['devices'][sw.onosDeviceId] = sw.getDeviceConfig(srcIp)
149
150 hostLocations = {}
151 # Link configs
152 for link in net.links:
153 switchPort = link.intf1.name.split('-')
Carmelo Cascone73f6b6d2017-08-24 13:17:55 +0200154 sw1Name = switchPort[0] # s11
155 port1Name = switchPort[1] # eth0
Yi Tseng7875cb72017-08-08 10:15:58 -0700156 port1 = port1Name[3:]
157 switchPort = link.intf2.name.split('-')
158 sw2Name = switchPort[0]
159 port2Name = switchPort[1]
160 port2 = port2Name[3:]
161 sw1 = net[sw1Name]
162 sw2 = net[sw2Name]
163 if isinstance(sw1, Host):
164 # record host location and ignore it
165 # e.g. {'h1': 'device:bmv2:11'}
166 hostLocations[sw1.name] = '%s/%s' % (sw2.onosDeviceId, port2)
167 continue
168
169 if isinstance(sw2, Host):
170 # record host location and ignore it
171 # e.g. {'h1': 'device:bmv2:11'}
172 hostLocations[sw2.name] = '%s/%s' % (sw1.onosDeviceId, port1)
173 continue
174
Carmelo Cascone34433252017-08-25 20:27:18 +0200175 for linkId in ('%s/%s-%s/%s' % (sw1.onosDeviceId, port1, sw2.onosDeviceId, port2),
176 '%s/%s-%s/%s' % (sw2.onosDeviceId, port2, sw1.onosDeviceId, port1)):
177 netcfg['links'][linkId] = {
178 'basic': {
179 'type': 'DIRECT',
180 'bandwidth': DEFAULT_SW_BW
181 }
Yi Tseng7875cb72017-08-08 10:15:58 -0700182 }
Yi Tseng7875cb72017-08-08 10:15:58 -0700183
184 # Host configs
185 longitude = BASE_LONGITUDE
186 for host in net.hosts:
187 longitude = longitude + BASE_SHIFT
188 hostDefaultIntf = host.defaultIntf()
189 hostMac = host.MAC(hostDefaultIntf)
190 hostIp = host.IP(hostDefaultIntf)
191 hostId = '%s/%d' % (hostMac, VLAN_NONE)
192 location = hostLocations[host.name]
193
194 # use host Id to generate host location
195 hostConfig = {
196 'basic': {
197 'locations': [location],
198 'ips': [hostIp],
199 'name': host.name,
200 'latitude': HOST_BASE_LATITUDE,
201 'longitude': longitude
202 }
203 }
204 netcfg['hosts'][hostId] = hostConfig
205
Carmelo Cascone34433252017-08-25 20:27:18 +0200206 netcfg["apps"] = {
207 "org.onosproject.core": {
208 "core": {
209 "linkDiscoveryMode": "STRICT"
210 }
211 }
212 }
213
Yi Tseng7875cb72017-08-08 10:15:58 -0700214 print "Writing network config to %s" % TEMP_NETCFG_FILE
215 with open(TEMP_NETCFG_FILE, 'w') as tempFile:
Carmelo Cascone73f6b6d2017-08-24 13:17:55 +0200216 json.dump(netcfg, tempFile, indent=4)
217
Carmelo Cascone785fada2016-06-16 18:34:16 -0700218
219def main(args):
Carmelo Cascone12e4d8d2016-07-05 15:55:15 -0700220 if not args.onos_ip:
221 controller = ONOSCluster('c0', 3)
222 onosIp = controller.nodes()[0].IP()
223 else:
Yi Tseng7875cb72017-08-08 10:15:58 -0700224 controller = RemoteController('c0', ip=args.onos_ip)
Carmelo Cascone12e4d8d2016-07-05 15:55:15 -0700225 onosIp = args.onos_ip
Carmelo Cascone785fada2016-06-16 18:34:16 -0700226
Carmelo Cascone34433252017-08-25 20:27:18 +0200227 topo = ClosTopo(args)
Yi Tseng7875cb72017-08-08 10:15:58 -0700228
Carmelo Cascone12e4d8d2016-07-05 15:55:15 -0700229 net = Mininet(topo=topo, build=False, controller=[controller])
Carmelo Cascone785fada2016-06-16 18:34:16 -0700230
231 net.build()
232 net.start()
233
Carmelo Cascone12e4d8d2016-07-05 15:55:15 -0700234 print "Network started"
Carmelo Cascone785fada2016-06-16 18:34:16 -0700235
Carmelo Cascone12e4d8d2016-07-05 15:55:15 -0700236 # Generate background traffic.
Carmelo Cascone785fada2016-06-16 18:34:16 -0700237 sleep(3)
238 for (h1, h2) in combinations(net.hosts, 2):
239 h1.startPingBg(h2)
240 h2.startPingBg(h1)
241
Carmelo Cascone12e4d8d2016-07-05 15:55:15 -0700242 print "Background ping started"
243
Carmelo Cascone785fada2016-06-16 18:34:16 -0700244 for h in net.hosts:
245 h.startIperfServer()
246
Carmelo Cascone12e4d8d2016-07-05 15:55:15 -0700247 print "Iperf servers started"
Carmelo Cascone785fada2016-06-16 18:34:16 -0700248
249 # sleep(4)
250 # print "Starting traffic from h1 to h3..."
251 # net.hosts[0].startIperfClient(net.hosts[-1], flowBw="200k", numFlows=100, duration=10)
252
Carmelo Cascone73f6b6d2017-08-24 13:17:55 +0200253 generateNetcfg(onosIp, net, args)
Yi Tseng7875cb72017-08-08 10:15:58 -0700254
Carmelo Cascone6e854042017-09-11 21:37:53 +0200255 if args.netcfg_sleep > 0:
256 print "Waiting %d seconds before pushing config to ONOS..." % args.netcfg_sleep
257 sleep(args.netcfg_sleep)
258
259 print "Pushing config to ONOS..."
Yi Tseng7875cb72017-08-08 10:15:58 -0700260 call(("%s/onos-netcfg" % RUN_PACK_PATH, onosIp, TEMP_NETCFG_FILE))
Carmelo Cascone12e4d8d2016-07-05 15:55:15 -0700261
262 if not args.onos_ip:
263 ONOSCLI(net)
264 else:
265 CLI(net)
Carmelo Cascone785fada2016-06-16 18:34:16 -0700266
267 net.stop()
Yi Tseng7875cb72017-08-08 10:15:58 -0700268 call(("rm", "-f", TEMP_NETCFG_FILE))
Carmelo Cascone785fada2016-06-16 18:34:16 -0700269
270
271if __name__ == '__main__':
Carmelo Cascone12e4d8d2016-07-05 15:55:15 -0700272 parser = argparse.ArgumentParser(
273 description='BMv2 mininet demo script (2-stage Clos topology)')
Carmelo Cascone785fada2016-06-16 18:34:16 -0700274 parser.add_argument('--onos-ip', help='ONOS-BMv2 controller IP address',
Carmelo Cascone12e4d8d2016-07-05 15:55:15 -0700275 type=str, action="store", required=False)
Carmelo Cascone34433252017-08-25 20:27:18 +0200276 parser.add_argument('--size', help='Number of leaf/spine switches',
277 type=int, action="store", required=False, default=2)
278 parser.add_argument('--with-imbalanced-striping', help='Topology with imbalanced striping',
279 type=bool, action="store", required=False, default=False)
Yi Tseng7875cb72017-08-08 10:15:58 -0700280 parser.add_argument('--pipeconf-id', help='Pipeconf ID for switches',
281 type=str, action="store", required=False, default='')
Carmelo Cascone6e854042017-09-11 21:37:53 +0200282 parser.add_argument('--netcfg-sleep', help='Seconds to wait before pushing config to ONOS',
283 type=int, action="store", required=False, default=5)
Carmelo Cascone785fada2016-06-16 18:34:16 -0700284 args = parser.parse_args()
285 setLogLevel('info')
286 main(args)