Enhancing ONOS mininet topology approach to remove boilerplate and simplify testing and demos.

Change-Id: Id8ab2bfa74254560714f4c27a9342693d1dc9788
diff --git a/tools/test/scenarios/net-setup.xml b/tools/test/scenarios/net-setup.xml
index 702480e..6599d39 100644
--- a/tools/test/scenarios/net-setup.xml
+++ b/tools/test/scenarios/net-setup.xml
@@ -34,13 +34,7 @@
         <step name="Wait-For-Mininet" requires="Start-Mininet"
               exec="onos-mininet wait 10"/>
 
-        <step name="Show-Network" requires="Wait-For-Mininet"
-              exec="onos-mininet sendAndExpect net --expect ."/>
-
-        <step name="Discover-Hosts" requires="Show-Network"
-              exec="onos-mininet sendAndExpect py [ h.cmd('arping -U -c 1 ' + h.IP()) for h in net.hosts ] --expect ."/>
-
-        <step name="Check-Summary" requires="Discover-Hosts"
+        <step name="Check-Summary" requires="~Wait-For-Mininet"
               exec="onos-check-summary ${OC1} [0-9]* 25 140 25"/>
 
         <step name="Balance-Masters" requires="~Check-Summary" if="${OC2}"
diff --git a/tools/test/topos/att-onos.py b/tools/test/topos/att-onos.py
index 109d335..bb75689 100644
--- a/tools/test/topos/att-onos.py
+++ b/tools/test/topos/att-onos.py
@@ -1,41 +1,6 @@
 #!/usr/bin/python
 
-import sys
-
-from mininet.net import Mininet
-from mininet.cli import CLI
-from mininet.log import setLogLevel
-from mininet.node import RemoteController
-from mininet.link import TCLink
-
+from onosnet import run
 from attmpls import AttMplsTopo
 
-setLogLevel( 'info' )
-
-def pingloop( net ):
-    setLogLevel( 'error' )
-    try:
-        while True:
-            net.ping()
-    finally:
-        setLogLevel( 'info' )
-
-def run(controllers=[ '127.0.0.1' ]):
-    Mininet.pingloop = pingloop
-    net = Mininet( topo=AttMplsTopo(), link=TCLink, build=False, autoSetMacs=True )
-    ctrl_count = 0
-    for controllerIP in controllers:
-        net.addController( 'c%d' % ctrl_count, RemoteController, ip=controllerIP )
-	ctrl_count = ctrl_count + 1
-    net.build()
-    net.start()
-    CLI( net )
-    net.stop()
-
-if __name__ == '__main__':
-    if len( sys.argv ) > 1:
-        controllers = sys.argv[ 1: ]
-    else:
-        print 'Usage: att-onos.py <c0 IP> <c1 IP> ...'
-        exit( 1 )
-    run( controllers )
+run( AttMplsTopo() )
diff --git a/tools/test/topos/attmpls.py b/tools/test/topos/attmpls.py
index 4fe7115..fedcb6c 100644
--- a/tools/test/topos/attmpls.py
+++ b/tools/test/topos/attmpls.py
@@ -175,3 +175,7 @@
         self.addLink( SNDG , PHNX, bw=10, delay='0.345064487693ms')
 
 topos = { 'att': ( lambda: AttMplsTopo() ) }
+
+if __name__ == '__main__':
+    from onosnet import run
+    run( AttMplsTopo() )
diff --git a/tools/test/topos/onosnet.py b/tools/test/topos/onosnet.py
new file mode 100644
index 0000000..cbf034b
--- /dev/null
+++ b/tools/test/topos/onosnet.py
@@ -0,0 +1,103 @@
+#!/usr/bin/python
+
+import sys
+from threading import Thread
+
+from mininet.net import Mininet
+from mininet.log import setLogLevel
+from mininet.node import RemoteController
+from mininet.log import info, debug
+from mininet.util import quietRun
+from mininet.link import TCLink
+from mininet.cli import CLI
+
+class ONOSMininet( Mininet ):
+
+    @classmethod
+    def setup( cls ):
+        cls.useArping = True if quietRun( 'which arping' ) else False
+
+    def __init__( self, controllers=[], gratuitousArp=True, build=True, *args, **kwargs ):
+        """Create Mininet object for ONOS.
+        controllers: List of controller IP addresses
+        gratuitousArp: Send an ARP from each host to aid controller's host discovery"""
+        # discarding provided controller (if any),
+        # using list of remote controller IPs instead
+        kwargs[ 'controller' ] = None
+
+        # delay building for a second
+        kwargs[ 'build' ] = False
+
+        Mininet.__init__(self, *args, **kwargs )
+
+        self.gratArp = gratuitousArp
+        self.useArping = ONOSMininet.useArping
+
+        info ( '*** Adding controllers\n' )
+        ctrl_count = 0
+        for controllerIP in controllers:
+            self.addController( 'c%d' % ctrl_count, RemoteController, ip=controllerIP )
+            info( '   c%d (%s)\n' % ( ctrl_count, controllerIP ) )
+            ctrl_count = ctrl_count + 1
+
+        if self.topo and build:
+            self.build()
+
+    def start( self ):
+        Mininet.start( self )
+        if self.gratArp:
+            self.waitConnected()
+            info ( '*** Sending a gratuitious ARP from each host\n' )
+            self.gratuitousArp()
+
+
+    def gratuitousArp( self ):
+        "Send an ARP from each host to aid controller's host discovery; fallback to ping if necessary"
+        if self.useArping:
+            for host in self.hosts:
+                info( '%s ' % host.name )
+                debug( host.cmd( 'arping -U -c 1 ' + host.IP() ) )
+            info ( '\n' )
+        else:
+            info( '\nWARNING: arping is not found, using ping instead.\n'
+                  'For higher performance, install arping: sudo apt-get install iputils-arping\n\n' )
+
+            threads = [ self.threadPing(s, d) for (s, d) in zip( self.hosts, self.hosts[1:] + self.hosts[0:1] ) ]
+            for t in threads:
+                t.join()
+            info ( '\n' )
+
+    def threadPing( self, src, dst ):
+        "Ping from src to dst in a thread"
+        def p():
+            src.cmd( 'ping -w 0.1 -W 0.1 -c1 ' + dst.IP() )
+        t = Thread( target=p )
+        info ( '%s ' % src.name )
+        t.start()
+        return t
+
+    def pingloop( self ):
+        "Loop forever pinging the full mesh of hosts"
+        setLogLevel( 'error' )
+        try:
+            while True:
+                self.ping()
+        finally:
+            setLogLevel( 'info' )
+
+# Initialize ONOSMininet the first time that the class is loaded
+ONOSMininet.setup()
+
+def run( topo, controllers=None, link=TCLink, autoSetMacs=True ):
+    if not controllers and len( sys.argv ) > 1:
+        controllers = sys.argv[ 1: ]
+    else:
+        print 'Need to provide a topology and list of controllers'
+        exit( 1 )
+
+    setLogLevel( 'info' )
+
+    net = ONOSMininet( topo=topo, controllers=controllers, link=link, autoSetMacs=autoSetMacs )
+    net.start()
+    CLI( net )
+    net.stop()
diff --git a/tools/test/topos/uk.py b/tools/test/topos/uk.py
index 1725d37..2ed1765 100644
--- a/tools/test/topos/uk.py
+++ b/tools/test/topos/uk.py
@@ -81,3 +81,7 @@
         self.addLink( YORK,   NRWICH, bw=10, delay='1.0ms')
 
 topos = { 'uk': ( lambda: UkTopo() ) }
+
+if __name__ == '__main__':
+    from onosnet import run
+    run( UkTopo() )