Minor refactoring of BMv2 mininet scripts

With options to delay pushing the netcfg for each device and generating
the full netcfg JSON for bmv2-demo.

Change-Id: I046a93a8c639f4bb4cf76cbd61b826473760bfb1
diff --git a/tools/dev/mininet/bmv2.py b/tools/dev/mininet/bmv2.py
index f218257..7066f61 100644
--- a/tools/dev/mininet/bmv2.py
+++ b/tools/dev/mininet/bmv2.py
@@ -35,7 +35,8 @@
     def config(self, **params):
         r = super(Host, self).config(**params)
         for off in ["rx", "tx", "sg"]:
-            cmd = "/sbin/ethtool --offload %s %s off" % (self.defaultIntf(), off)
+            cmd = "/sbin/ethtool --offload %s %s off"\
+                  % (self.defaultIntf(), off)
             self.cmd(cmd)
         # disable IPv6
         self.cmd("sysctl -w net.ipv6.conf.all.disable_ipv6=1")
@@ -52,7 +53,8 @@
 
     def __init__(self, name, json=None, debugger=False, loglevel="warn", elogger=False,
                  persistent=False, grpcPort=None, thriftPort=None, netcfg=True, dryrun=False,
-                 pipeconfId="", pktdump=False, valgrind=False, **kwargs):
+                 pipeconfId="", pktdump=False, valgrind=False, netcfgDelay=0,
+                 **kwargs):
         Switch.__init__(self, name, **kwargs)
         self.grpcPort = ONOSBmv2Switch.pickUnusedPort() if not grpcPort else grpcPort
         self.thriftPort = ONOSBmv2Switch.pickUnusedPort() if not thriftPort else thriftPort
@@ -71,6 +73,7 @@
         self.netcfg = parseBoolean(netcfg)
         self.dryrun = parseBoolean(dryrun)
         self.valgrind = parseBoolean(valgrind)
+        self.netcfgDelay = netcfgDelay
         self.netcfgfile = '/tmp/bmv2-%d-netcfg.json' % self.deviceId
         self.pipeconfId = pipeconfId
         if persistent:
@@ -229,11 +232,13 @@
             out = self.cmd(cmdStr)
             if out:
                 print out
-            if self.netcfg and self.valgrind:
-                # With valgrind, it takes some time before the gRPC server is available.
-                # Wait before pushing the netcfg.
-                info("\n*** Waiting %d seconds before pushing the config to ONOS...\n" % VALGRIND_SLEEP)
-                time.sleep(VALGRIND_SLEEP)
+            if self.netcfg:
+                if self.valgrind:
+                    # With valgrind, it takes some time before the gRPC server is available.
+                    # Wait before pushing the netcfg.
+                    info("\n*** Waiting %d seconds before pushing the config to ONOS...\n" % VALGRIND_SLEEP)
+                    time.sleep(VALGRIND_SLEEP)
+                time.sleep(self.netcfgDelay)
 
         try:  # onos.py
             clist = controllers[0].nodes()
diff --git a/tools/test/topos/bmv2-demo.py b/tools/test/topos/bmv2-demo.py
index 9bccc5c..ef0a124 100755
--- a/tools/test/topos/bmv2-demo.py
+++ b/tools/test/topos/bmv2-demo.py
@@ -42,8 +42,12 @@
 from mininet.topo import Topo
 
 
+def getCmdBg(cmd, logfile="/dev/null"):
+    return "{} > {} 2>&1 &".format(cmd, logfile)
+
+
 class ClosTopo(Topo):
-    "2 stage Clos topology"
+    """2 stage Clos topology"""
 
     def __init__(self, args, **opts):
         # Initialize topology and default options
@@ -58,16 +62,17 @@
 
         for switchId in bmv2SwitchIds:
             deviceId = int(switchId[1:])
-            # Use first number in device id to calculate latitude (row number)
+            # Use first number in device id to calculate latitude (row number),
+            # use second to calculate longitude (column 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",
+                                                    loglevel=args.log_level,
                                                     deviceId=deviceId,
-                                                    netcfg=False,
+                                                    netcfg=True,
+                                                    netcfgDelay=0.5,
                                                     longitude=longitude,
                                                     latitude=latitude,
                                                     pipeconfId=args.pipeconf_id)
@@ -75,14 +80,17 @@
         for i in range(1, args.size + 1):
             for j in range(1, args.size + 1):
                 if i == j:
-                    # 2 links
-                    self.addLink(bmv2Switches["s1%d" % i], bmv2Switches["s2%d" % j],
+                    self.addLink(bmv2Switches["s1%d" % i],
+                                 bmv2Switches["s2%d" % j],
                                  cls=TCLink, bw=DEFAULT_SW_BW)
                     if args.with_imbalanced_striping:
-                        self.addLink(bmv2Switches["s1%d" % i], bmv2Switches["s2%d" % j],
+                        # 2 links
+                        self.addLink(bmv2Switches["s1%d" % i],
+                                     bmv2Switches["s2%d" % j],
                                      cls=TCLink, bw=DEFAULT_SW_BW)
                 else:
-                    self.addLink(bmv2Switches["s1%d" % i], bmv2Switches["s2%d" % j],
+                    self.addLink(bmv2Switches["s1%d" % i],
+                                 bmv2Switches["s2%d" % j],
                                  cls=TCLink, bw=DEFAULT_SW_BW)
 
         for hostId in range(1, args.size + 1):
@@ -90,11 +98,12 @@
                                 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=DEFAULT_HOST_BW)
+            self.addLink(host, bmv2Switches["s1%d" % hostId],
+                         cls=TCLink, bw=DEFAULT_HOST_BW)
 
 
 class DemoHost(ONOSHost):
-    "Demo host"
+    """Demo host"""
 
     def __init__(self, name, **params):
         ONOSHost.__init__(self, name, **params)
@@ -109,10 +118,11 @@
         self.cmd(self.getInfiniteCmdBg("iperf -s -u"))
 
     def startIperfClient(self, h, flowBw="512k", numFlows=5, duration=5):
-        iperfCmd = "iperf -c{} -u -b{} -P{} -t{}".format(h.IP(), flowBw, numFlows, duration)
-        self.cmd(self.getInfiniteCmdBg(iperfCmd, sleep=0))
+        iperfCmd = "iperf -c{} -u -b{} -P{} -t{}".format(
+            h.IP(), flowBw, numFlows, duration)
+        self.cmd(self.getInfiniteCmdBg(iperfCmd, delay=0))
 
-    def stop(self):
+    def stop(self, **kwargs):
         self.cmd("killall iperf")
         self.cmd("killall ping")
         self.cmd("killall arping")
@@ -127,28 +137,27 @@
         )
         print "**********"
 
-    def getInfiniteCmdBg(self, cmd, logfile="/dev/null", sleep=1):
+    def getInfiniteCmdBg(self, cmd, logfile="/dev/null", delay=1):
         return "(while [ -e {} ]; " \
                "do {}; " \
                "sleep {}; " \
-               "done;) > {} 2>&1 &".format(self.exectoken, cmd, sleep, logfile)
-
-    def getCmdBg(self, cmd, logfile="/dev/null"):
-        return "{} > {} 2>&1 &".format(cmd, logfile)
+               "done;) > {} 2>&1 &".format(self.exectoken, cmd, delay, logfile)
 
 
 def generateNetcfg(onosIp, net, args):
     netcfg = OrderedDict()
+
+    netcfg['hosts'] = {}
     netcfg['devices'] = {}
     netcfg['links'] = {}
-    netcfg['hosts'] = {}
-    # Device configs
-    for sw in net.switches:
-        srcIp = sw.getSourceIp(onosIp)
-        netcfg['devices'][sw.onosDeviceId] = sw.getDeviceConfig(srcIp)
+
+    if args.full_netcfg:
+        # 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
@@ -172,14 +181,18 @@
             hostLocations[sw2.name] = '%s/%s' % (sw1.onosDeviceId, port1)
             continue
 
-        for linkId in ('%s/%s-%s/%s' % (sw1.onosDeviceId, port1, sw2.onosDeviceId, port2),
-                       '%s/%s-%s/%s' % (sw2.onosDeviceId, port2, sw1.onosDeviceId, port1)):
-            netcfg['links'][linkId] = {
-                'basic': {
-                    'type': 'DIRECT',
-                    'bandwidth': DEFAULT_SW_BW
+        if args.full_netcfg:
+            # Link configs
+            for linkId in ('%s/%s-%s/%s' % (sw1.onosDeviceId, port1,
+                                            sw2.onosDeviceId, port2),
+                           '%s/%s-%s/%s' % (sw2.onosDeviceId, port2,
+                                            sw1.onosDeviceId, port1)):
+                netcfg['links'][linkId] = {
+                    'basic': {
+                        'type': 'DIRECT',
+                        'bandwidth': DEFAULT_SW_BW
+                    }
                 }
-            }
 
     # Host configs
     longitude = BASE_LONGITUDE
@@ -203,13 +216,14 @@
         }
         netcfg['hosts'][hostId] = hostConfig
 
-    netcfg["apps"] = {
-        "org.onosproject.core": {
-            "core": {
-                "linkDiscoveryMode": "STRICT"
+    if args.full_netcfg:
+        netcfg["apps"] = {
+            "org.onosproject.core": {
+                "core": {
+                    "linkDiscoveryMode": "STRICT"
+                }
             }
         }
-    }
 
     print "Writing network config to %s" % TEMP_NETCFG_FILE
     with open(TEMP_NETCFG_FILE, 'w') as tempFile:
@@ -233,7 +247,7 @@
 
     print "Network started"
 
-    # Generate background traffic.
+    # Always generate background pings.
     sleep(3)
     for (h1, h2) in combinations(net.hosts, 2):
         h1.startPingBg(h2)
@@ -246,14 +260,17 @@
 
     print "Iperf servers started"
 
-    # sleep(4)
-    # print "Starting traffic from h1 to h3..."
-    # net.hosts[0].startIperfClient(net.hosts[-1], flowBw="200k", numFlows=100, duration=10)
+    if args.bg_traffic:
+        sleep(4)
+        print "Starting iperf clients..."
+        net.hosts[0].startIperfClient(net.hosts[-1], flowBw="400k",
+                                      numFlows=50, duration=10)
 
     generateNetcfg(onosIp, net, args)
 
     if args.netcfg_sleep > 0:
-        print "Waiting %d seconds before pushing config to ONOS..." % args.netcfg_sleep
+        print "Waiting %d seconds before pushing config to ONOS..." \
+              % args.netcfg_sleep
         sleep(args.netcfg_sleep)
 
     print "Pushing config to ONOS..."
@@ -275,12 +292,25 @@
                         type=str, action="store", required=False)
     parser.add_argument('--size', help='Number of leaf/spine switches',
                         type=int, action="store", required=False, default=2)
-    parser.add_argument('--with-imbalanced-striping', help='Topology with imbalanced striping',
-                        type=bool, action="store", required=False, default=False)
+    parser.add_argument('--with-imbalanced-striping',
+                        help='Topology with imbalanced striping',
+                        type=bool, action="store", required=False,
+                        default=False)
     parser.add_argument('--pipeconf-id', help='Pipeconf ID for switches',
                         type=str, action="store", required=False, default='')
-    parser.add_argument('--netcfg-sleep', help='Seconds to wait before pushing config to ONOS',
+    parser.add_argument('--netcfg-sleep',
+                        help='Seconds to wait before pushing config to ONOS',
                         type=int, action="store", required=False, default=5)
-    args = parser.parse_args()
+    parser.add_argument('--log-level', help='BMv2 log level',
+                        type=str, action="store", required=False,
+                        default='warn')
+    parser.add_argument('--full-netcfg',
+                        help='Generate full netcfg JSON with links and devices',
+                        type=bool, action="store", required=False,
+                        default=False)
+    parser.add_argument('--bg-traffic',
+                        help='Starts background traffic',
+                        type=bool, action="store", required=False,
+                        default=False)
     setLogLevel('info')
-    main(args)
+    main(parser.parse_args())