Carmelo Cascone
Committed by Gerrit Code Review

Added Bmv2 demo scripts (mininet and netcfg)

Change-Id: I7471a9ebe55f74dbe8c63faef9a8685c48621862
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}
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 +}
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)