From 7875cb78bbb40fc3b627d92fd36a35f2ce9142fc Mon Sep 17 00:00:00 2001 From: Yi Tseng Date: Tue, 8 Aug 2017 10:15:58 -0700 Subject: [PATCH] [ONOS-6854] refactor bmv2-demo.py Change-Id: I9b7460b15f6664363f2ff0b16110e2b3bc4dedeb --- tools/dev/mininet/bmv2.py | 79 +++++++++----- tools/test/topos/bmv2-demo-cfg.json | 162 ---------------------------- tools/test/topos/bmv2-demo.py | 127 ++++++++++++++++++---- 3 files changed, 156 insertions(+), 212 deletions(-) delete mode 100644 tools/test/topos/bmv2-demo-cfg.json mode change 100644 => 100755 tools/test/topos/bmv2-demo.py diff --git a/tools/dev/mininet/bmv2.py b/tools/dev/mininet/bmv2.py index 5071c88c13..c4e27d5d86 100644 --- a/tools/dev/mininet/bmv2.py +++ b/tools/dev/mininet/bmv2.py @@ -23,7 +23,8 @@ class ONOSBmv2Switch(Switch): instanceCount = 0 def __init__(self, name, json=None, debugger=False, loglevel="warn", elogger=False, - persistent=False, grpcPort=None, thriftPort=None, netcfg=True, **kwargs): + persistent=False, grpcPort=None, thriftPort=None, netcfg=True, + pipeconfId="", **kwargs): Switch.__init__(self, name, **kwargs) self.grpcPort = ONOSBmv2Switch.pickUnusedPort() if not grpcPort else grpcPort self.thriftPort = ONOSBmv2Switch.pickUnusedPort() if not thriftPort else thriftPort @@ -40,12 +41,25 @@ class ONOSBmv2Switch(Switch): self.persistent = persistent self.netcfg = netcfg self.netcfgfile = '/tmp/bmv2-%d-netcfg.json' % self.deviceId + self.pipeconfId = pipeconfId if persistent: self.exectoken = "/tmp/bmv2-%d-exec-token" % self.deviceId self.cmd("touch %s" % self.exectoken) # Store thrift port for future uses. self.cmd("echo %d > /tmp/bmv2-%d-grpc-port" % (self.grpcPort, self.deviceId)) + if 'longitude' in kwargs: + self.longitude = kwargs['longitude'] + else: + self.longitude = None + + if 'latitude' in kwargs: + self.latitude = kwargs['latitude'] + else: + self.latitude = None + + self.onosDeviceId = "device:bmv2:%d" % self.deviceId + @classmethod def pickUnusedPort(cls): s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) @@ -63,15 +77,7 @@ class ONOSBmv2Switch(Switch): r = re.search(r"src (\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})", ipRouteOut) return r.group(1) if r else None - def doOnosNetcfg(self, controllerIP): - """ - Notifies ONOS about the new device via Netcfg. - """ - srcIP = self.getSourceIp(controllerIP) - if not srcIP: - warn("WARN: unable to get device IP address, won't do onos-netcfg") - return - onosDeviceId = "bmv2:%s" % self.deviceId + def getDeviceConfig(self, srcIP): portData = {} portId = 1 for intfName in self.intfNames(): @@ -87,25 +93,44 @@ class ONOSBmv2Switch(Switch): } portId += 1 + basicCfg = { + "driver": "bmv2" + } + + if self.longitude and self.latitude: + basicCfg["longitude"] = self.longitude + basicCfg["latitude"] = self.latitude + + cfgData = { + "generalprovider": { + "p4runtime": { + "ip": srcIP, + "port": self.grpcPort, + "deviceId": self.deviceId, + "deviceKeyId": "p4runtime:%s" % self.onosDeviceId + } + }, + "piPipeconf": { + "piPipeconfId": self.pipeconfId + }, + "basic": basicCfg, + "ports": portData + } + + return cfgData + + def doOnosNetcfg(self, controllerIP): + """ + Notifies ONOS about the new device via Netcfg. + """ + srcIP = self.getSourceIp(controllerIP) + if not srcIP: + warn("WARN: unable to get device IP address, won't do onos-netcfg") + return + cfgData = { "devices": { - "device:%s" % onosDeviceId: { - "generalprovider": { - "p4runtime": { - "ip": srcIP, - "port": self.grpcPort, - "deviceId": self.deviceId, - "deviceKeyId": "p4runtime:%s" % onosDeviceId - } - }, - "piPipeconf": { - "piPipeconfId": "" - }, - "basic": { - "driver": "bmv2" - }, - "ports": portData - } + self.onosDeviceId: self.getDeviceConfig(srcIP) } } with open(self.netcfgfile, 'w') as fp: diff --git a/tools/test/topos/bmv2-demo-cfg.json b/tools/test/topos/bmv2-demo-cfg.json deleted file mode 100644 index 6c745b5139..0000000000 --- a/tools/test/topos/bmv2-demo-cfg.json +++ /dev/null @@ -1,162 +0,0 @@ -{ - "apps": { - "org.onosproject.core": { - "core": { - "linkDiscoveryMode": "STRICT" - } - } - }, - "devices": { - "bmv2:192.168.123.4:9090#11": { - "basic": { - "name": "bmv2:11", - "latitude": 40, - "longitude": -107 - } - }, - "bmv2:192.168.123.4:9091#12": { - "basic": { - "name": "bmv2:12", - "latitude": 40, - "longitude": -99 - } - }, - "bmv2:192.168.123.4:9092#13": { - "basic": { - "name": "bmv2:13", - "latitude": 40, - "longitude": -91 - } - }, - "bmv2:192.168.123.4:9093#21": { - "basic": { - "name": "bmv2:21", - "latitude": 46, - "longitude": -107 - } - }, - "bmv2:192.168.123.4:9094#22": { - "basic": { - "name": "bmv2:22", - "latitude": 46, - "longitude": -99 - } - }, - "bmv2:192.168.123.4:9095#23": { - "basic": { - "name": "bmv2:23", - "latitude": 46, - "longitude": -91 - } - } - }, - "links": { - "bmv2:192.168.123.4:9090#11/1-bmv2:192.168.123.4:9093#21/1": { - "basic": {} - }, - "bmv2:192.168.123.4:9093#21/1-bmv2:192.168.123.4:9090#11/1": { - "basic": {} - }, - "bmv2:192.168.123.4:9090#11/2-bmv2:192.168.123.4:9093#21/2": { - "basic": {} - }, - "bmv2:192.168.123.4:9093#21/2-bmv2:192.168.123.4:9090#11/2": { - "basic": {} - }, - "bmv2:192.168.123.4:9090#11/3-bmv2:192.168.123.4:9094#22/1": { - "basic": {} - }, - "bmv2:192.168.123.4:9094#22/1-bmv2:192.168.123.4:9090#11/3": { - "basic": {} - }, - "bmv2:192.168.123.4:9090#11/4-bmv2:192.168.123.4:9095#23/1": { - "basic": {} - }, - "bmv2:192.168.123.4:9095#23/1-bmv2:192.168.123.4:9090#11/4": { - "basic": {} - }, - "bmv2:192.168.123.4:9091#12/1-bmv2:192.168.123.4:9093#21/3": { - "basic": {} - }, - "bmv2:192.168.123.4:9093#21/3-bmv2:192.168.123.4:9091#12/1": { - "basic": {} - }, - "bmv2:192.168.123.4:9091#12/2-bmv2:192.168.123.4:9094#22/2": { - "basic": {} - }, - "bmv2:192.168.123.4:9094#22/2-bmv2:192.168.123.4:9091#12/2": { - "basic": {} - }, - "bmv2:192.168.123.4:9091#12/3-bmv2:192.168.123.4:9094#22/3": { - "basic": {} - }, - "bmv2:192.168.123.4:9094#22/3-bmv2:192.168.123.4:9091#12/3": { - "basic": {} - }, - "bmv2:192.168.123.4:9091#12/4-bmv2:192.168.123.4:9095#23/2": { - "basic": {} - }, - "bmv2:192.168.123.4:9095#23/2-bmv2:192.168.123.4:9091#12/4": { - "basic": {} - }, - "bmv2:192.168.123.4:9092#13/1-bmv2:192.168.123.4:9093#21/4": { - "basic": {} - }, - "bmv2:192.168.123.4:9093#21/4-bmv2:192.168.123.4:9092#13/1": { - "basic": {} - }, - "bmv2:192.168.123.4:9092#13/2-bmv2:192.168.123.4:9094#22/4": { - "basic": {} - }, - "bmv2:192.168.123.4:9094#22/4-bmv2:192.168.123.4:9092#13/2": { - "basic": {} - }, - "bmv2:192.168.123.4:9092#13/3-bmv2:192.168.123.4:9095#23/3": { - "basic": {} - }, - "bmv2:192.168.123.4:9095#23/3-bmv2:192.168.123.4:9092#13/3": { - "basic": {} - }, - "bmv2:192.168.123.4:9092#13/4-bmv2:192.168.123.4:9095#23/4": { - "basic": {} - }, - "bmv2:192.168.123.4:9095#23/4-bmv2:192.168.123.4:9092#13/4": { - "basic": {} - } - }, - "hosts": { - "00:00:00:00:00:01/-1": { - "basic": { - "locations": ["bmv2:192.168.123.4:9090#11/5"], - "ips": [ - "10.0.0.1" - ], - "name": "h1", - "latitude": 36, - "longitude": -107 - } - }, - "00:00:00:00:00:02/-1": { - "basic": { - "locations": ["bmv2:192.168.123.4:9091#12/5"], - "ips": [ - "10.0.0.2" - ], - "name": "h2", - "latitude": 36, - "longitude": -99 - } - }, - "00:00:00:00:00:03/-1": { - "basic": { - "locations": ["bmv2:192.168.123.4:9092#13/5"], - "ips": [ - "10.0.0.3" - ], - "name": "h3", - "latitude": 36, - "longitude": -91 - } - } - } -} diff --git a/tools/test/topos/bmv2-demo.py b/tools/test/topos/bmv2-demo.py old mode 100644 new mode 100755 index 646c73535b..a2aeaa6123 --- a/tools/test/topos/bmv2-demo.py +++ b/tools/test/topos/bmv2-demo.py @@ -2,8 +2,18 @@ import os import sys +import json import argparse +TEMP_NETCFG_FILE = '/tmp/bmv2-demo-cfg.json' +BASE_LONGITUDE = -115 +SWITCH_BASE_LATITUDE = 25 +HOST_BASE_LATITUDE = 28 +BASE_SHIFT = 8 +VLAN_NONE = -1 +DEFAULT_SW_BW = 50 +DEFAULT_HOST_BW = 25 + if 'ONOS_ROOT' not in os.environ: print "Environment var $ONOS_ROOT not set" exit() @@ -28,47 +38,52 @@ from mininet.link import TCLink from mininet.log import setLogLevel from mininet.net import Mininet from mininet.node import RemoteController, Host -from mininet.topo import Topo, SingleSwitchTopo - +from mininet.topo import Topo class ClosTopo(Topo): "2 stage Clos topology" - def __init__(self, **opts): + def __init__(self, pipeconfId="", **opts): # Initialize topology and default options Topo.__init__(self, **opts) bmv2SwitchIds = ["s11", "s12", "s13", "s21", "s22", "s23"] - bmv2Switches = {} - tport = 9090 for switchId in bmv2SwitchIds: + deviceId=int(switchId[1:]) + # Use first number in device id to calculate latitude (row number) + latitude = SWITCH_BASE_LATITUDE + (deviceId // 10) * BASE_SHIFT + + # Use second number in device id to calculate longitude (column number) + longitude = BASE_LONGITUDE + (deviceId % 10) * BASE_SHIFT bmv2Switches[switchId] = self.addSwitch(switchId, cls=ONOSBmv2Switch, loglevel="warn", - deviceId=int(switchId[1:]), - thriftPort=tport) - tport += 1 + deviceId=deviceId, + netcfg=False, + longitude=longitude, + latitude=latitude, + pipeconfId=pipeconfId) for i in (1, 2, 3): for j in (1, 2, 3): if i == j: # 2 links self.addLink(bmv2Switches["s1%d" % i], bmv2Switches["s2%d" % j], - cls=TCLink, bw=50) + cls=TCLink, bw=DEFAULT_SW_BW) self.addLink(bmv2Switches["s1%d" % i], bmv2Switches["s2%d" % j], - cls=TCLink, bw=50) + cls=TCLink, bw=DEFAULT_SW_BW) else: self.addLink(bmv2Switches["s1%d" % i], bmv2Switches["s2%d" % j], - cls=TCLink, bw=50) + cls=TCLink, bw=DEFAULT_SW_BW) for hostId in (1, 2, 3): host = self.addHost("h%d" % hostId, cls=DemoHost, ip="10.0.0.%d/24" % hostId, mac='00:00:00:00:00:%02x' % hostId) - self.addLink(host, bmv2Switches["s1%d" % hostId], cls=TCLink, bw=22) + self.addLink(host, bmv2Switches["s1%d" % hostId], cls=TCLink, bw=DEFAULT_HOST_BW) class DemoHost(Host): @@ -82,10 +97,8 @@ class DemoHost(Host): def config(self, **params): r = super(Host, self).config(**params) - self.defaultIntf().rename("eth0") - for off in ["rx", "tx", "sg"]: - cmd = "/sbin/ethtool --offload eth0 %s off" % off + cmd = "/sbin/ethtool --offload %s %s off" % (self.defaultIntf(), off) self.cmd(cmd) # disable IPv6 @@ -130,17 +143,83 @@ class DemoHost(Host): def getCmdBg(self, cmd, logfile="/dev/null"): return "{} > {} 2>&1 &".format(cmd, logfile) +def generateNetcfg(onosIp, net): + netcfg = { 'devices': {}, 'links': {}, 'hosts': {}} + # Device configs + for sw in net.switches: + srcIp = sw.getSourceIp(onosIp) + netcfg['devices'][sw.onosDeviceId] = sw.getDeviceConfig(srcIp) + + hostLocations = {} + # Link configs + for link in net.links: + switchPort = link.intf1.name.split('-') + sw1Name = switchPort[0] # s11 + port1Name = switchPort[1] # eth0 + port1 = port1Name[3:] + switchPort = link.intf2.name.split('-') + sw2Name = switchPort[0] + port2Name = switchPort[1] + port2 = port2Name[3:] + sw1 = net[sw1Name] + sw2 = net[sw2Name] + if isinstance(sw1, Host): + # record host location and ignore it + # e.g. {'h1': 'device:bmv2:11'} + hostLocations[sw1.name] = '%s/%s' % (sw2.onosDeviceId, port2) + continue + + if isinstance(sw2, Host): + # record host location and ignore it + # e.g. {'h1': 'device:bmv2:11'} + hostLocations[sw2.name] = '%s/%s' % (sw1.onosDeviceId, port1) + continue + + linkId = '%s/%s-%s/%s' % (sw1.onosDeviceId, port1, sw2.onosDeviceId, port2) + netcfg['links'][linkId] = { + 'basic': { + 'type': 'DIRECT', + 'bandwidth': 50 + } + } + + # Host configs + longitude = BASE_LONGITUDE + for host in net.hosts: + longitude = longitude + BASE_SHIFT + hostDefaultIntf = host.defaultIntf() + hostMac = host.MAC(hostDefaultIntf) + hostIp = host.IP(hostDefaultIntf) + hostId = '%s/%d' % (hostMac, VLAN_NONE) + location = hostLocations[host.name] + + # use host Id to generate host location + hostConfig = { + 'basic': { + 'locations': [location], + 'ips': [hostIp], + 'name': host.name, + 'latitude': HOST_BASE_LATITUDE, + 'longitude': longitude + } + } + netcfg['hosts'][hostId] = hostConfig + + print "Writing network config to %s" % TEMP_NETCFG_FILE + with open(TEMP_NETCFG_FILE, 'w') as tempFile: + json.dump(netcfg, tempFile) def main(args): - topo = ClosTopo() - + setLogLevel('debug') if not args.onos_ip: controller = ONOSCluster('c0', 3) onosIp = controller.nodes()[0].IP() else: - controller = RemoteController('c0', ip=args.onos_ip, port=args.onos_port) + controller = RemoteController('c0', ip=args.onos_ip) onosIp = args.onos_ip + topo = ClosTopo(pipeconfId=args.pipeconf_id) + net = Mininet(topo=topo, build=False, controller=[controller]) net.build() @@ -165,9 +244,10 @@ def main(args): # print "Starting traffic from h1 to h3..." # net.hosts[0].startIperfClient(net.hosts[-1], flowBw="200k", numFlows=100, duration=10) - print "Setting netcfg..." - call(("%s/onos-netcfg" % RUN_PACK_PATH, onosIp, - "%s/tools/test/topos/bmv2-demo-cfg.json" % ONOS_ROOT)) + generateNetcfg(onosIp, net) + + print "Uploading netcfg..." + call(("%s/onos-netcfg" % RUN_PACK_PATH, onosIp, TEMP_NETCFG_FILE)) if not args.onos_ip: ONOSCLI(net) @@ -175,6 +255,7 @@ def main(args): CLI(net) net.stop() + call(("rm", "-f", TEMP_NETCFG_FILE)) if __name__ == '__main__': @@ -182,8 +263,8 @@ if __name__ == '__main__': description='BMv2 mininet demo script (2-stage Clos topology)') parser.add_argument('--onos-ip', help='ONOS-BMv2 controller IP address', type=str, action="store", required=False) - parser.add_argument('--onos-port', help='ONOS-BMv2 controller port', - type=int, action="store", default=40123) + parser.add_argument('--pipeconf-id', help='Pipeconf ID for switches', + type=str, action="store", required=False, default='') args = parser.parse_args() setLogLevel('info') main(args)