blob: 4aa7eb5c4fc86b83d38276f113bb68fea503aea0 [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 Cascone977ae3f2016-06-23 19:28:28 -07007
Yi Tseng7875cb72017-08-08 10:15:58 -07008TEMP_NETCFG_FILE = '/tmp/bmv2-demo-cfg.json'
9BASE_LONGITUDE = -115
10SWITCH_BASE_LATITUDE = 25
11HOST_BASE_LATITUDE = 28
12BASE_SHIFT = 8
13VLAN_NONE = -1
14DEFAULT_SW_BW = 50
15DEFAULT_HOST_BW = 25
16
Carmelo Cascone977ae3f2016-06-23 19:28:28 -070017if 'ONOS_ROOT' not in os.environ:
18 print "Environment var $ONOS_ROOT not set"
19 exit()
20else:
Carmelo Cascone12e4d8d2016-07-05 15:55:15 -070021 ONOS_ROOT = os.environ["ONOS_ROOT"]
22 sys.path.append(ONOS_ROOT + "/tools/dev/mininet")
Devin Lim0d944e22017-06-23 15:17:53 -070023if 'RUN_PACK_PATH' not in os.environ:
24 print "Environment var $RUN_PACK_PATH not set"
25 exit()
26else:
27 RUN_PACK_PATH = os.environ["RUN_PACK_PATH"]
Carmelo Cascone12e4d8d2016-07-05 15:55:15 -070028
29from onos import ONOSCluster, ONOSCLI
30from bmv2 import ONOSBmv2Switch
Carmelo Cascone977ae3f2016-06-23 19:28:28 -070031
Carmelo Cascone785fada2016-06-16 18:34:16 -070032from itertools import combinations
33from time import sleep
Carmelo Cascone12e4d8d2016-07-05 15:55:15 -070034from subprocess import call
Carmelo Cascone785fada2016-06-16 18:34:16 -070035
Carmelo Cascone785fada2016-06-16 18:34:16 -070036from mininet.cli import CLI
37from mininet.link import TCLink
38from mininet.log import setLogLevel
39from mininet.net import Mininet
40from mininet.node import RemoteController, Host
Yi Tseng7875cb72017-08-08 10:15:58 -070041from mininet.topo import Topo
Carmelo Cascone785fada2016-06-16 18:34:16 -070042
Carmelo Cascone73f6b6d2017-08-24 13:17:55 +020043
Carmelo Cascone785fada2016-06-16 18:34:16 -070044class ClosTopo(Topo):
45 "2 stage Clos topology"
46
Yi Tseng7875cb72017-08-08 10:15:58 -070047 def __init__(self, pipeconfId="", **opts):
Carmelo Cascone785fada2016-06-16 18:34:16 -070048 # Initialize topology and default options
49 Topo.__init__(self, **opts)
50
51 bmv2SwitchIds = ["s11", "s12", "s13", "s21", "s22", "s23"]
Carmelo Cascone785fada2016-06-16 18:34:16 -070052 bmv2Switches = {}
53
Carmelo Cascone785fada2016-06-16 18:34:16 -070054 for switchId in bmv2SwitchIds:
Carmelo Cascone73f6b6d2017-08-24 13:17:55 +020055 deviceId = int(switchId[1:])
Yi Tseng7875cb72017-08-08 10:15:58 -070056 # Use first number in device id to calculate latitude (row number)
57 latitude = SWITCH_BASE_LATITUDE + (deviceId // 10) * BASE_SHIFT
58
59 # Use second number in device id to calculate longitude (column number)
60 longitude = BASE_LONGITUDE + (deviceId % 10) * BASE_SHIFT
Carmelo Cascone785fada2016-06-16 18:34:16 -070061 bmv2Switches[switchId] = self.addSwitch(switchId,
62 cls=ONOSBmv2Switch,
63 loglevel="warn",
Yi Tseng7875cb72017-08-08 10:15:58 -070064 deviceId=deviceId,
65 netcfg=False,
66 longitude=longitude,
67 latitude=latitude,
68 pipeconfId=pipeconfId)
Carmelo Cascone785fada2016-06-16 18:34:16 -070069
70 for i in (1, 2, 3):
71 for j in (1, 2, 3):
72 if i == j:
73 # 2 links
74 self.addLink(bmv2Switches["s1%d" % i], bmv2Switches["s2%d" % j],
Yi Tseng7875cb72017-08-08 10:15:58 -070075 cls=TCLink, bw=DEFAULT_SW_BW)
Carmelo Cascone785fada2016-06-16 18:34:16 -070076 self.addLink(bmv2Switches["s1%d" % i], bmv2Switches["s2%d" % j],
Yi Tseng7875cb72017-08-08 10:15:58 -070077 cls=TCLink, bw=DEFAULT_SW_BW)
Carmelo Cascone785fada2016-06-16 18:34:16 -070078 else:
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 Cascone785fada2016-06-16 18:34:16 -070081
82 for hostId in (1, 2, 3):
83 host = self.addHost("h%d" % hostId,
84 cls=DemoHost,
85 ip="10.0.0.%d/24" % hostId,
86 mac='00:00:00:00:00:%02x' % hostId)
Yi Tseng7875cb72017-08-08 10:15:58 -070087 self.addLink(host, bmv2Switches["s1%d" % hostId], cls=TCLink, bw=DEFAULT_HOST_BW)
Carmelo Cascone785fada2016-06-16 18:34:16 -070088
89
90class DemoHost(Host):
91 "Demo host"
92
93 def __init__(self, name, inNamespace=True, **params):
94 Host.__init__(self, name, inNamespace=inNamespace, **params)
95 self.exectoken = "/tmp/mn-exec-token-host-%s" % name
96 self.cmd("touch %s" % self.exectoken)
97
98 def config(self, **params):
99 r = super(Host, self).config(**params)
100
Carmelo Cascone785fada2016-06-16 18:34:16 -0700101 for off in ["rx", "tx", "sg"]:
Yi Tseng7875cb72017-08-08 10:15:58 -0700102 cmd = "/sbin/ethtool --offload %s %s off" % (self.defaultIntf(), off)
Carmelo Cascone785fada2016-06-16 18:34:16 -0700103 self.cmd(cmd)
104
105 # disable IPv6
106 self.cmd("sysctl -w net.ipv6.conf.all.disable_ipv6=1")
107 self.cmd("sysctl -w net.ipv6.conf.default.disable_ipv6=1")
108 self.cmd("sysctl -w net.ipv6.conf.lo.disable_ipv6=1")
109
110 return r
111
112 def startPingBg(self, h):
113 self.cmd(self.getInfiniteCmdBg("ping -i0.5 %s" % h.IP()))
114 self.cmd(self.getInfiniteCmdBg("arping -w5000000 %s" % h.IP()))
115
116 def startIperfServer(self):
117 self.cmd(self.getInfiniteCmdBg("iperf3 -s"))
118
119 def startIperfClient(self, h, flowBw="512k", numFlows=5, duration=5):
120 iperfCmd = "iperf3 -c{} -b{} -P{} -t{}".format(h.IP(), flowBw, numFlows, duration)
121 self.cmd(self.getInfiniteCmdBg(iperfCmd, sleep=0))
122
123 def stop(self):
124 self.cmd("killall iperf3")
125 self.cmd("killall ping")
126 self.cmd("killall arping")
127
128 def describe(self):
129 print "**********"
130 print self.name
131 print "default interface: %s\t%s\t%s" % (
132 self.defaultIntf().name,
133 self.defaultIntf().IP(),
134 self.defaultIntf().MAC()
135 )
136 print "**********"
137
138 def getInfiniteCmdBg(self, cmd, logfile="/dev/null", sleep=1):
139 return "(while [ -e {} ]; " \
140 "do {}; " \
141 "sleep {}; " \
142 "done;) > {} 2>&1 &".format(self.exectoken, cmd, sleep, logfile)
143
144 def getCmdBg(self, cmd, logfile="/dev/null"):
145 return "{} > {} 2>&1 &".format(cmd, logfile)
146
Carmelo Cascone73f6b6d2017-08-24 13:17:55 +0200147
148def generateNetcfg(onosIp, net, args):
149 netcfg = {'devices': {}, 'links': {}, 'hosts': {}}
Yi Tseng7875cb72017-08-08 10:15:58 -0700150 # Device configs
151 for sw in net.switches:
152 srcIp = sw.getSourceIp(onosIp)
153 netcfg['devices'][sw.onosDeviceId] = sw.getDeviceConfig(srcIp)
154
155 hostLocations = {}
156 # Link configs
157 for link in net.links:
158 switchPort = link.intf1.name.split('-')
Carmelo Cascone73f6b6d2017-08-24 13:17:55 +0200159 sw1Name = switchPort[0] # s11
160 port1Name = switchPort[1] # eth0
Yi Tseng7875cb72017-08-08 10:15:58 -0700161 port1 = port1Name[3:]
162 switchPort = link.intf2.name.split('-')
163 sw2Name = switchPort[0]
164 port2Name = switchPort[1]
165 port2 = port2Name[3:]
166 sw1 = net[sw1Name]
167 sw2 = net[sw2Name]
168 if isinstance(sw1, Host):
169 # record host location and ignore it
170 # e.g. {'h1': 'device:bmv2:11'}
171 hostLocations[sw1.name] = '%s/%s' % (sw2.onosDeviceId, port2)
172 continue
173
174 if isinstance(sw2, Host):
175 # record host location and ignore it
176 # e.g. {'h1': 'device:bmv2:11'}
177 hostLocations[sw2.name] = '%s/%s' % (sw1.onosDeviceId, port1)
178 continue
179
180 linkId = '%s/%s-%s/%s' % (sw1.onosDeviceId, port1, sw2.onosDeviceId, port2)
181 netcfg['links'][linkId] = {
182 'basic': {
183 'type': 'DIRECT',
184 'bandwidth': 50
185 }
186 }
187
188 # Host configs
189 longitude = BASE_LONGITUDE
190 for host in net.hosts:
191 longitude = longitude + BASE_SHIFT
192 hostDefaultIntf = host.defaultIntf()
193 hostMac = host.MAC(hostDefaultIntf)
194 hostIp = host.IP(hostDefaultIntf)
195 hostId = '%s/%d' % (hostMac, VLAN_NONE)
196 location = hostLocations[host.name]
197
198 # use host Id to generate host location
199 hostConfig = {
200 'basic': {
201 'locations': [location],
202 'ips': [hostIp],
203 'name': host.name,
204 'latitude': HOST_BASE_LATITUDE,
205 'longitude': longitude
206 }
207 }
208 netcfg['hosts'][hostId] = hostConfig
209
210 print "Writing network config to %s" % TEMP_NETCFG_FILE
211 with open(TEMP_NETCFG_FILE, 'w') as tempFile:
Carmelo Cascone73f6b6d2017-08-24 13:17:55 +0200212 json.dump(netcfg, tempFile, indent=4)
213
Carmelo Cascone785fada2016-06-16 18:34:16 -0700214
215def main(args):
Carmelo Cascone12e4d8d2016-07-05 15:55:15 -0700216 if not args.onos_ip:
217 controller = ONOSCluster('c0', 3)
218 onosIp = controller.nodes()[0].IP()
219 else:
Yi Tseng7875cb72017-08-08 10:15:58 -0700220 controller = RemoteController('c0', ip=args.onos_ip)
Carmelo Cascone12e4d8d2016-07-05 15:55:15 -0700221 onosIp = args.onos_ip
Carmelo Cascone785fada2016-06-16 18:34:16 -0700222
Yi Tseng7875cb72017-08-08 10:15:58 -0700223 topo = ClosTopo(pipeconfId=args.pipeconf_id)
224
Carmelo Cascone12e4d8d2016-07-05 15:55:15 -0700225 net = Mininet(topo=topo, build=False, controller=[controller])
Carmelo Cascone785fada2016-06-16 18:34:16 -0700226
227 net.build()
228 net.start()
229
Carmelo Cascone12e4d8d2016-07-05 15:55:15 -0700230 print "Network started"
Carmelo Cascone785fada2016-06-16 18:34:16 -0700231
Carmelo Cascone12e4d8d2016-07-05 15:55:15 -0700232 # Generate background traffic.
Carmelo Cascone785fada2016-06-16 18:34:16 -0700233 sleep(3)
234 for (h1, h2) in combinations(net.hosts, 2):
235 h1.startPingBg(h2)
236 h2.startPingBg(h1)
237
Carmelo Cascone12e4d8d2016-07-05 15:55:15 -0700238 print "Background ping started"
239
Carmelo Cascone785fada2016-06-16 18:34:16 -0700240 for h in net.hosts:
241 h.startIperfServer()
242
Carmelo Cascone12e4d8d2016-07-05 15:55:15 -0700243 print "Iperf servers started"
Carmelo Cascone785fada2016-06-16 18:34:16 -0700244
245 # sleep(4)
246 # print "Starting traffic from h1 to h3..."
247 # net.hosts[0].startIperfClient(net.hosts[-1], flowBw="200k", numFlows=100, duration=10)
248
Carmelo Cascone73f6b6d2017-08-24 13:17:55 +0200249 generateNetcfg(onosIp, net, args)
Yi Tseng7875cb72017-08-08 10:15:58 -0700250
251 print "Uploading netcfg..."
252 call(("%s/onos-netcfg" % RUN_PACK_PATH, onosIp, TEMP_NETCFG_FILE))
Carmelo Cascone12e4d8d2016-07-05 15:55:15 -0700253
254 if not args.onos_ip:
255 ONOSCLI(net)
256 else:
257 CLI(net)
Carmelo Cascone785fada2016-06-16 18:34:16 -0700258
259 net.stop()
Yi Tseng7875cb72017-08-08 10:15:58 -0700260 call(("rm", "-f", TEMP_NETCFG_FILE))
Carmelo Cascone785fada2016-06-16 18:34:16 -0700261
262
263if __name__ == '__main__':
Carmelo Cascone12e4d8d2016-07-05 15:55:15 -0700264 parser = argparse.ArgumentParser(
265 description='BMv2 mininet demo script (2-stage Clos topology)')
Carmelo Cascone785fada2016-06-16 18:34:16 -0700266 parser.add_argument('--onos-ip', help='ONOS-BMv2 controller IP address',
Carmelo Cascone12e4d8d2016-07-05 15:55:15 -0700267 type=str, action="store", required=False)
Yi Tseng7875cb72017-08-08 10:15:58 -0700268 parser.add_argument('--pipeconf-id', help='Pipeconf ID for switches',
269 type=str, action="store", required=False, default='')
Carmelo Cascone785fada2016-06-16 18:34:16 -0700270 args = parser.parse_args()
271 setLogLevel('info')
272 main(args)