blob: a2aeaa612334c9b74956fc6c6f2199ffdcd73d99 [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
43class ClosTopo(Topo):
44 "2 stage Clos topology"
45
Yi Tseng7875cb72017-08-08 10:15:58 -070046 def __init__(self, pipeconfId="", **opts):
Carmelo Cascone785fada2016-06-16 18:34:16 -070047 # Initialize topology and default options
48 Topo.__init__(self, **opts)
49
50 bmv2SwitchIds = ["s11", "s12", "s13", "s21", "s22", "s23"]
Carmelo Cascone785fada2016-06-16 18:34:16 -070051 bmv2Switches = {}
52
Carmelo Cascone785fada2016-06-16 18:34:16 -070053 for switchId in bmv2SwitchIds:
Yi Tseng7875cb72017-08-08 10:15:58 -070054 deviceId=int(switchId[1:])
55 # Use first number in device id to calculate latitude (row number)
56 latitude = SWITCH_BASE_LATITUDE + (deviceId // 10) * BASE_SHIFT
57
58 # Use second number in device id to calculate longitude (column number)
59 longitude = BASE_LONGITUDE + (deviceId % 10) * BASE_SHIFT
Carmelo Cascone785fada2016-06-16 18:34:16 -070060 bmv2Switches[switchId] = self.addSwitch(switchId,
61 cls=ONOSBmv2Switch,
62 loglevel="warn",
Yi Tseng7875cb72017-08-08 10:15:58 -070063 deviceId=deviceId,
64 netcfg=False,
65 longitude=longitude,
66 latitude=latitude,
67 pipeconfId=pipeconfId)
Carmelo Cascone785fada2016-06-16 18:34:16 -070068
69 for i in (1, 2, 3):
70 for j in (1, 2, 3):
71 if i == j:
72 # 2 links
73 self.addLink(bmv2Switches["s1%d" % i], bmv2Switches["s2%d" % j],
Yi Tseng7875cb72017-08-08 10:15:58 -070074 cls=TCLink, bw=DEFAULT_SW_BW)
Carmelo Cascone785fada2016-06-16 18:34:16 -070075 self.addLink(bmv2Switches["s1%d" % i], bmv2Switches["s2%d" % j],
Yi Tseng7875cb72017-08-08 10:15:58 -070076 cls=TCLink, bw=DEFAULT_SW_BW)
Carmelo Cascone785fada2016-06-16 18:34:16 -070077 else:
78 self.addLink(bmv2Switches["s1%d" % i], bmv2Switches["s2%d" % j],
Yi Tseng7875cb72017-08-08 10:15:58 -070079 cls=TCLink, bw=DEFAULT_SW_BW)
Carmelo Cascone785fada2016-06-16 18:34:16 -070080
81 for hostId in (1, 2, 3):
82 host = self.addHost("h%d" % hostId,
83 cls=DemoHost,
84 ip="10.0.0.%d/24" % hostId,
85 mac='00:00:00:00:00:%02x' % hostId)
Yi Tseng7875cb72017-08-08 10:15:58 -070086 self.addLink(host, bmv2Switches["s1%d" % hostId], cls=TCLink, bw=DEFAULT_HOST_BW)
Carmelo Cascone785fada2016-06-16 18:34:16 -070087
88
89class DemoHost(Host):
90 "Demo host"
91
92 def __init__(self, name, inNamespace=True, **params):
93 Host.__init__(self, name, inNamespace=inNamespace, **params)
94 self.exectoken = "/tmp/mn-exec-token-host-%s" % name
95 self.cmd("touch %s" % self.exectoken)
96
97 def config(self, **params):
98 r = super(Host, self).config(**params)
99
Carmelo Cascone785fada2016-06-16 18:34:16 -0700100 for off in ["rx", "tx", "sg"]:
Yi Tseng7875cb72017-08-08 10:15:58 -0700101 cmd = "/sbin/ethtool --offload %s %s off" % (self.defaultIntf(), off)
Carmelo Cascone785fada2016-06-16 18:34:16 -0700102 self.cmd(cmd)
103
104 # disable IPv6
105 self.cmd("sysctl -w net.ipv6.conf.all.disable_ipv6=1")
106 self.cmd("sysctl -w net.ipv6.conf.default.disable_ipv6=1")
107 self.cmd("sysctl -w net.ipv6.conf.lo.disable_ipv6=1")
108
109 return r
110
111 def startPingBg(self, h):
112 self.cmd(self.getInfiniteCmdBg("ping -i0.5 %s" % h.IP()))
113 self.cmd(self.getInfiniteCmdBg("arping -w5000000 %s" % h.IP()))
114
115 def startIperfServer(self):
116 self.cmd(self.getInfiniteCmdBg("iperf3 -s"))
117
118 def startIperfClient(self, h, flowBw="512k", numFlows=5, duration=5):
119 iperfCmd = "iperf3 -c{} -b{} -P{} -t{}".format(h.IP(), flowBw, numFlows, duration)
120 self.cmd(self.getInfiniteCmdBg(iperfCmd, sleep=0))
121
122 def stop(self):
123 self.cmd("killall iperf3")
124 self.cmd("killall ping")
125 self.cmd("killall arping")
126
127 def describe(self):
128 print "**********"
129 print self.name
130 print "default interface: %s\t%s\t%s" % (
131 self.defaultIntf().name,
132 self.defaultIntf().IP(),
133 self.defaultIntf().MAC()
134 )
135 print "**********"
136
137 def getInfiniteCmdBg(self, cmd, logfile="/dev/null", sleep=1):
138 return "(while [ -e {} ]; " \
139 "do {}; " \
140 "sleep {}; " \
141 "done;) > {} 2>&1 &".format(self.exectoken, cmd, sleep, logfile)
142
143 def getCmdBg(self, cmd, logfile="/dev/null"):
144 return "{} > {} 2>&1 &".format(cmd, logfile)
145
Yi Tseng7875cb72017-08-08 10:15:58 -0700146def generateNetcfg(onosIp, net):
147 netcfg = { 'devices': {}, 'links': {}, 'hosts': {}}
148 # Device configs
149 for sw in net.switches:
150 srcIp = sw.getSourceIp(onosIp)
151 netcfg['devices'][sw.onosDeviceId] = sw.getDeviceConfig(srcIp)
152
153 hostLocations = {}
154 # Link configs
155 for link in net.links:
156 switchPort = link.intf1.name.split('-')
157 sw1Name = switchPort[0] # s11
158 port1Name = switchPort[1] # eth0
159 port1 = port1Name[3:]
160 switchPort = link.intf2.name.split('-')
161 sw2Name = switchPort[0]
162 port2Name = switchPort[1]
163 port2 = port2Name[3:]
164 sw1 = net[sw1Name]
165 sw2 = net[sw2Name]
166 if isinstance(sw1, Host):
167 # record host location and ignore it
168 # e.g. {'h1': 'device:bmv2:11'}
169 hostLocations[sw1.name] = '%s/%s' % (sw2.onosDeviceId, port2)
170 continue
171
172 if isinstance(sw2, Host):
173 # record host location and ignore it
174 # e.g. {'h1': 'device:bmv2:11'}
175 hostLocations[sw2.name] = '%s/%s' % (sw1.onosDeviceId, port1)
176 continue
177
178 linkId = '%s/%s-%s/%s' % (sw1.onosDeviceId, port1, sw2.onosDeviceId, port2)
179 netcfg['links'][linkId] = {
180 'basic': {
181 'type': 'DIRECT',
182 'bandwidth': 50
183 }
184 }
185
186 # Host configs
187 longitude = BASE_LONGITUDE
188 for host in net.hosts:
189 longitude = longitude + BASE_SHIFT
190 hostDefaultIntf = host.defaultIntf()
191 hostMac = host.MAC(hostDefaultIntf)
192 hostIp = host.IP(hostDefaultIntf)
193 hostId = '%s/%d' % (hostMac, VLAN_NONE)
194 location = hostLocations[host.name]
195
196 # use host Id to generate host location
197 hostConfig = {
198 'basic': {
199 'locations': [location],
200 'ips': [hostIp],
201 'name': host.name,
202 'latitude': HOST_BASE_LATITUDE,
203 'longitude': longitude
204 }
205 }
206 netcfg['hosts'][hostId] = hostConfig
207
208 print "Writing network config to %s" % TEMP_NETCFG_FILE
209 with open(TEMP_NETCFG_FILE, 'w') as tempFile:
210 json.dump(netcfg, tempFile)
Carmelo Cascone785fada2016-06-16 18:34:16 -0700211
212def main(args):
Yi Tseng7875cb72017-08-08 10:15:58 -0700213 setLogLevel('debug')
Carmelo Cascone12e4d8d2016-07-05 15:55:15 -0700214 if not args.onos_ip:
215 controller = ONOSCluster('c0', 3)
216 onosIp = controller.nodes()[0].IP()
217 else:
Yi Tseng7875cb72017-08-08 10:15:58 -0700218 controller = RemoteController('c0', ip=args.onos_ip)
Carmelo Cascone12e4d8d2016-07-05 15:55:15 -0700219 onosIp = args.onos_ip
Carmelo Cascone785fada2016-06-16 18:34:16 -0700220
Yi Tseng7875cb72017-08-08 10:15:58 -0700221 topo = ClosTopo(pipeconfId=args.pipeconf_id)
222
Carmelo Cascone12e4d8d2016-07-05 15:55:15 -0700223 net = Mininet(topo=topo, build=False, controller=[controller])
Carmelo Cascone785fada2016-06-16 18:34:16 -0700224
225 net.build()
226 net.start()
227
Carmelo Cascone12e4d8d2016-07-05 15:55:15 -0700228 print "Network started"
Carmelo Cascone785fada2016-06-16 18:34:16 -0700229
Carmelo Cascone12e4d8d2016-07-05 15:55:15 -0700230 # Generate background traffic.
Carmelo Cascone785fada2016-06-16 18:34:16 -0700231 sleep(3)
232 for (h1, h2) in combinations(net.hosts, 2):
233 h1.startPingBg(h2)
234 h2.startPingBg(h1)
235
Carmelo Cascone12e4d8d2016-07-05 15:55:15 -0700236 print "Background ping started"
237
Carmelo Cascone785fada2016-06-16 18:34:16 -0700238 for h in net.hosts:
239 h.startIperfServer()
240
Carmelo Cascone12e4d8d2016-07-05 15:55:15 -0700241 print "Iperf servers started"
Carmelo Cascone785fada2016-06-16 18:34:16 -0700242
243 # sleep(4)
244 # print "Starting traffic from h1 to h3..."
245 # net.hosts[0].startIperfClient(net.hosts[-1], flowBw="200k", numFlows=100, duration=10)
246
Yi Tseng7875cb72017-08-08 10:15:58 -0700247 generateNetcfg(onosIp, net)
248
249 print "Uploading netcfg..."
250 call(("%s/onos-netcfg" % RUN_PACK_PATH, onosIp, TEMP_NETCFG_FILE))
Carmelo Cascone12e4d8d2016-07-05 15:55:15 -0700251
252 if not args.onos_ip:
253 ONOSCLI(net)
254 else:
255 CLI(net)
Carmelo Cascone785fada2016-06-16 18:34:16 -0700256
257 net.stop()
Yi Tseng7875cb72017-08-08 10:15:58 -0700258 call(("rm", "-f", TEMP_NETCFG_FILE))
Carmelo Cascone785fada2016-06-16 18:34:16 -0700259
260
261if __name__ == '__main__':
Carmelo Cascone12e4d8d2016-07-05 15:55:15 -0700262 parser = argparse.ArgumentParser(
263 description='BMv2 mininet demo script (2-stage Clos topology)')
Carmelo Cascone785fada2016-06-16 18:34:16 -0700264 parser.add_argument('--onos-ip', help='ONOS-BMv2 controller IP address',
Carmelo Cascone12e4d8d2016-07-05 15:55:15 -0700265 type=str, action="store", required=False)
Yi Tseng7875cb72017-08-08 10:15:58 -0700266 parser.add_argument('--pipeconf-id', help='Pipeconf ID for switches',
267 type=str, action="store", required=False, default='')
Carmelo Cascone785fada2016-06-16 18:34:16 -0700268 args = parser.parse_args()
269 setLogLevel('info')
270 main(args)