[ONOS-6854] refactor bmv2-demo.py

Change-Id: I9b7460b15f6664363f2ff0b16110e2b3bc4dedeb
diff --git a/tools/test/topos/bmv2-demo.py b/tools/test/topos/bmv2-demo.py
old mode 100644
new mode 100755
index 646c7353..a2aeaa6
--- 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.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 @@
     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 @@
     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 @@
     # 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 @@
         CLI(net)
 
     net.stop()
+    call(("rm", "-f", TEMP_NETCFG_FILE))
 
 
 if __name__ == '__main__':
@@ -182,8 +263,8 @@
         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)