Committed by
Gerrit Code Review
Added Bmv2 demo scripts (mininet and netcfg)
Change-Id: I7471a9ebe55f74dbe8c63faef9a8685c48621862
Showing
3 changed files
with
429 additions
and
0 deletions
tools/dev/mininet/bmv2.py
0 → 100644
1 | +from mininet.log import error, info | ||
2 | +from mininet.node import Switch | ||
3 | +from os import environ | ||
4 | +from os.path import isfile | ||
5 | + | ||
6 | + | ||
7 | +class ONOSBmv2Switch(Switch): | ||
8 | + """BMv2 software switch """ | ||
9 | + | ||
10 | + thriftPort = 9090 | ||
11 | + deviceId = 0 | ||
12 | + | ||
13 | + def __init__(self, name, thriftPort=None, deviceId=None, debugger=False, | ||
14 | + loglevel="warn", elogger=False, persistent=True, **kwargs): | ||
15 | + Switch.__init__(self, name, **kwargs) | ||
16 | + self.swPath = environ['BMV2_EXE'] | ||
17 | + self.jsonPath = environ['BMV2_JSON'] | ||
18 | + if not thriftPort: | ||
19 | + self.thriftPort = ONOSBmv2Switch.thriftPort | ||
20 | + ONOSBmv2Switch.thriftPort += 1 | ||
21 | + else: | ||
22 | + self.thriftPort = thriftPort | ||
23 | + ONOSBmv2Switch.thriftPort = max(thriftPort, ONOSBmv2Switch.thriftPort) | ||
24 | + if not deviceId: | ||
25 | + if self.dpid: | ||
26 | + self.deviceId = int(self.dpid, 0 if 'x' in self.dpid else 16) | ||
27 | + else: | ||
28 | + self.deviceId = ONOSBmv2Switch.deviceId | ||
29 | + ONOSBmv2Switch.deviceId += 1 | ||
30 | + else: | ||
31 | + self.deviceId = deviceId | ||
32 | + ONOSBmv2Switch.deviceId = max(deviceId, ONOSBmv2Switch.deviceId) | ||
33 | + self.debugger = debugger | ||
34 | + self.loglevel = loglevel | ||
35 | + self.logfile = '/tmp/bmv2-%d.log' % self.deviceId | ||
36 | + self.output = open(self.logfile, 'w') | ||
37 | + self.elogger = elogger | ||
38 | + self.persistent = persistent | ||
39 | + if persistent: | ||
40 | + self.exectoken = "/tmp/bmv2-%d-exec-token" % self.deviceId | ||
41 | + self.cmd("touch %s" % self.exectoken) | ||
42 | + | ||
43 | + @classmethod | ||
44 | + def setup(cls): | ||
45 | + err = False | ||
46 | + | ||
47 | + if 'BMV2_EXE' not in environ: | ||
48 | + error("ERROR! environment var $BMV2_EXE not set\n") | ||
49 | + err = True | ||
50 | + elif not isfile(environ['BMV2_EXE']): | ||
51 | + error("ERROR! BMV2_EXE=%s: no such file\n" % environ['BMV2_EXE']) | ||
52 | + err = True | ||
53 | + | ||
54 | + if 'BMV2_JSON' not in environ: | ||
55 | + error("ERROR! environment var $BMV2_JSON not set\n") | ||
56 | + err = True | ||
57 | + elif not isfile(environ['BMV2_JSON']): | ||
58 | + error("ERROR! BMV2_JSON=%s: no such file\n" % environ['BMV2_JSON']) | ||
59 | + err = True | ||
60 | + | ||
61 | + if err: | ||
62 | + exit(1) | ||
63 | + | ||
64 | + def start(self, controllers): | ||
65 | + args = [self.swPath, '--device-id %s' % str(self.deviceId)] | ||
66 | + for port, intf in self.intfs.items(): | ||
67 | + if not intf.IP(): | ||
68 | + args.append('-i %d@%s' % (port, intf.name)) | ||
69 | + if self.thriftPort: | ||
70 | + args.append('--thrift-port %d' % self.thriftPort) | ||
71 | + if self.elogger: | ||
72 | + nanomsg = 'ipc:///tmp/bmv2-%d-log.ipc' % self.deviceId | ||
73 | + args.append('--nanolog %s' % nanomsg) | ||
74 | + if self.debugger: | ||
75 | + args.append('--debugger') | ||
76 | + args.append('--log-console -L%s' % self.loglevel) | ||
77 | + args.append(self.jsonPath) | ||
78 | + | ||
79 | + assert controllers[0] | ||
80 | + c = controllers[0] | ||
81 | + args.append('--') | ||
82 | + args.append('--controller-ip %s' % c.IP()) | ||
83 | + args.append('--controller-port %d' % c.port) | ||
84 | + | ||
85 | + bmv2cmd = " ".join(args) | ||
86 | + | ||
87 | + info("\nStarting BMv2 target: %s\n" % bmv2cmd) | ||
88 | + | ||
89 | + if self.persistent: | ||
90 | + # Re-exec the switch if it crashes. | ||
91 | + cmdStr = "(while [ -e {} ]; " \ | ||
92 | + "do {} ; " \ | ||
93 | + "sleep 1; " \ | ||
94 | + "done;) > {} 2>&1 &".format(self.exectoken, bmv2cmd, self.logfile) | ||
95 | + else: | ||
96 | + cmdStr = "{} > {} 2>&1 &".format(bmv2cmd, self.logfile) | ||
97 | + | ||
98 | + self.cmd(cmdStr) | ||
99 | + | ||
100 | + def stop(self): | ||
101 | + "Terminate switch." | ||
102 | + self.output.flush() | ||
103 | + if self.persistent: | ||
104 | + self.cmd("rm -f %s" % self.exectoken) | ||
105 | + self.cmd('kill %' + self.swPath) | ||
106 | + self.deleteIntfs() | ||
107 | + | ||
108 | + | ||
109 | +### Exports for bin/mn | ||
110 | + | ||
111 | +switches = {'onosbmv2': ONOSBmv2Switch} |
tools/test/topos/bmv2-demo-cfg.json
0 → 100644
1 | +{ | ||
2 | + "apps": { | ||
3 | + "org.onosproject.core": { | ||
4 | + "core": { | ||
5 | + "linkDiscoveryMode": "STRICT" | ||
6 | + } | ||
7 | + } | ||
8 | + }, | ||
9 | + "devices": { | ||
10 | + "bmv2:192.168.57.100:9090#11": { | ||
11 | + "basic": { | ||
12 | + "name": "bmv2:11", | ||
13 | + "latitude": 40, | ||
14 | + "longitude": -107 | ||
15 | + } | ||
16 | + }, | ||
17 | + "bmv2:192.168.57.100:9091#12": { | ||
18 | + "basic": { | ||
19 | + "name": "bmv2:12", | ||
20 | + "latitude": 40, | ||
21 | + "longitude": -99 | ||
22 | + } | ||
23 | + }, | ||
24 | + "bmv2:192.168.57.100:9092#13": { | ||
25 | + "basic": { | ||
26 | + "name": "bmv2:13", | ||
27 | + "latitude": 40, | ||
28 | + "longitude": -91 | ||
29 | + } | ||
30 | + }, | ||
31 | + "bmv2:192.168.57.100:9093#21": { | ||
32 | + "basic": { | ||
33 | + "name": "bmv2:21", | ||
34 | + "latitude": 46, | ||
35 | + "longitude": -107 | ||
36 | + } | ||
37 | + }, | ||
38 | + "bmv2:192.168.57.100:9094#22": { | ||
39 | + "basic": { | ||
40 | + "name": "bmv2:22", | ||
41 | + "latitude": 46, | ||
42 | + "longitude": -99 | ||
43 | + } | ||
44 | + }, | ||
45 | + "bmv2:192.168.57.100:9095#23": { | ||
46 | + "basic": { | ||
47 | + "name": "bmv2:23", | ||
48 | + "latitude": 46, | ||
49 | + "longitude": -91 | ||
50 | + } | ||
51 | + } | ||
52 | + }, | ||
53 | + "links": { | ||
54 | + "bmv2:192.168.57.100:9090#11/1-bmv2:192.168.57.100:9093#21/1": { | ||
55 | + "basic": {} | ||
56 | + }, | ||
57 | + "bmv2:192.168.57.100:9093#21/1-bmv2:192.168.57.100:9090#11/1": { | ||
58 | + "basic": {} | ||
59 | + }, | ||
60 | + "bmv2:192.168.57.100:9090#11/2-bmv2:192.168.57.100:9093#21/2": { | ||
61 | + "basic": {} | ||
62 | + }, | ||
63 | + "bmv2:192.168.57.100:9093#21/2-bmv2:192.168.57.100:9090#11/2": { | ||
64 | + "basic": {} | ||
65 | + }, | ||
66 | + "bmv2:192.168.57.100:9090#11/3-bmv2:192.168.57.100:9094#22/1": { | ||
67 | + "basic": {} | ||
68 | + }, | ||
69 | + "bmv2:192.168.57.100:9094#22/1-bmv2:192.168.57.100:9090#11/3": { | ||
70 | + "basic": {} | ||
71 | + }, | ||
72 | + "bmv2:192.168.57.100:9090#11/4-bmv2:192.168.57.100:9095#23/1": { | ||
73 | + "basic": {} | ||
74 | + }, | ||
75 | + "bmv2:192.168.57.100:9095#23/1-bmv2:192.168.57.100:9090#11/4": { | ||
76 | + "basic": {} | ||
77 | + }, | ||
78 | + "bmv2:192.168.57.100:9091#12/1-bmv2:192.168.57.100:9093#21/3": { | ||
79 | + "basic": {} | ||
80 | + }, | ||
81 | + "bmv2:192.168.57.100:9093#21/3-bmv2:192.168.57.100:9091#12/1": { | ||
82 | + "basic": {} | ||
83 | + }, | ||
84 | + "bmv2:192.168.57.100:9091#12/2-bmv2:192.168.57.100:9094#22/2": { | ||
85 | + "basic": {} | ||
86 | + }, | ||
87 | + "bmv2:192.168.57.100:9094#22/2-bmv2:192.168.57.100:9091#12/2": { | ||
88 | + "basic": {} | ||
89 | + }, | ||
90 | + "bmv2:192.168.57.100:9091#12/3-bmv2:192.168.57.100:9094#22/3": { | ||
91 | + "basic": {} | ||
92 | + }, | ||
93 | + "bmv2:192.168.57.100:9094#22/3-bmv2:192.168.57.100:9091#12/3": { | ||
94 | + "basic": {} | ||
95 | + }, | ||
96 | + "bmv2:192.168.57.100:9091#12/4-bmv2:192.168.57.100:9095#23/2": { | ||
97 | + "basic": {} | ||
98 | + }, | ||
99 | + "bmv2:192.168.57.100:9095#23/2-bmv2:192.168.57.100:9091#12/4": { | ||
100 | + "basic": {} | ||
101 | + }, | ||
102 | + "bmv2:192.168.57.100:9092#13/1-bmv2:192.168.57.100:9093#21/4": { | ||
103 | + "basic": {} | ||
104 | + }, | ||
105 | + "bmv2:192.168.57.100:9093#21/4-bmv2:192.168.57.100:9092#13/1": { | ||
106 | + "basic": {} | ||
107 | + }, | ||
108 | + "bmv2:192.168.57.100:9092#13/2-bmv2:192.168.57.100:9094#22/4": { | ||
109 | + "basic": {} | ||
110 | + }, | ||
111 | + "bmv2:192.168.57.100:9094#22/4-bmv2:192.168.57.100:9092#13/2": { | ||
112 | + "basic": {} | ||
113 | + }, | ||
114 | + "bmv2:192.168.57.100:9092#13/3-bmv2:192.168.57.100:9095#23/3": { | ||
115 | + "basic": {} | ||
116 | + }, | ||
117 | + "bmv2:192.168.57.100:9095#23/3-bmv2:192.168.57.100:9092#13/3": { | ||
118 | + "basic": {} | ||
119 | + }, | ||
120 | + "bmv2:192.168.57.100:9092#13/4-bmv2:192.168.57.100:9095#23/4": { | ||
121 | + "basic": {} | ||
122 | + }, | ||
123 | + "bmv2:192.168.57.100:9095#23/4-bmv2:192.168.57.100:9092#13/4": { | ||
124 | + "basic": {} | ||
125 | + } | ||
126 | + }, | ||
127 | + "hosts": { | ||
128 | + "00:00:00:00:00:01/-1": { | ||
129 | + "basic": { | ||
130 | + "location": "bmv2:192.168.57.100:9090#11/5", | ||
131 | + "ips": [ | ||
132 | + "10.0.0.1" | ||
133 | + ], | ||
134 | + "name": "h1", | ||
135 | + "latitude": 36, | ||
136 | + "longitude": -107 | ||
137 | + } | ||
138 | + }, | ||
139 | + "00:00:00:00:00:02/-1": { | ||
140 | + "basic": { | ||
141 | + "location": "bmv2:192.168.57.100:9091#12/5", | ||
142 | + "ips": [ | ||
143 | + "10.0.0.2" | ||
144 | + ], | ||
145 | + "name": "h2", | ||
146 | + "latitude": 36, | ||
147 | + "longitude": -99 | ||
148 | + } | ||
149 | + }, | ||
150 | + "00:00:00:00:00:03/-1": { | ||
151 | + "basic": { | ||
152 | + "location": "bmv2:192.168.57.100:9092#13/5", | ||
153 | + "ips": [ | ||
154 | + "10.0.0.3" | ||
155 | + ], | ||
156 | + "name": "h3", | ||
157 | + "latitude": 36, | ||
158 | + "longitude": -91 | ||
159 | + } | ||
160 | + } | ||
161 | + } | ||
162 | +} |
tools/test/topos/bmv2-demo.py
0 → 100644
1 | +#!/usr/bin/python | ||
2 | + | ||
3 | +import argparse | ||
4 | +from itertools import combinations | ||
5 | +from time import sleep | ||
6 | + | ||
7 | +from bmv2 import ONOSBmv2Switch | ||
8 | +from mininet.cli import CLI | ||
9 | +from mininet.link import TCLink | ||
10 | +from mininet.log import setLogLevel | ||
11 | +from mininet.net import Mininet | ||
12 | +from mininet.node import RemoteController, Host | ||
13 | +from mininet.topo import Topo | ||
14 | + | ||
15 | + | ||
16 | +class ClosTopo(Topo): | ||
17 | + "2 stage Clos topology" | ||
18 | + | ||
19 | + def __init__(self, **opts): | ||
20 | + # Initialize topology and default options | ||
21 | + Topo.__init__(self, **opts) | ||
22 | + | ||
23 | + bmv2SwitchIds = ["s11", "s12", "s13", "s21", "s22", "s23"] | ||
24 | + | ||
25 | + bmv2Switches = {} | ||
26 | + | ||
27 | + tport = 9090 | ||
28 | + for switchId in bmv2SwitchIds: | ||
29 | + bmv2Switches[switchId] = self.addSwitch(switchId, | ||
30 | + cls=ONOSBmv2Switch, | ||
31 | + loglevel="warn", | ||
32 | + device_id=int(switchId[1:]), | ||
33 | + thrift_port=tport) | ||
34 | + tport += 1 | ||
35 | + | ||
36 | + for i in (1, 2, 3): | ||
37 | + for j in (1, 2, 3): | ||
38 | + if i == j: | ||
39 | + # 2 links | ||
40 | + self.addLink(bmv2Switches["s1%d" % i], bmv2Switches["s2%d" % j], | ||
41 | + cls=TCLink, bw=50) | ||
42 | + self.addLink(bmv2Switches["s1%d" % i], bmv2Switches["s2%d" % j], | ||
43 | + cls=TCLink, bw=50) | ||
44 | + else: | ||
45 | + self.addLink(bmv2Switches["s1%d" % i], bmv2Switches["s2%d" % j], | ||
46 | + cls=TCLink, bw=50) | ||
47 | + | ||
48 | + for hostId in (1, 2, 3): | ||
49 | + host = self.addHost("h%d" % hostId, | ||
50 | + cls=DemoHost, | ||
51 | + ip="10.0.0.%d/24" % hostId, | ||
52 | + mac='00:00:00:00:00:%02x' % hostId) | ||
53 | + self.addLink(host, bmv2Switches["s1%d" % hostId], cls=TCLink, bw=22) | ||
54 | + | ||
55 | + | ||
56 | +class DemoHost(Host): | ||
57 | + "Demo host" | ||
58 | + | ||
59 | + def __init__(self, name, inNamespace=True, **params): | ||
60 | + Host.__init__(self, name, inNamespace=inNamespace, **params) | ||
61 | + self.exectoken = "/tmp/mn-exec-token-host-%s" % name | ||
62 | + self.cmd("touch %s" % self.exectoken) | ||
63 | + | ||
64 | + def config(self, **params): | ||
65 | + r = super(Host, self).config(**params) | ||
66 | + | ||
67 | + self.defaultIntf().rename("eth0") | ||
68 | + | ||
69 | + for off in ["rx", "tx", "sg"]: | ||
70 | + cmd = "/sbin/ethtool --offload eth0 %s off" % off | ||
71 | + self.cmd(cmd) | ||
72 | + | ||
73 | + # disable IPv6 | ||
74 | + self.cmd("sysctl -w net.ipv6.conf.all.disable_ipv6=1") | ||
75 | + self.cmd("sysctl -w net.ipv6.conf.default.disable_ipv6=1") | ||
76 | + self.cmd("sysctl -w net.ipv6.conf.lo.disable_ipv6=1") | ||
77 | + | ||
78 | + return r | ||
79 | + | ||
80 | + def startPingBg(self, h): | ||
81 | + self.cmd(self.getInfiniteCmdBg("ping -i0.5 %s" % h.IP())) | ||
82 | + self.cmd(self.getInfiniteCmdBg("arping -w5000000 %s" % h.IP())) | ||
83 | + | ||
84 | + def startIperfServer(self): | ||
85 | + self.cmd(self.getInfiniteCmdBg("iperf3 -s")) | ||
86 | + | ||
87 | + def startIperfClient(self, h, flowBw="512k", numFlows=5, duration=5): | ||
88 | + iperfCmd = "iperf3 -c{} -b{} -P{} -t{}".format(h.IP(), flowBw, numFlows, duration) | ||
89 | + self.cmd(self.getInfiniteCmdBg(iperfCmd, sleep=0)) | ||
90 | + | ||
91 | + def stop(self): | ||
92 | + self.cmd("killall iperf3") | ||
93 | + self.cmd("killall ping") | ||
94 | + self.cmd("killall arping") | ||
95 | + | ||
96 | + def describe(self): | ||
97 | + print "**********" | ||
98 | + print self.name | ||
99 | + print "default interface: %s\t%s\t%s" % ( | ||
100 | + self.defaultIntf().name, | ||
101 | + self.defaultIntf().IP(), | ||
102 | + self.defaultIntf().MAC() | ||
103 | + ) | ||
104 | + print "**********" | ||
105 | + | ||
106 | + def getInfiniteCmdBg(self, cmd, logfile="/dev/null", sleep=1): | ||
107 | + return "(while [ -e {} ]; " \ | ||
108 | + "do {}; " \ | ||
109 | + "sleep {}; " \ | ||
110 | + "done;) > {} 2>&1 &".format(self.exectoken, cmd, sleep, logfile) | ||
111 | + | ||
112 | + def getCmdBg(self, cmd, logfile="/dev/null"): | ||
113 | + return "{} > {} 2>&1 &".format(cmd, logfile) | ||
114 | + | ||
115 | + | ||
116 | +def main(args): | ||
117 | + topo = ClosTopo() | ||
118 | + | ||
119 | + net = Mininet(topo=topo, build=False) | ||
120 | + | ||
121 | + net.addController('c0', controller=RemoteController, ip=args.onos_ip, port=args.onos_port) | ||
122 | + | ||
123 | + net.build() | ||
124 | + net.start() | ||
125 | + | ||
126 | + print "Network started..." | ||
127 | + | ||
128 | + # Generates background traffic (needed for host discovery and bmv2 config swap). | ||
129 | + sleep(3) | ||
130 | + for (h1, h2) in combinations(net.hosts, 2): | ||
131 | + h1.startPingBg(h2) | ||
132 | + h2.startPingBg(h1) | ||
133 | + | ||
134 | + for h in net.hosts: | ||
135 | + h.startIperfServer() | ||
136 | + | ||
137 | + print "Background ping started..." | ||
138 | + | ||
139 | + # sleep(4) | ||
140 | + # print "Starting traffic from h1 to h3..." | ||
141 | + # net.hosts[0].startIperfClient(net.hosts[-1], flowBw="200k", numFlows=100, duration=10) | ||
142 | + | ||
143 | + CLI(net) | ||
144 | + | ||
145 | + net.stop() | ||
146 | + | ||
147 | + | ||
148 | +if __name__ == '__main__': | ||
149 | + parser = argparse.ArgumentParser(description='BMv2 mininet demo script (2-stage Clos topology)') | ||
150 | + parser.add_argument('--onos-ip', help='ONOS-BMv2 controller IP address', | ||
151 | + type=str, action="store", required=True) | ||
152 | + parser.add_argument('--onos-port', help='ONOS-BMv2 controller port', | ||
153 | + type=int, action="store", default=40123) | ||
154 | + args = parser.parse_args() | ||
155 | + setLogLevel('info') | ||
156 | + main(args) |
-
Please register or login to post a comment