Merge "Add Network Configuration subsytem test"
diff --git a/TestON/drivers/common/api/controller/onosrestdriver.py b/TestON/drivers/common/api/controller/onosrestdriver.py
index aae0d56..3ed6ab3 100644
--- a/TestON/drivers/common/api/controller/onosrestdriver.py
+++ b/TestON/drivers/common/api/controller/onosrestdriver.py
@@ -1338,3 +1338,145 @@
             main.log.exception( self.name + ": Uncaught exception!" )
             main.cleanup()
             main.exit()
+
+    def getNetCfg( self, ip="DEFAULT", port="DEFAULT",
+                   subjectClass=None, subjectKey=None, configKey=None ):
+        """
+        Description:
+            Get a json object with the ONOS network configurations
+        Returns:
+            A json object containing the network configuration in
+            ONOS; Returns main.FALSE if error on requests;
+            Returns None for exception
+        """
+        try:
+            output = None
+            if ip == "DEFAULT":
+                main.log.warn( "No ip given, reverting to ip from topo file" )
+                ip = self.ip_address
+            if port == "DEFAULT":
+                main.log.warn( "No port given, reverting to port " +
+                               "from topo file" )
+                port = self.port
+            url = "/network/configuration"
+            if subjectClass:
+                url += "/" + subjectClass
+                if subjectKey:
+                    url += "/" + subjectKey
+                    if configKey:
+                        url += "/" + configKey
+            response = self.send( ip, port, url=url )
+            if response:
+                if 200 <= response[ 0 ] <= 299:
+                    output = response[ 1 ]
+                    a = json.loads( output )
+                    b = json.dumps( a )
+                    return b
+                elif response[ 0 ] == 404:
+                    main.log.error( "Requested configuration doesn't exist: " +
+                                    str( response ) )
+                    return {}
+                else:
+                    main.log.error( "Error with REST request, response was: " +
+                                    str( response ) )
+                    return main.FALSE
+        except ( AttributeError, TypeError ):
+            main.log.exception( self.name + ": Object not as expected" )
+            return None
+        except Exception:
+            main.log.exception( self.name + ": Uncaught exception!" )
+            main.cleanup()
+            main.exit()
+
+    def setNetCfg( self, cfgJson, ip="DEFAULT", port="DEFAULT",
+                   subjectClass=None, subjectKey=None, configKey=None ):
+        """
+        Description:
+            Set a json object with the ONOS network configurations
+        Returns:
+            Returns main.TRUE for successful requests; Returns main.FALSE
+            if error on requests;
+            Returns None for exceptions
+
+        """
+        try:
+            output = None
+            if ip == "DEFAULT":
+                main.log.warn( "No ip given, reverting to ip from topo file" )
+                ip = self.ip_address
+            if port == "DEFAULT":
+                main.log.warn( "No port given, reverting to port " +
+                               "from topo file" )
+                port = self.port
+            url = "/network/configuration"
+            if subjectClass:
+                url += "/" + subjectClass
+                if subjectKey:
+                    url += "/" + subjectKey
+                    if configKey:
+                        url += "/" + configKey
+            response = self.send( ip, port,
+                                  method="POST",
+                                  url=url,
+                                  data=json.dumps( cfgJson ) )
+            if response:
+                if 200 <= response[ 0 ] <= 299:
+                    main.log.info( self.name + ": Successfully POST cfg" )
+                    return main.TRUE
+                else:
+                    main.log.error( "Error with REST request, response was: " +
+                                    str( response ) )
+                    return main.FALSE
+        except ( AttributeError, TypeError ):
+            main.log.exception( self.name + ": Object not as expected" )
+            return None
+        except Exception:
+            main.log.exception( self.name + ": Uncaught exception!" )
+            main.cleanup()
+            main.exit()
+
+    def removeNetCfg( self, ip="DEFAULT", port="DEFAULT",
+                      subjectClass=None, subjectKey=None, configKey=None ):
+        """
+        Description:
+            Remove a json object from the ONOS network configurations
+        Returns:
+            Returns main.TRUE for successful requests; Returns main.FALSE
+            if error on requests;
+            Returns None for exceptions
+
+        """
+        try:
+            output = None
+            if ip == "DEFAULT":
+                main.log.warn( "No ip given, reverting to ip from topo file" )
+                ip = self.ip_address
+            if port == "DEFAULT":
+                main.log.warn( "No port given, reverting to port " +
+                               "from topo file" )
+                port = self.port
+            url = "/network/configuration"
+            if subjectClass:
+                url += "/" + subjectClass
+                if subjectKey:
+                    url += "/" + subjectKey
+                    if configKey:
+                        url += "/" + configKey
+            response = self.send( ip, port,
+                                  method="DELETE",
+                                  url=url )
+            if response:
+                if 200 <= response[ 0 ] <= 299:
+                    main.log.info( self.name + ": Successfully delete cfg" )
+                    return main.TRUE
+                else:
+                    main.log.error( "Error with REST request, response was: " +
+                                    str( response ) )
+                    return main.FALSE
+        except ( AttributeError, TypeError ):
+            main.log.exception( self.name + ": Object not as expected" )
+            return None
+        except Exception:
+            main.log.exception( self.name + ": Uncaught exception!" )
+            main.cleanup()
+            main.exit()
diff --git a/TestON/tests/FUNCnetCfg/Dependency/netCfg.py b/TestON/tests/FUNCnetCfg/Dependency/netCfg.py
new file mode 100644
index 0000000..eb2ab73
--- /dev/null
+++ b/TestON/tests/FUNCnetCfg/Dependency/netCfg.py
@@ -0,0 +1,32 @@
+"""
+These functions are for use with the Network config system
+"""
+import time
+
+def compareCfg( main, gossipTime=None ):
+    """
+    Compare the network configurations across all nodes in the network
+    gossipTime is the number of seconds each gossip round take for the netCfg maps
+    """
+    main.step( "Check net config" )
+    if gossipTime:
+        time.sleep( gossipTime * len( main.nodes ) )
+    responses = []
+    failMsg = "Net Cfg is different on some nodes."
+    failed = False
+    for node in main.nodes:
+        response = node.getNetCfg( )
+        responses.append( node.pprint( response ) )
+        if response == main.FALSE:
+            failed = True
+    compare = [ i == responses[0] for i in responses ]
+    if failed:
+        failMsg += " Some nodes failed to GET netCfg."
+    utilities.assert_equals( expect=True,
+                             actual=all( compare ),
+                             onpass="Net Cfg is the same on all nodes",
+                             onfail=failMsg )
+    if not all( compare ):
+        main.log.debug( "Net Config results:" )
+        for i in responses:
+            main.log.debug( i )
diff --git a/TestON/tests/FUNCnetCfg/Dependency/startUp.py b/TestON/tests/FUNCnetCfg/Dependency/startUp.py
new file mode 100644
index 0000000..cfaf589
--- /dev/null
+++ b/TestON/tests/FUNCnetCfg/Dependency/startUp.py
@@ -0,0 +1,32 @@
+"""
+    This wrapper function is use for starting up onos instance
+"""
+
+import time
+
+def onosBuild( main, gitBranch ):
+    """
+        This includes pulling ONOS and building it using maven install
+    """
+
+    buildResult = main.FALSE
+
+    # Git checkout a branch of ONOS
+    checkOutResult = main.ONOSbench.gitCheckout( gitBranch )
+    # Does the git pull on the branch that was checked out
+    if not checkOutResult:
+        main.log.warn( "Failed to checked out " + gitBranch +
+                                           " branch")
+    else:
+        main.log.info( "Successfully checked out " + gitBranch +
+                                           " branch")
+    gitPullResult = main.ONOSbench.gitPull()
+    if gitPullResult == main.ERROR:
+        main.log.error( "Error pulling git branch" )
+    else:
+        main.log.info( "Successfully pulled " + gitBranch + " branch" )
+
+    # Maven clean install
+    buildResult = main.ONOSbench.cleanInstall()
+
+    return buildResult
diff --git a/TestON/tests/FUNCnetCfg/Dependency/topo.py b/TestON/tests/FUNCnetCfg/Dependency/topo.py
new file mode 100644
index 0000000..d834a09
--- /dev/null
+++ b/TestON/tests/FUNCnetCfg/Dependency/topo.py
@@ -0,0 +1,98 @@
+"""
+    These functions can be used for topology comparisons
+"""
+
+import time
+import os
+import json
+
+def getAllDevices( main ):
+    """
+        Return a list containing the devices output from each ONOS node
+    """
+    devices = []
+    threads = []
+    for i in range( main.numCtrls ):
+        t = main.Thread( target=main.CLIs[i].devices,
+                         name="devices-" + str( i ),
+                         args=[ ] )
+        threads.append( t )
+        t.start()
+
+    for t in threads:
+        t.join()
+        devices.append( t.result )
+    return devices
+
+def getAllHosts( main ):
+    """
+        Return a list containing the hosts output from each ONOS node
+    """
+    hosts = []
+    ipResult = main.TRUE
+    threads = []
+    for i in range( main.numCtrls ):
+        t = main.Thread( target=main.CLIs[i].hosts,
+                         name="hosts-" + str( i ),
+                         args=[ ] )
+        threads.append( t )
+        t.start()
+
+    for t in threads:
+        t.join()
+        hosts.append( t.result )
+    return hosts
+
+def getAllPorts( main ):
+    """
+        Return a list containing the ports output from each ONOS node
+    """
+    ports = []
+    threads = []
+    for i in range( main.numCtrls ):
+        t = main.Thread( target=main.CLIs[i].ports,
+                         name="ports-" + str( i ),
+                         args=[ ] )
+        threads.append( t )
+        t.start()
+
+    for t in threads:
+        t.join()
+        ports.append( t.result )
+    return ports
+
+def getAllLinks( main ):
+    """
+        Return a list containing the links output from each ONOS node
+    """
+    links = []
+    threads = []
+    for i in range( main.numCtrls ):
+        t = main.Thread( target=main.CLIs[i].links,
+                         name="links-" + str( i ),
+                         args=[ ] )
+        threads.append( t )
+        t.start()
+
+    for t in threads:
+        t.join()
+        links.append( t.result )
+    return links
+
+def getAllClusters( main ):
+    """
+        Return a list containing the clusters output from each ONOS node
+    """
+    clusters = []
+    threads = []
+    for i in range( main.numCtrls ):
+        t = main.Thread( target=main.CLIs[i].clusters,
+                         name="clusters-" + str( i ),
+                         args=[ ] )
+        threads.append( t )
+        t.start()
+
+    for t in threads:
+        t.join()
+        clusters.append( t.result )
+    return clusters
diff --git a/TestON/tests/FUNCnetCfg/FUNCnetCfg.params b/TestON/tests/FUNCnetCfg/FUNCnetCfg.params
new file mode 100644
index 0000000..4a92477
--- /dev/null
+++ b/TestON/tests/FUNCnetCfg/FUNCnetCfg.params
@@ -0,0 +1,42 @@
+<PARAMS>
+    # CASE - Description
+    # 1 - Variable initialization and optional pull and build ONOS package
+    # 2 - Install ONOS
+    # 9 - Report logs
+    # 10 - Start Mininet with Openflow 1.0
+    # 11 - Start Mininet with Openflow 1.3
+    # 12 - Assign switch to controller
+    # 14 - Stop Mininet
+    # 20 - Add NetCfgs for undiscovered devices
+    # 21 - Check NetCfgs after devices connect
+    # 22 - Add NetCfgs for discovered devices
+    # 23 - Check NetCfgs after all devices are connected and NetCfgs are set
+    # 24 - Remove NetCfgs
+
+    <testcases>1,2,20,11,21,22,23,24</testcases>
+
+    <DEPENDENCY>
+        <path>/tests/FUNCnetCfg/Dependency/</path>
+        <wrapper1>startUp</wrapper1>
+        <wrapper2>netCfg</wrapper2>
+        <wrapper3>topo</wrapper3>
+    </DEPENDENCY>
+
+    <ENV>
+        <cellApps>drivers,openflow,proxyarp,mobility</cellApps>
+    </ENV>
+    <GIT>
+        <pull>False</pull>
+        <branch>master</branch>
+    </GIT>
+
+    <SLEEP>
+        <startup>15</startup>
+        <cfgGossip>2</cfgGossip>
+    </SLEEP>
+
+    <MININET>
+        <switch>4</switch>
+    </MININET>
+
+</PARAMS>
diff --git a/TestON/tests/FUNCnetCfg/FUNCnetCfg.py b/TestON/tests/FUNCnetCfg/FUNCnetCfg.py
new file mode 100644
index 0000000..190357d
--- /dev/null
+++ b/TestON/tests/FUNCnetCfg/FUNCnetCfg.py
@@ -0,0 +1,756 @@
+
+# Testing the basic intent functionality of ONOS
+
+class FUNCnetCfg:
+
+    def __init__( self ):
+        self.default = ''
+
+    def CASE1( self, main ):
+        import imp
+        import re
+
+        """
+        - Construct tests variables
+        - GIT ( optional )
+            - Checkout ONOS master branch
+            - Pull latest ONOS code
+        - Building ONOS ( optional )
+            - Install ONOS package
+            - Build ONOS package
+        """
+
+        main.case( "Constructing test variables and building ONOS package" )
+        main.step( "Constructing test variables" )
+        main.caseExplanation = "This test case is mainly for loading " +\
+                               "from params file, and pull and build the " +\
+                               " latest ONOS package"
+        stepResult = main.FALSE
+
+        # Test variables
+        try:
+            main.testOnDirectory = re.sub( "(/tests)$", "", main.testDir )
+            main.apps = main.params[ 'ENV' ][ 'cellApps' ]
+            gitBranch = main.params[ 'GIT' ][ 'branch' ]
+            main.dependencyPath = main.testOnDirectory + \
+                                  main.params[ 'DEPENDENCY' ][ 'path' ]
+            if main.ONOSbench.maxNodes:
+                main.maxNodes = int( main.ONOSbench.maxNodes )
+            else:
+                main.maxNodes = 0
+            wrapperFile1 = main.params[ 'DEPENDENCY' ][ 'wrapper1' ]
+            wrapperFile2 = main.params[ 'DEPENDENCY' ][ 'wrapper2' ]
+            wrapperFile3 = main.params[ 'DEPENDENCY' ][ 'wrapper3' ]
+            main.startUpSleep = int( main.params[ 'SLEEP' ][ 'startup' ] )
+            main.gossipTime = int( main.params[ 'SLEEP'][ 'cfgGossip' ] )
+            gitPull = main.params[ 'GIT' ][ 'pull' ]
+            main.cellData = {}  # for creating cell file
+            main.hostsData = {}
+            main.nodes = []
+            main.ONOSip = []
+
+            main.ONOSip = main.ONOSbench.getOnosIps()
+
+            # Assigning ONOS cli handles to a list
+            try:
+                for i in range( 1, main.maxNodes + 1 ):
+                    main.nodes.append( getattr( main, 'ONOSrest' + str( i ) ) )
+            except AttributeError:
+                main.log.warn( "A " + str( main.maxNodes ) + " node cluster " +
+                               "was defined in env variables, but only " +
+                               str( len( main.nodes ) ) +
+                               " nodes were defined in the .topo file. " +
+                               "Using " + str( len( main.nodes ) ) +
+                               " nodes for the test." )
+
+            main.numCtrls = len( main.nodes )
+
+            # -- INIT SECTION, SHOULD ONLY BE RUN ONCE -- #
+            main.startUp = imp.load_source( wrapperFile1,
+                                            main.dependencyPath +
+                                            wrapperFile1 +
+                                            ".py" )
+
+            main.netCfg = imp.load_source( wrapperFile2,
+                                           main.dependencyPath +
+                                           wrapperFile2 +
+                                           ".py" )
+
+            main.topo = imp.load_source( wrapperFile3,
+                                         main.dependencyPath +
+                                         wrapperFile3 +
+                                         ".py" )
+
+            if main.nodes:
+                stepResult = main.TRUE
+            else:
+                main.log.error( "Did not properly created list of ONOS handle" )
+                stepResult = main.FALSE
+        except Exception as e:
+            main.log.exception(e)
+            main.cleanup()
+            main.exit()
+
+        utilities.assert_equals( expect=main.TRUE,
+                                 actual=stepResult,
+                                 onpass="Successfully construct " +
+                                        "test variables ",
+                                 onfail="Failed to construct test variables" )
+
+        if gitPull == 'True':
+            main.step( "Building ONOS in " + gitBranch + " branch" )
+            onosBuildResult = main.startUp.onosBuild( main, gitBranch )
+            stepResult = onosBuildResult
+            utilities.assert_equals( expect=main.TRUE,
+                                     actual=stepResult,
+                                     onpass="Successfully compiled " +
+                                            "latest ONOS",
+                                     onfail="Failed to compile " +
+                                            "latest ONOS" )
+        else:
+            main.log.warn( "Did not pull new code so skipping mvn " +
+                           "clean install" )
+        main.ONOSbench.getVersion( report=True )
+
+    def CASE2( self, main ):
+        """
+        - Set up cell
+            - Create cell file
+            - Set cell file
+            - Verify cell file
+        - Kill ONOS process
+        - Uninstall ONOS cluster
+        - Verify ONOS start up
+        - Install ONOS cluster
+        - Connect to cli
+        """
+        import time
+        main.case( "Starting up " + str( main.numCtrls ) +
+                   " node(s) ONOS cluster" )
+        main.caseExplanation = "Set up ONOS with " + str( main.numCtrls ) +\
+                                " node(s) ONOS cluster"
+
+        # kill off all onos processes
+        main.log.info( "Safety check, killing all ONOS processes" +
+                       " before initiating environment setup" )
+
+        for i in range( main.maxNodes ):
+            main.ONOSbench.onosStop( main.ONOSip[ i ] )
+            main.ONOSbench.onosDie( main.ONOSip[ i ] )
+
+        tempOnosIp = []
+        for i in range( main.numCtrls ):
+            tempOnosIp.append( main.ONOSip[i] )
+
+        main.ONOSbench.createCellFile( main.ONOSbench.ip_address,
+                                       "temp", main.Mininet1.ip_address,
+                                       main.apps, tempOnosIp )
+
+        main.step( "Apply cell to environment" )
+        cellResult = main.ONOSbench.setCell( "temp" )
+        verifyResult = main.ONOSbench.verifyCell()
+        stepResult = cellResult and verifyResult
+        utilities.assert_equals( expect=main.TRUE,
+                                 actual=stepResult,
+                                 onpass="Successfully applied cell to environment",
+                                 onfail="Failed to apply cell to environment " )
+
+        main.step( "Creating ONOS package" )
+        packageResult = main.ONOSbench.onosPackage()
+        stepResult = packageResult
+        utilities.assert_equals( expect=main.TRUE,
+                                 actual=stepResult,
+                                 onpass="Successfully created ONOS package",
+                                 onfail="Failed to create ONOS package" )
+
+        time.sleep( main.startUpSleep )
+        main.step( "Uninstalling ONOS package" )
+        onosUninstallResult = main.TRUE
+        for ip in main.ONOSip:
+            onosUninstallResult = onosUninstallResult and \
+                    main.ONOSbench.onosUninstall( nodeIp=ip )
+        stepResult = onosUninstallResult
+        utilities.assert_equals( expect=main.TRUE,
+                                 actual=stepResult,
+                                 onpass="Successfully uninstalled ONOS package",
+                                 onfail="Failed to uninstall ONOS package" )
+
+        time.sleep( main.startUpSleep )
+        main.step( "Installing ONOS package" )
+        onosInstallResult = main.TRUE
+        for i in range( main.numCtrls ):
+            onosInstallResult = onosInstallResult and \
+                    main.ONOSbench.onosInstall( node=main.ONOSip[ i ] )
+        stepResult = onosInstallResult
+        utilities.assert_equals( expect=main.TRUE,
+                                 actual=stepResult,
+                                 onpass="Successfully installed ONOS package",
+                                 onfail="Failed to install ONOS package" )
+
+        time.sleep( main.startUpSleep )
+        main.step( "Starting ONOS service" )
+        stopResult = main.TRUE
+        startResult = main.TRUE
+        onosIsUp = main.TRUE
+
+        for i in range( main.numCtrls ):
+            onosIsUp = onosIsUp and main.ONOSbench.isup( main.ONOSip[ i ] )
+        if onosIsUp == main.TRUE:
+            main.log.report( "ONOS instance is up and ready" )
+        else:
+            main.log.report( "ONOS instance may not be up, stop and " +
+                             "start ONOS again " )
+            for i in range( main.numCtrls ):
+                stopResult = stopResult and \
+                        main.ONOSbench.onosStop( main.ONOSip[ i ] )
+            for i in range( main.numCtrls ):
+                startResult = startResult and \
+                        main.ONOSbench.onosStart( main.ONOSip[ i ] )
+        stepResult = onosIsUp and stopResult and startResult
+        utilities.assert_equals( expect=main.TRUE,
+                                 actual=stepResult,
+                                 onpass="ONOS service is ready",
+                                 onfail="ONOS service did not start properly" )
+
+    def CASE8( self, main ):
+        """
+        Compare Topo
+        """
+        import json
+
+        main.case( "Compare ONOS Topology view to Mininet topology" )
+        main.caseExplanation = "Compare topology elements between Mininet" +\
+                                " and ONOS"
+
+        main.step( "Gathering topology information" )
+        # TODO: add a parameterized sleep here
+        devicesResults = main.TRUE
+        linksResults = main.TRUE
+        hostsResults = main.TRUE
+        devices = main.topo.getAllDevices( main )
+        hosts = main.topo.getAllHosts( main )
+        ports = main.topo.getAllPorts( main )
+        links = main.topo.getAllLinks( main )
+        clusters = main.topo.getAllClusters( main )
+
+        mnSwitches = main.Mininet1.getSwitches()
+        mnLinks = main.Mininet1.getLinks()
+        mnHosts = main.Mininet1.getHosts()
+
+        main.step( "Comparing MN topology to ONOS topology" )
+        for controller in range( main.numCtrls ):
+            controllerStr = str( controller + 1 )
+            if devices[ controller ] and ports[ controller ] and\
+               "Error" not in devices[ controller ] and\
+               "Error" not in ports[ controller ]:
+
+                currentDevicesResult = main.Mininet1.compareSwitches(
+                        mnSwitches,
+                        json.loads( devices[ controller ] ),
+                        json.loads( ports[ controller ] ) )
+            else:
+                currentDevicesResult = main.FALSE
+            utilities.assert_equals( expect=main.TRUE,
+                                     actual=currentDevicesResult,
+                                     onpass="ONOS" + controllerStr +
+                                     " Switches view is correct",
+                                     onfail="ONOS" + controllerStr +
+                                     " Switches view is incorrect" )
+
+            if links[ controller ] and "Error" not in links[ controller ]:
+                currentLinksResult = main.Mininet1.compareLinks(
+                        mnSwitches, mnLinks,
+                        json.loads( links[ controller ] ) )
+            else:
+                currentLinksResult = main.FALSE
+            utilities.assert_equals( expect=main.TRUE,
+                                     actual=currentLinksResult,
+                                     onpass="ONOS" + controllerStr +
+                                     " links view is correct",
+                                     onfail="ONOS" + controllerStr +
+                                     " links view is incorrect" )
+
+            if hosts[ controller ] or "Error" not in hosts[ controller ]:
+                currentHostsResult = main.Mininet1.compareHosts(
+                        mnHosts,
+                        json.loads( hosts[ controller ] ) )
+            else:
+                currentHostsResult = main.FALSE
+            utilities.assert_equals( expect=main.TRUE,
+                                     actual=currentHostsResult,
+                                     onpass="ONOS" + controllerStr +
+                                     " hosts exist in Mininet",
+                                     onfail="ONOS" + controllerStr +
+                                     " hosts don't match Mininet" )
+
+    def CASE9( self, main ):
+        '''
+            Report errors/warnings/exceptions
+        '''
+        main.log.info( "Error report: \n" )
+        main.ONOSbench.logReport(
+                globalONOSip[0],
+                [ "INFO", "WARN", "ERROR" , "Except" ],
+                "s" )
+        # main.ONOSbench.logReport( globalONOSip[1], [ "INFO" ], "d" )
+
+    def CASE10( self, main ):
+        """
+            Start Mininet topology with OF 1.0 switches
+        """
+        main.OFProtocol = "1.0"
+        main.log.report( "Start Mininet topology with OF 1.0 switches" )
+        main.case( "Start Mininet topology with OF 1.0 switches" )
+        main.caseExplanation = "Start mininet topology with OF 1.0 " +\
+                                "switches to test intents, exits out if " +\
+                                "topology did not start correctly"
+
+        main.step( "Starting Mininet topology with OF 1.0 switches" )
+        args = "--switch ovs,protocols=OpenFlow10"
+        switches = int( main.params['MININET']['switch'] )
+        cmd = "mn --topo linear,{} {}".format( switches, args )
+        topoResult = main.Mininet1.startNet( mnCmd = cmd )
+        stepResult = topoResult
+        utilities.assert_equals( expect=main.TRUE,
+                                 actual=stepResult,
+                                 onpass="Successfully loaded topology",
+                                 onfail="Failed to load topology" )
+        # Exit if topology did not load properly
+        if not topoResult:
+            main.cleanup()
+            main.exit()
+
+    def CASE11( self, main ):
+        """
+            Start Mininet topology with OF 1.3 switches
+        """
+        import re
+        main.OFProtocol = "1.3"
+        main.log.report( "Start Mininet topology with OF 1.3 switches" )
+        main.case( "Start Mininet topology with OF 1.3 switches" )
+        main.caseExplanation = "Start mininet topology with OF 1.3 " +\
+                                "switches to test intents, exits out if " +\
+                                "topology did not start correctly"
+
+        main.step( "Starting Mininet topology with OF 1.3 switches" )
+        args = "--switch ovs,protocols=OpenFlow13"
+        switches = int( main.params['MININET']['switch'] )
+        cmd = "mn --topo linear,{} {}".format( switches, args )
+        topoResult = main.Mininet1.startNet( mnCmd = cmd )
+        stepResult = topoResult
+        utilities.assert_equals( expect=main.TRUE,
+                                 actual=stepResult,
+                                 onpass="Successfully loaded topology",
+                                 onfail="Failed to load topology" )
+        # Exit if topology did not load properly
+        if not topoResult:
+            main.cleanup()
+            main.exit()
+
+        tempONOSip = []
+        for i in range( main.numCtrls ):
+            tempONOSip.append( main.ONOSip[ i ] )
+
+        swList = [ "s" + str( i ) for i in range( 1, switches + 1 ) ]
+        assignResult = main.Mininet1.assignSwController( sw=swList,
+                                                         ip=tempONOSip,
+                                                         port='6653' )
+        if not assignResult:
+            main.cleanup()
+            main.exit()
+
+        assignResult = main.TRUE
+        for sw in swList:
+            response = main.Mininet1.getSwController( "s" + str( i ) )
+            main.log.info( "Response is " + str( response ) )
+            for ip in tempONOSip:
+                if re.search( "tcp:" + ip, response ):
+                    assignResult = assignResult and main.TRUE
+                else:
+                    assignResult = assignResult and main.FALSE
+        stepResult = assignResult
+        utilities.assert_equals( expect=main.TRUE,
+                                 actual=stepResult,
+                                 onpass="Successfully assigned switches" +
+                                        "to controller",
+                                 onfail="Failed to assign switches to " +
+                                        "controller" )
+
+    def CASE14( self, main ):
+        """
+            Stop mininet
+        """
+        main.log.report( "Stop Mininet topology" )
+        main.case( "Stop Mininet topology" )
+        main.caseExplanation = "Stopping the current mininet topology " +\
+                                "to start up fresh"
+
+        main.step( "Stopping Mininet Topology" )
+        topoResult = main.Mininet1.stopNet( )
+        stepResult = topoResult
+        utilities.assert_equals( expect=main.TRUE,
+                                 actual=stepResult,
+                                 onpass="Successfully stop mininet",
+                                 onfail="Failed to stop mininet" )
+        # Exit if topology did not load properly
+        if not topoResult:
+            main.cleanup()
+            main.exit()
+
+    def CASE20( self, main ):
+        """
+        Add some device configurations and then check they are distributed
+        to all nodes
+        """
+        main.case( "Add Network configurations to the cluster" )
+        main.caseExplanation = "Add Network Configurations for devices" +\
+                               " not discovered yet. One device is allowed" +\
+                               ", the other disallowed."
+        pprint = main.nodes[0].pprint
+
+        main.step( "Add Net Cfg for switch1" )
+        s1Json = { "rackAddress": 1,
+                   "name": "Switch1",
+                   "owner": "Jimmy",
+                   "allowed": True }
+        main.s1Json = s1Json
+        setS1Allow = main.ONOSrest1.setNetCfg( s1Json,
+                                               subjectClass="devices",
+                                               subjectKey="of:0000000000000001",
+                                               configKey="basic" )
+        s1Result = False
+        if setS1Allow:
+            # Check what we set is what is in ONOS
+            getS1 = main.ONOSrest1.getNetCfg( subjectClass="devices",
+                                              subjectKey="of:0000000000000001",
+                                              configKey="basic" )
+            onosCfg = pprint( getS1 )
+            sentCfg = pprint( s1Json )
+            if onosCfg == sentCfg:
+                s1Result = True
+            else:
+                main.log.error( "ONOS NetCfg doesn't match what was sent" )
+                main.log.debug( "ONOS config: {}".format( onosCfg ) )
+                main.log.debug( "Sent config: {}".format( sentCfg ) )
+        utilities.assert_equals( expect=True,
+                                 actual=s1Result,
+                                 onpass="Net Cfg added for device s1",
+                                 onfail="Net Cfg for device s1 not correctly set" )
+
+        main.step( "Add Net Cfg for switch3" )
+        s3Json = { "rackAddress": 3,
+                   "name": "Switch3",
+                   "owner": "Jane",
+                   "allowed": False }
+        main.s3Json = s3Json
+        setS3Disallow = main.ONOSrest1.setNetCfg( s3Json,
+                                                  subjectClass="devices",
+                                                  subjectKey="of:0000000000000003",
+                                                  configKey="basic" )
+        s3Result = False
+        if setS3Disallow:
+            # Check what we set is what is in ONOS
+            getS3 = main.ONOSrest1.getNetCfg( subjectClass="devices",
+                                              subjectKey="of:0000000000000003",
+                                              configKey="basic" )
+            onosCfg = pprint( getS3 )
+            sentCfg = pprint( s3Json )
+            if onosCfg == sentCfg:
+                s3Result = True
+            else:
+                main.log.error( "ONOS NetCfg doesn't match what was sent" )
+                main.log.debug( "ONOS config: {}".format( onosCfg ) )
+                main.log.debug( "Sent config: {}".format( sentCfg ) )
+        utilities.assert_equals( expect=True,
+                                 actual=s3Result,
+                                 onpass="Net Cfg added for device s3",
+                                 onfail="Net Cfg for device s3 not correctly set" )
+        main.netCfg.compareCfg( main, main.gossipTime )
+
+    def CASE21( self, main ):
+        """
+        Initial check of devices
+        """
+        import json
+        try:
+            assert main.s1Json, "s1Json not defined"
+        except AssertionError:
+            main.log.exception( "Case Prerequisites not set: " )
+            main.cleanup()
+            main.exit()
+        main.case( "Check Devices After they initially connect to ONOS" )
+
+        main.netCfg.compareCfg( main )
+
+        main.step( "ONOS should only show devices S1, S2, and S4" )
+        devices = main.ONOSrest1.devices()
+        main.log.debug( main.ONOSrest1.pprint( devices ) )
+        allowedDevices = [ "of:{}".format( str( i ).zfill( 16 ) ) for i in [ 1, 2, 4 ] ]
+        print allowedDevices
+        onosDevices = []
+        for sw in json.loads( devices ):
+            onosDevices.append( str( sw['id'] ) )
+        onosDevices.sort()
+        print onosDevices
+        utilities.assert_equals( expect=allowedDevices,
+                                 actual=onosDevices,
+                                 onpass="Only allowed devices are in ONOS",
+                                 onfail="ONOS devices doesn't match the list" +
+                                        " of allowed devices" )
+
+        main.step( "Check device annotations" )
+        keys = [ 'name', 'owner', 'rackAddress' ]
+        for sw in json.loads( devices ):
+            if "of:0000000000000001" in sw['id']:
+                s1Correct = True
+                for k in keys:
+                    if str( sw['annotations'][k] ) != str( main.s1Json[k] ):
+                        s1Correct = False
+                        main.log.debug( "{} is wrong on s1".format( k ) )
+                if not s1Correct:
+                    main.log.error( "Annotations for s1 are incorrect: {}".format( sw ) )
+        try:
+            stepResult = s1Correct
+        except NameError:
+            stepResult = False
+            main.log.error( "s1 not found in devices" )
+        utilities.assert_equals( expect=True,
+                                 actual=stepResult,
+                                 onpass="Configured device's annotations are correct",
+                                 onfail="Incorrect annotations for configured devices." )
+
+    def CASE22( self, main ):
+        """
+        Add some device configurations for connected devices and then check
+        they are distributed to all nodes
+        """
+        main.case( "Add Network configurations for connected devices to the cluster" )
+        main.caseExplanation = "Add Network Configurations for discovered " +\
+                               "devices. One device is allowed" +\
+                               ", the other disallowed."
+        pprint = main.nodes[0].pprint
+
+        main.step( "Add Net Cfg for switch2" )
+        s2Json = { "rackAddress": 2,
+                   "name": "Switch2",
+                   "owner": "Jenny",
+                   "allowed": True }
+        main.s2Json = s2Json
+        setS2Allow = main.ONOSrest2.setNetCfg( s2Json,
+                                               subjectClass="devices",
+                                               subjectKey="of:0000000000000002",
+                                               configKey="basic" )
+        s2Result = False
+        if setS2Allow:
+            # Check what we set is what is in ONOS
+            getS2 = main.ONOSrest2.getNetCfg( subjectClass="devices",
+                                              subjectKey="of:0000000000000002",
+                                              configKey="basic" )
+            onosCfg = pprint( getS2 )
+            sentCfg = pprint( s2Json )
+            if onosCfg == sentCfg:
+                s2Result = True
+            else:
+                main.log.error( "ONOS NetCfg doesn't match what was sent" )
+                main.log.debug( "ONOS config: {}".format( onosCfg ) )
+                main.log.debug( "Sent config: {}".format( sentCfg ) )
+        utilities.assert_equals( expect=True,
+                                 actual=s2Result,
+                                 onpass="Net Cfg added for device s2",
+                                 onfail="Net Cfg for device s2 not correctly set" )
+
+        main.step( "Add Net Cfg for switch4" )
+        s4Json = { "rackAddress": 4,
+                   "name": "Switch4",
+                   "owner": "John",
+                   "allowed": False }
+        main.s4Json = s4Json
+        setS4Disallow = main.ONOSrest4.setNetCfg( s4Json,
+                                                  subjectClass="devices",
+                                                  subjectKey="of:0000000000000004",
+                                                  configKey="basic" )
+        s4Result = False
+        if setS4Disallow:
+            # Check what we set is what is in ONOS
+            getS4 = main.ONOSrest4.getNetCfg( subjectClass="devices",
+                                              subjectKey="of:0000000000000004",
+                                              configKey="basic" )
+            onosCfg = pprint( getS4 )
+            sentCfg = pprint( s4Json )
+            if onosCfg == sentCfg:
+                s4Result = True
+            else:
+                main.log.error( "ONOS NetCfg doesn't match what was sent" )
+                main.log.debug( "ONOS config: {}".format( onosCfg ) )
+                main.log.debug( "Sent config: {}".format( sentCfg ) )
+        utilities.assert_equals( expect=True,
+                                 actual=s4Result,
+                                 onpass="Net Cfg added for device s4",
+                                 onfail="Net Cfg for device s3 not correctly set" )
+
+        main.netCfg.compareCfg( main, main.gossipTime )
+
+    def CASE23( self, main ):
+        """
+        Check of devices after all Network Configurations are set
+        """
+        import json
+        try:
+            assert main.s1Json, "s1Json not defined"
+            assert main.s2Json, "s2Json not defined"
+        except AssertionError:
+            main.log.exception( "Case Prerequisites not set: " )
+            main.cleanup()
+            main.exit()
+        main.case( "Check Devices after all configurations are set" )
+
+        main.netCfg.compareCfg( main )
+
+        main.step( "ONOS should only show devices S1 and S2" )
+        devices = main.ONOSrest1.devices()
+        main.log.debug( main.ONOSrest1.pprint( devices ) )
+        allowedDevices = [ "of:{}".format( str( i ).zfill( 16 ) ) for i in [ 1, 2 ] ]
+        onosDevices = []
+        for sw in json.loads( devices ):
+            onosDevices.append( str( sw['id'] ) )
+        onosDevices.sort()
+        failMsg = "ONOS devices doesn't match the list of allowed devices.\n"
+        failMsg += "Expected devices: {}\nActual devices: {}".format( allowedDevices,
+                                                                      onosDevices )
+        utilities.assert_equals( expect=allowedDevices,
+                                 actual=onosDevices,
+                                 onpass="Only allowed devices are in ONOS",
+                                 onfail=failMsg )
+
+        main.step( "Check device annotations" )
+        keys = [ 'name', 'owner', 'rackAddress' ]
+        for sw in json.loads( devices ):
+            if "of:0000000000000001" in sw['id']:
+                s1Correct = True
+                for k in keys:
+                    if str( sw['annotations'][k] ) != str( main.s1Json[k] ):
+                        s1Correct = False
+                        main.log.debug( "{} is wrong on s1".format( k ) )
+                if not s1Correct:
+                    main.log.error( "Annotations for s1 are incorrect: {}".format( sw ) )
+            elif "of:0000000000000002" in sw['id']:
+                s2Correct = True
+                for k in keys:
+                    if str( sw['annotations'][k] ) != str( main.s2Json[k] ):
+                        s2Correct = False
+                        main.log.debug( "{} is wrong on s2".format( k ) )
+                if not s2Correct:
+                    main.log.error( "Annotations for s2 are incorrect: {}".format( sw ) )
+        try:
+            stepResult = s1Correct and s2Correct
+        except NameError:
+            stepResult = False
+            main.log.error( "s1 and/or s2 not found in devices" )
+        utilities.assert_equals( expect=True,
+                                 actual=stepResult,
+                                 onpass="Configured device's annotations are correct",
+                                 onfail="Incorrect annotations for configured devices." )
+
+    def CASE24( self, main ):
+        """
+        Testing removal of configurations
+        """
+        try:
+            assert main.s1Json, "s1Json not defined"
+            assert main.s2Json, "s2Json not defined"
+            assert main.s3Json, "s3Json not defined"
+            assert main.s4Json, "s4Json not defined"
+        except AssertionError:
+            main.log.exception( "Case Prerequisites not set: " )
+            main.cleanup()
+            main.exit()
+        main.case( "Testing removal of configurations" )
+        main.step( "Remove 'allowed' configuration from all devices" )
+
+        s1Json = main.s1Json  # NOTE: This is a reference
+        try:
+            del s1Json['allowed']
+        except KeyError:
+            main.log.exception( "Key not found" )
+        setS1 = main.ONOSrest1.setNetCfg( s1Json,
+                                          subjectClass="devices",
+                                          subjectKey="of:0000000000000001",
+                                          configKey="basic" )
+
+        s2Json = main.s2Json  # NOTE: This is a reference
+        try:
+            del s2Json['allowed']
+        except KeyError:
+            main.log.exception( "Key not found" )
+        setS2 = main.ONOSrest2.setNetCfg( s2Json,
+                                          subjectClass="devices",
+                                          subjectKey="of:0000000000000002",
+                                          configKey="basic" )
+
+        s3Json = main.s3Json  # NOTE: This is a reference
+        try:
+            del s3Json['allowed']
+        except KeyError:
+            main.log.exception( "Key not found" )
+        setS3 = main.ONOSrest3.setNetCfg( s3Json,
+                                          subjectClass="devices",
+                                          subjectKey="of:0000000000000003",
+                                          configKey="basic" )
+
+        s4Json = main.s4Json  # NOTE: This is a reference
+        try:
+            del s4Json['allowed']
+        except KeyError:
+            main.log.exception( "Key not found" )
+        setS4 = main.ONOSrest4.setNetCfg( s4Json,
+                                          subjectClass="devices",
+                                          subjectKey="of:0000000000000004",
+                                          configKey="basic" )
+        removeAllowed = setS1 and setS2 and setS3 and setS4
+        utilities.assert_equals( expect=main.TRUE,
+                                 actual=removeAllowed,
+                                 onpass="Successfully removed 'allowed' config from devices",
+                                 onfail="Failed to remove the 'allowed' config key." )
+
+        main.netCfg.compareCfg( main, main.gossipTime )
+
+        main.step( "Delete basic config for s1 and s2" )
+        removeS1 = main.ONOSrest1.removeNetCfg( subjectClass="devices",
+                                                subjectKey="of:0000000000000001",
+                                                configKey="basic" )
+        removeS2 = main.ONOSrest2.removeNetCfg( subjectClass="devices",
+                                                subjectKey="of:0000000000000002",
+                                                configKey="basic" )
+        removeSingles = removeS1 and removeS2
+        utilities.assert_equals( expect=main.TRUE,
+                                 actual=removeSingles,
+                                 onpass="Successfully removed S1 and S2 basic config",
+                                 onfail="Failed to removed S1 and S2 basic config" )
+
+        main.netCfg.compareCfg( main, main.gossipTime )
+
+        main.step( "Delete the net config for S3" )
+        removeS3 = main.ONOSrest3.removeNetCfg( subjectClass="devices",
+                                                subjectKey="of:0000000000000003" )
+        utilities.assert_equals( expect=main.TRUE,
+                                 actual=removeS3,
+                                 onpass="Successfully removed S3's config",
+                                 onfail="Failed to removed S3's config" )
+
+        main.netCfg.compareCfg( main, main.gossipTime )
+
+        main.step( "Delete the net config for all devices" )
+        remove = main.ONOSrest3.removeNetCfg( subjectClass="devices" )
+        utilities.assert_equals( expect=main.TRUE,
+                                 actual=remove,
+                                 onpass="Successfully removed device config",
+                                 onfail="Failed to remove device config" )
+
+        main.netCfg.compareCfg( main, main.gossipTime )
+
+        main.step( "Assert the net config for devices is empty" )
+        get = main.ONOSrest3.getNetCfg( subjectClass="devices" )
+        utilities.assert_equals( expect='{}',
+                                 actual=get,
+                                 onpass="Successfully removed device config",
+                                 onfail="Failed to remove device config" )
diff --git a/TestON/tests/FUNCnetCfg/FUNCnetCfg.topo b/TestON/tests/FUNCnetCfg/FUNCnetCfg.topo
new file mode 100755
index 0000000..df4cd42
--- /dev/null
+++ b/TestON/tests/FUNCnetCfg/FUNCnetCfg.topo
@@ -0,0 +1,101 @@
+<TOPOLOGY>
+    <COMPONENT>
+
+        <ONOSbench>
+            <host>localhost</host>
+            <user>admin</user>
+            <password></password>
+            <type>OnosDriver</type>
+            <connect_order>1</connect_order>
+            <COMPONENTS>
+            </COMPONENTS>
+        </ONOSbench>
+
+        <ONOSrest1>
+            <host>OC1</host>
+            <port>8181</port>
+            <user>onos</user>
+            <password>rocks</password>
+            <type>OnosRestDriver</type>
+            <connect_order>2</connect_order>
+            <COMPONENTS>
+            </COMPONENTS>
+        </ONOSrest1>
+
+        <ONOSrest2>
+            <host>OC2</host>
+            <port>8181</port>
+            <user>onos</user>
+            <password>rocks</password>
+            <type>OnosRestDriver</type>
+            <connect_order>3</connect_order>
+            <COMPONENTS>
+            </COMPONENTS>
+        </ONOSrest2>
+
+        <ONOSrest3>
+            <host>OC3</host>
+            <port>8181</port>
+            <user>onos</user>
+            <password>rocks</password>
+            <type>OnosRestDriver</type>
+            <connect_order>4</connect_order>
+            <COMPONENTS>
+            </COMPONENTS>
+        </ONOSrest3>
+
+        <ONOSrest4>
+            <host>OC4</host>
+            <port>8181</port>
+            <user>onos</user>
+            <password>rocks</password>
+            <type>OnosRestDriver</type>
+            <connect_order>3</connect_order>
+            <COMPONENTS>
+            </COMPONENTS>
+        </ONOSrest4>
+
+        <ONOSrest5>
+            <host>OC5</host>
+            <port>8181</port>
+            <user>onos</user>
+            <password>rocks</password>
+            <type>OnosRestDriver</type>
+            <connect_order>3</connect_order>
+            <COMPONENTS>
+            </COMPONENTS>
+        </ONOSrest5>
+
+        <ONOSrest6>
+            <host>OC6</host>
+            <port>8181</port>
+            <user>onos</user>
+            <password>rocks</password>
+            <type>OnosRestDriver</type>
+            <connect_order>3</connect_order>
+            <COMPONENTS>
+            </COMPONENTS>
+        </ONOSrest6>
+
+        <ONOSrest7>
+            <host>OC7</host>
+            <port>8181</port>
+            <user>onos</user>
+            <password>rocks</password>
+            <type>OnosRestDriver</type>
+            <connect_order>3</connect_order>
+            <COMPONENTS>
+            </COMPONENTS>
+        </ONOSrest7>
+
+        <Mininet1>
+            <host>OCN</host>
+            <user>admin</user>
+            <password></password>
+            <type>MininetCliDriver</type>
+            <connect_order>5</connect_order>
+            <COMPONENTS> </COMPONENTS>
+        </Mininet1>
+
+    </COMPONENT>
+</TOPOLOGY>
diff --git a/TestON/tests/FUNCnetCfg/README b/TestON/tests/FUNCnetCfg/README
new file mode 100644
index 0000000..f52af84
--- /dev/null
+++ b/TestON/tests/FUNCnetCfg/README
@@ -0,0 +1,11 @@
+This test is designed to test the Network config subsystem. There are two
+primary areas that we are testing, The NetCfg subsystem as a framework and how
+this is used by other subsystems.
+Features Tested:
+A. Network Configuration subsystem as a framework
+    1. Add/Modify/Remove/Reading Net Config data
+    2. Distribution of configurations across ONOS nodes
+B. Usage of Net Cfg by other systems
+    1. Device configuration
+        a. Allow/disallow devices
+        b. Device name and other annotations
diff --git a/TestON/tests/FUNCnetCfg/__init__.py b/TestON/tests/FUNCnetCfg/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/TestON/tests/FUNCnetCfg/__init__.py