Added option to run Bmv2 with valgrind in bmv2.py

Helpful to debug Bmv2 memory leaks.

Change-Id: I404914201c00203ab3050c439fd7af8a608774d2
diff --git a/tools/dev/mininet/bmv2.py b/tools/dev/mininet/bmv2.py
index 5a4de21..e942646 100644
--- a/tools/dev/mininet/bmv2.py
+++ b/tools/dev/mininet/bmv2.py
@@ -4,6 +4,8 @@
 import json
 import urllib2
 
+import time
+
 from mininet.log import info, warn, error
 from mininet.node import Switch, Host
 
@@ -15,6 +17,15 @@
 ONOS_ROOT = os.environ["ONOS_ROOT"]
 CPU_PORT = 255
 PKT_BYTES_TO_DUMP = 80
+VALGRIND_PREFIX = 'valgrind --leak-check=yes'
+VALGRIND_SLEEP = 10  # seconds
+
+
+def parseBoolean(value):
+    if value in ['1', 1, 'true', 'True']:
+        return True
+    else:
+        return False
 
 
 class ONOSHost(Host):
@@ -40,8 +51,8 @@
     instanceCount = 0
 
     def __init__(self, name, json=None, debugger=False, loglevel="warn", elogger=False,
-                 persistent=False, grpcPort=None, thriftPort=None, netcfg=True,
-                 pipeconfId="", pktdump=False, **kwargs):
+                 persistent=False, grpcPort=None, thriftPort=None, netcfg=True, dryrun=False,
+                 pipeconfId="", pktdump=False, valgrind=False, **kwargs):
         Switch.__init__(self, name, **kwargs)
         self.grpcPort = ONOSBmv2Switch.pickUnusedPort() if not grpcPort else grpcPort
         self.thriftPort = ONOSBmv2Switch.pickUnusedPort() if not thriftPort else thriftPort
@@ -51,13 +62,15 @@
             self.deviceId = ONOSBmv2Switch.deviceId
             ONOSBmv2Switch.deviceId += 1
         self.json = json
-        self.debugger = debugger
+        self.debugger = parseBoolean(debugger)
         self.loglevel = loglevel
         self.logfile = '/tmp/bmv2-%d.log' % self.deviceId
-        self.elogger = elogger
-        self.pktdump = pktdump
-        self.persistent = persistent
-        self.netcfg = netcfg
+        self.elogger = parseBoolean(elogger)
+        self.pktdump = parseBoolean(pktdump)
+        self.persistent = parseBoolean(persistent)
+        self.netcfg = parseBoolean(netcfg)
+        self.dryrun = parseBoolean(dryrun)
+        self.valgrind = parseBoolean(valgrind)
         self.netcfgfile = '/tmp/bmv2-%d-netcfg.json' % self.deviceId
         self.pipeconfId = pipeconfId
         if persistent:
@@ -153,6 +166,11 @@
         }
         with open(self.netcfgfile, 'w') as fp:
             json.dump(cfgData, fp, indent=4)
+
+        if not self.netcfg:
+            # Do not push config to ONOS.
+            return
+
         # Build netcfg URL
         url = 'http://%s:8181/onos/v1/network/configuration/' % controllerIP
         # Instantiate password manager for HTTP auth
@@ -194,7 +212,11 @@
         args.append('--grpc-server-addr 0.0.0.0:%d' % self.grpcPort)
 
         bmv2cmd = " ".join(args)
-        info("\nStarting BMv2 target: %s\n" % bmv2cmd)
+        if self.valgrind:
+            bmv2cmd = "%s %s" % (VALGRIND_PREFIX, bmv2cmd)
+        if self.dryrun:
+            info("\n*** DRY RUN (not executing bmv2)")
+        info("\nStarting BMv2 target: %s" % bmv2cmd)
 
         if self.persistent:
             # Bash loop to re-exec the switch if it crashes.
@@ -203,18 +225,23 @@
         cmdStr = "{} > {} 2>&1 &".format(bmv2cmd, self.logfile)
 
         # Starts the switch.
-        out = self.cmd(cmdStr)
-        if out:
-            print out
+        if not self.dryrun:
+            out = self.cmd(cmdStr)
+            if out:
+                print out
+            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)
 
-        if self.netcfg:
-            try:  # onos.py
-                clist = controllers[0].nodes()
-            except AttributeError:
-                clist = controllers
-            assert len(clist) > 0
-            cip = clist[0].IP()
-            self.doOnosNetcfg(cip)
+        try:  # onos.py
+            clist = controllers[0].nodes()
+        except AttributeError:
+            clist = controllers
+        assert len(clist) > 0
+        cip = clist[0].IP()
+        self.doOnosNetcfg(cip)
 
     def stop(self, deleteIntfs=True):
         """Terminate switch."""