Carmelo Cascone
Committed by Jonathan Hart

Varius updates to BMv2 mininet scripts

Most notably:
- Added support for onos.py's ONOSCluster controller (it works with
	"mn --custom onos.py,bmv2.py --switch onosbmv2 ...")
- Randomly select an open port for the Thrift RPC server

Change-Id: Ifa974741dc4a3693777f01866b1f6203d0e7e75e
1 +import socket
2 +
1 from mininet.log import error, info 3 from mininet.log import error, info
2 from mininet.node import Switch 4 from mininet.node import Switch
3 from os import environ 5 from os import environ
...@@ -7,20 +9,18 @@ from os.path import isfile ...@@ -7,20 +9,18 @@ from os.path import isfile
7 class ONOSBmv2Switch(Switch): 9 class ONOSBmv2Switch(Switch):
8 """BMv2 software switch """ 10 """BMv2 software switch """
9 11
10 - thriftPort = 9090
11 deviceId = 0 12 deviceId = 0
13 + instanceCount = 0
12 14
13 def __init__(self, name, thriftPort=None, deviceId=None, debugger=False, 15 def __init__(self, name, thriftPort=None, deviceId=None, debugger=False,
14 loglevel="warn", elogger=False, persistent=True, **kwargs): 16 loglevel="warn", elogger=False, persistent=True, **kwargs):
15 Switch.__init__(self, name, **kwargs) 17 Switch.__init__(self, name, **kwargs)
16 self.swPath = environ['BMV2_EXE'] 18 self.swPath = environ['BMV2_EXE']
17 self.jsonPath = environ['BMV2_JSON'] 19 self.jsonPath = environ['BMV2_JSON']
18 - if not thriftPort: 20 + if thriftPort:
19 - self.thriftPort = ONOSBmv2Switch.thriftPort
20 - ONOSBmv2Switch.thriftPort += 1
21 - else:
22 self.thriftPort = thriftPort 21 self.thriftPort = thriftPort
23 - ONOSBmv2Switch.thriftPort = max(thriftPort, ONOSBmv2Switch.thriftPort) 22 + else:
23 + self.thriftPort = ONOSBmv2Switch.pickUnusedPort()
24 if not deviceId: 24 if not deviceId:
25 if self.dpid: 25 if self.dpid:
26 self.deviceId = int(self.dpid, 0 if 'x' in self.dpid else 16) 26 self.deviceId = int(self.dpid, 0 if 'x' in self.dpid else 16)
...@@ -39,25 +39,32 @@ class ONOSBmv2Switch(Switch): ...@@ -39,25 +39,32 @@ class ONOSBmv2Switch(Switch):
39 if persistent: 39 if persistent:
40 self.exectoken = "/tmp/bmv2-%d-exec-token" % self.deviceId 40 self.exectoken = "/tmp/bmv2-%d-exec-token" % self.deviceId
41 self.cmd("touch %s" % self.exectoken) 41 self.cmd("touch %s" % self.exectoken)
42 + # Store thrift port for future uses.
43 + self.cmd("echo %d > /tmp/bmv2-%d-thrift-port" % (self.thriftPort, self.deviceId))
44 +
45 + @classmethod
46 + def pickUnusedPort(cls):
47 + s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
48 + s.bind(('localhost', 0))
49 + addr, port = s.getsockname()
50 + s.close()
51 + return port
42 52
43 @classmethod 53 @classmethod
44 def setup(cls): 54 def setup(cls):
45 err = False 55 err = False
46 -
47 if 'BMV2_EXE' not in environ: 56 if 'BMV2_EXE' not in environ:
48 error("ERROR! environment var $BMV2_EXE not set\n") 57 error("ERROR! environment var $BMV2_EXE not set\n")
49 err = True 58 err = True
50 elif not isfile(environ['BMV2_EXE']): 59 elif not isfile(environ['BMV2_EXE']):
51 error("ERROR! BMV2_EXE=%s: no such file\n" % environ['BMV2_EXE']) 60 error("ERROR! BMV2_EXE=%s: no such file\n" % environ['BMV2_EXE'])
52 err = True 61 err = True
53 -
54 if 'BMV2_JSON' not in environ: 62 if 'BMV2_JSON' not in environ:
55 error("ERROR! environment var $BMV2_JSON not set\n") 63 error("ERROR! environment var $BMV2_JSON not set\n")
56 err = True 64 err = True
57 elif not isfile(environ['BMV2_JSON']): 65 elif not isfile(environ['BMV2_JSON']):
58 error("ERROR! BMV2_JSON=%s: no such file\n" % environ['BMV2_JSON']) 66 error("ERROR! BMV2_JSON=%s: no such file\n" % environ['BMV2_JSON'])
59 err = True 67 err = True
60 -
61 if err: 68 if err:
62 exit(1) 69 exit(1)
63 70
...@@ -75,17 +82,23 @@ class ONOSBmv2Switch(Switch): ...@@ -75,17 +82,23 @@ class ONOSBmv2Switch(Switch):
75 args.append('--debugger') 82 args.append('--debugger')
76 args.append('--log-console -L%s' % self.loglevel) 83 args.append('--log-console -L%s' % self.loglevel)
77 args.append(self.jsonPath) 84 args.append(self.jsonPath)
78 - 85 + try: # onos.py
79 - assert controllers[0] 86 + clist = controllers[0].nodes()
80 - c = controllers[0] 87 + except AttributeError:
88 + clist = controllers
89 + assert len(clist) > 0
90 + # BMv2 can't connect to multiple controllers.
91 + # Uniformly balance connections among available ones.
92 + cip = clist[ONOSBmv2Switch.instanceCount % len(clist)].IP()
93 + ONOSBmv2Switch.instanceCount += 1
94 + # BMv2 controler port is hardcoded here as it is hardcoded also in ONOS.
95 + cport = 40123
81 args.append('--') 96 args.append('--')
82 - args.append('--controller-ip %s' % c.IP()) 97 + args.append('--controller-ip %s' % cip)
83 - args.append('--controller-port %d' % c.port) 98 + args.append('--controller-port %d' % cport)
84 99
85 bmv2cmd = " ".join(args) 100 bmv2cmd = " ".join(args)
86 -
87 info("\nStarting BMv2 target: %s\n" % bmv2cmd) 101 info("\nStarting BMv2 target: %s\n" % bmv2cmd)
88 -
89 if self.persistent: 102 if self.persistent:
90 # Re-exec the switch if it crashes. 103 # Re-exec the switch if it crashes.
91 cmdStr = "(while [ -e {} ]; " \ 104 cmdStr = "(while [ -e {} ]; " \
...@@ -94,18 +107,16 @@ class ONOSBmv2Switch(Switch): ...@@ -94,18 +107,16 @@ class ONOSBmv2Switch(Switch):
94 "done;) > {} 2>&1 &".format(self.exectoken, bmv2cmd, self.logfile) 107 "done;) > {} 2>&1 &".format(self.exectoken, bmv2cmd, self.logfile)
95 else: 108 else:
96 cmdStr = "{} > {} 2>&1 &".format(bmv2cmd, self.logfile) 109 cmdStr = "{} > {} 2>&1 &".format(bmv2cmd, self.logfile)
97 -
98 self.cmd(cmdStr) 110 self.cmd(cmdStr)
99 111
100 def stop(self): 112 def stop(self):
101 "Terminate switch." 113 "Terminate switch."
102 self.output.flush() 114 self.output.flush()
103 - if self.persistent: 115 + self.cmd("rm -f /tmp/bmv2-%d-*" % self.deviceId)
104 - self.cmd("rm -f %s" % self.exectoken) 116 + self.cmd("rm -f /tmp/bmv2-%d.log" % self.deviceId)
105 self.cmd('kill %' + self.swPath) 117 self.cmd('kill %' + self.swPath)
106 self.deleteIntfs() 118 self.deleteIntfs()
107 119
108 120
109 ### Exports for bin/mn 121 ### Exports for bin/mn
110 -
111 switches = {'onosbmv2': ONOSBmv2Switch} 122 switches = {'onosbmv2': ONOSBmv2Switch}
......
1 #!/usr/bin/python 1 #!/usr/bin/python
2 2
3 +import os
4 +import sys
3 import argparse 5 import argparse
6 +
7 +# Find and import bmv2.py
8 +if 'ONOS_ROOT' not in os.environ:
9 + print "Environment var $ONOS_ROOT not set"
10 + exit()
11 +else:
12 + sys.path.append(os.environ["ONOS_ROOT"] + "/tools/dev/mininet")
13 + from bmv2 import ONOSBmv2Switch
14 +
4 from itertools import combinations 15 from itertools import combinations
5 from time import sleep 16 from time import sleep
6 17
7 -from bmv2 import ONOSBmv2Switch
8 from mininet.cli import CLI 18 from mininet.cli import CLI
9 from mininet.link import TCLink 19 from mininet.link import TCLink
10 from mininet.log import setLogLevel 20 from mininet.log import setLogLevel
...@@ -29,8 +39,8 @@ class ClosTopo(Topo): ...@@ -29,8 +39,8 @@ class ClosTopo(Topo):
29 bmv2Switches[switchId] = self.addSwitch(switchId, 39 bmv2Switches[switchId] = self.addSwitch(switchId,
30 cls=ONOSBmv2Switch, 40 cls=ONOSBmv2Switch,
31 loglevel="warn", 41 loglevel="warn",
32 - device_id=int(switchId[1:]), 42 + deviceId=int(switchId[1:]),
33 - thrift_port=tport) 43 + thriftPort=tport)
34 tport += 1 44 tport += 1
35 45
36 for i in (1, 2, 3): 46 for i in (1, 2, 3):
......