Run an ONOS cluster inside Mininet (via onos.py) with any demo script.

E.g. $ sudo -E python attmpls.py --cluster-size 3

Also, added option (--netcfg) to auto set netcfg at Mininet startup, and
added command to bring onos instances up/down to Mininet CLI.

Change-Id: Id02917fd5181af496b7d954da0ef3d5f2cbb970d
diff --git a/tools/dev/mininet/onos.py b/tools/dev/mininet/onos.py
index 051b3b3..98b63c8 100755
--- a/tools/dev/mininet/onos.py
+++ b/tools/dev/mininet/onos.py
@@ -270,6 +270,21 @@
 
     # pylint: enable=arguments-differ
 
+    def intfsDown( self ):
+        """Bring all interfaces down"""
+        for intf in self.intfs.values():
+            cmdOutput = intf.ifconfig( 'down' )
+            # no output indicates success
+            if cmdOutput:
+                error( "Error setting %s down: %s " % ( intf.name, cmdOutput ) )
+
+    def intfsUp( self ):
+        """Bring all interfaces up"""
+        for intf in self.intfs.values():
+            cmdOutput = intf.ifconfig( 'up' )
+            if cmdOutput:
+                error( "Error setting %s up: %s " % ( intf.name, cmdOutput ) )
+
     def stop( self ):
         # XXX This will kill all karafs - too bad!
         self.cmd( 'pkill -HUP -f karaf.jar && wait' )
@@ -617,6 +632,31 @@
         elapsed = time.time() - startTime
         debug( 'Completed in %.2f seconds\n' % elapsed )
 
+    def onosupdown( self, cmd, instance ):
+        if not instance:
+            info( 'Provide the name of an ONOS instance.\n' )
+            return
+        c0 = self.mn.controllers[ 0 ]
+        if isONOSCluster( c0 ):
+            try:
+                onos = self.mn.controllers[ 0 ].net.getNodeByName( instance )
+                if isONOSNode( onos ):
+                    info('Bringing %s %s...\n' % ( instance, cmd ) )
+                    if cmd == 'up':
+                        onos.intfsUp()
+                    else:
+                        onos.intfsDown()
+            except KeyError:
+                info( 'No such ONOS instance %s.\n' % instance )
+
+    def do_onosdown( self, instance=None ):
+        """Disconnects an ONOS instance from the network"""
+        self.onosupdown( 'down', instance )
+
+    def do_onosup( self, instance=None ):
+        """"Connects an ONOS instance to the network"""
+        self.onosupdown( 'up', instance )
+
 
 # For interactive use, exit on error
 exitOnError = dict( nodeOpts={ 'alertAction': 'exit' } )
diff --git a/tools/test/topos/onosnet.py b/tools/test/topos/onosnet.py
index 1738cf7..a1550fc 100644
--- a/tools/test/topos/onosnet.py
+++ b/tools/test/topos/onosnet.py
@@ -1,20 +1,22 @@
 #!/usr/bin/python
-
-import sys
 import itertools
+import os
 import signal
-from time import sleep
+import sys
+from argparse import ArgumentParser
+from subprocess import call
 from threading import Thread
+from time import sleep
 
-from mininet.net import Mininet
-from mininet.log import setLogLevel
-from mininet.node import RemoteController, Node
-from mininet.log import info, debug, output, error
-from mininet.link import TCLink
-from mininet.cli import CLI
-
-# This is the program that each host will call
 import gratuitousArp
+from mininet.cli import CLI
+from mininet.examples.controlnet import MininetFacade
+from mininet.link import TCLink
+from mininet.log import info, output, error
+from mininet.log import setLogLevel
+from mininet.net import Mininet
+from mininet.node import RemoteController, Node
+
 ARP_PATH = gratuitousArp.__file__.replace('.pyc', '.py')
 
 class ONOSMininet( Mininet ):
@@ -23,9 +25,6 @@
         """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
@@ -34,12 +33,14 @@
 
         self.gratArp = gratuitousArp
 
-        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 a controller is not provided, use list of remote controller IPs instead.
+        if 'controller' not in kwargs or not kwargs['controller']:
+            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()
@@ -153,6 +154,18 @@
 class BackgroundException( Exception ):
     pass
 
+
+def get_mn(mn):
+    if isinstance(mn, ONOSMininet):
+        return mn
+    elif isinstance(mn, MininetFacade):
+        # There's more Mininet objects instantiated (e.g. one for the control network in onos.py).
+        for net in mn.nets:
+            if isinstance(net, ONOSMininet):
+                return net
+    return None
+
+
 def do_bgIperf( self, line ):
     args = line.split()
     if not args:
@@ -178,29 +191,70 @@
             error( "node '%s' not in network\n" % arg )
         else:
             hosts.append( self.mn[ arg ] )
-    if "bgIperf" in dir(self.mn) and not err:
-        self.mn.bgIperf( hosts, seconds=seconds )
+    mn = get_mn( self.mn )
+    if "bgIperf" in dir( mn ) and not err:
+        mn.bgIperf( hosts, seconds=seconds )
+    else:
+        output('Background Iperf is not supported.\n')
 
 def do_gratuitousArp( self, line ):
     args = line.split()
-    if "gratuitousArp" in dir( self.mn ):
-        self.mn.gratuitousArp( args )
+    mn = get_mn(self.mn)
+    if "gratuitousArp" in dir( mn ):
+        mn.gratuitousArp( args )
     else:
         output( 'Gratuitous ARP is not supported.\n' )
 
 CLI.do_bgIperf = do_bgIperf
 CLI.do_gratuitousArp = do_gratuitousArp
 
-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'
+def run( topo, controllers=None, link=TCLink, autoSetMacs=True):
+    if not topo:
+        print 'Need to provide a topology'
+        exit(1)
+
+    parser = ArgumentParser(description='ONOS Mininet ' + type(topo).__name__)
+    parser.add_argument('--cluster-size', help='Starts an ONOS cluster with the given number of instances',
+                        type=int, action='store', dest='clusterSize', required=False, default=0)
+    parser.add_argument('--netcfg', help='Relative path of the JSON file to be used with netcfg',
+                        type=str, action='store', dest='netcfgJson', required=False, default='')
+    parser.add_argument('ipAddrs', metavar='IP', type=str, nargs='*',
+                        help='List of controller IP addresses', default=[])
+    args = parser.parse_args()
+
+    if not controllers and len(args.ipAddrs) > 0:
+        controllers = args.ipAddrs
+
+    if not controllers and args.clusterSize < 1:
+        print 'Need to provide a list of controller IPs, or define a cluster size.'
         exit( 1 )
 
     setLogLevel( 'info' )
 
-    net = ONOSMininet( topo=topo, controllers=controllers, link=link, autoSetMacs=autoSetMacs )
+    if args.clusterSize > 0:
+        if 'ONOS_ROOT' not in os.environ:
+            print "Environment var $ONOS_ROOT not set (needed to import onos.py)"
+            exit( 1 )
+        sys.path.append(os.environ["ONOS_ROOT"] + "/tools/dev/mininet")
+        from onos import ONOSCluster, ONOSOVSSwitch, ONOSCLI
+        controller = ONOSCluster('c0', args.clusterSize)
+        onosAddr = controller.nodes()[0].IP()
+        net = ONOSMininet( topo=topo, controller=controller, switch=ONOSOVSSwitch, link=link,
+                           autoSetMacs=autoSetMacs )
+        cli = ONOSCLI
+    else:
+        onosAddr = controllers[0]
+        net = ONOSMininet(topo=topo, controllers=controllers, link=link, autoSetMacs=autoSetMacs)
+        cli = CLI
+
     net.start()
-    CLI( net )
+
+    if len(args.netcfgJson) > 0:
+        if not os.path.isfile(args.netcfgJson):
+            error('*** WARNING no such netcfg file: %s\n' % args.netcfgJson)
+        else:
+            info('*** Setting netcfg: %s\n' % args.netcfgJson)
+            call(("onos-netcfg", onosAddr, args.netcfgJson))
+
+    cli( net )
     net.stop()