Merge "[ONOS-7225]: Fix/Add graphs on individual test wiki pages (FUNC, HA, USECASE)"
diff --git a/TestON/JenkinsFile/FUNCJenkinsFile b/TestON/JenkinsFile/FUNCJenkinsFile
index 3555299..857ff54 100644
--- a/TestON/JenkinsFile/FUNCJenkinsFile
+++ b/TestON/JenkinsFile/FUNCJenkinsFile
@@ -15,7 +15,8 @@
 "FUNCnetconf" : [wiki_link:prop["WikiPrefix"]+"-"+"FUNCnetconf", wiki_file:"FUNCnetconfWiki.txt"],
 "FUNCgroup" : [wiki_link:prop["WikiPrefix"]+"-"+"FUNCgroup", wiki_file:"FUNCgroupWiki.txt"],
 "FUNCintent" : [wiki_link:prop["WikiPrefix"]+"-"+"FUNCintent", wiki_file:"FUNCintentWiki.txt"],
-"FUNCintentRest" : [wiki_link:prop["WikiPrefix"]+"-"+"FUNCintentRest", wiki_file:"FUNCintentRestWiki.txt"]
+"FUNCintentRest" : [wiki_link:prop["WikiPrefix"]+"-"+"FUNCintentRest", wiki_file:"FUNCintentRestWiki.txt"],
+"FUNCformCluster" : [wiki_link:prop["WikiPrefix"]+"-"+"FUNCformCluster", wiki_file:"FUNCformClusterWiki.txt"]
 ]
 table_name = "executed_test_tests"
 result_name = "executed_test_results"
@@ -118,7 +119,7 @@
                             done
                             ls -al
                             cd '''
-                            if( prop["manualRun"] == "false" ){
+                            if( prop["manualRun"] == "false" || prop["postResult"] == "true" ){
                                 // Post Results
                                 withCredentials([
                                     string(credentialsId: 'db_pass', variable: 'pass'),
@@ -163,7 +164,7 @@
                         }
                     }
 
-                    if( prop["manualRun"] == "false" ){
+                    if( prop["manualRun"] == "false" || prop["postResult"] == "true" ){
                         def post = build job: "Pipeline_postjob_VM", propagate: false,
                             parameters: [
                                 string(name: 'Wiki_Contents', value: fileContents),
diff --git a/TestON/JenkinsFile/JenkinsfileTrigger b/TestON/JenkinsFile/JenkinsfileTrigger
index d0ce382..6b1da7b 100644
--- a/TestON/JenkinsFile/JenkinsfileTrigger
+++ b/TestON/JenkinsFile/JenkinsfileTrigger
@@ -6,15 +6,16 @@
 AllTheTests=
 [
     "FUNC":[
-            "FUNCipv6Intent" : ["basic":true, "extra_A":false, "extra_B":false, "day":""],
-            "FUNCoptical" :    ["basic":true, "extra_A":false, "extra_B":false, "day":""],
-            "FUNCflow" :       ["basic":true, "extra_A":false, "extra_B":false, "day":""],
-            "FUNCnetCfg":      ["basic":true, "extra_A":false, "extra_B":false, "day":""],
-            "FUNCovsdbtest" :  ["basic":true, "extra_A":false, "extra_B":false, "day":""],
-            "FUNCnetconf" :    ["basic":true, "extra_A":false, "extra_B":false, "day":""],
-            "FUNCgroup" :      ["basic":true, "extra_A":false, "extra_B":false, "day":""],
-            "FUNCintent" :     ["basic":false, "extra_A":true, "extra_B":false, "day":""],
-            "FUNCintentRest" : ["basic":false, "extra_A":false, "extra_B":true, "day":""]
+            "FUNCipv6Intent" : ["basic":true, "extra_A":false, "extra_B":false, "new_Test":false, "day":""],
+            "FUNCoptical" :    ["basic":true, "extra_A":false, "extra_B":false, "new_Test":false, "day":""],
+            "FUNCflow" :       ["basic":true, "extra_A":false, "extra_B":false, "new_Test":false, "day":""],
+            "FUNCnetCfg":      ["basic":true, "extra_A":false, "extra_B":false, "new_Test":false, "day":""],
+            "FUNCovsdbtest" :  ["basic":true, "extra_A":false, "extra_B":false, "new_Test":false, "day":""],
+            "FUNCnetconf" :    ["basic":true, "extra_A":false, "extra_B":false, "new_Test":false, "day":""],
+            "FUNCgroup" :      ["basic":true, "extra_A":false, "extra_B":false, "new_Test":false, "day":""],
+            "FUNCformCluster" :["basic":false, "extra_A":false, "extra_B":false, "new_Test":true, "day":""],
+            "FUNCintent" :     ["basic":false, "extra_A":true, "extra_B":false, "new_Test":false, "day":""],
+            "FUNCintentRest" : ["basic":false, "extra_A":false, "extra_B":true, "new_Test":false, "day":""],
     ],
     "HA":[
             "HAsanity" :                ["basic":true, "extra_A":false, "extra_B":false, "day":""],
@@ -197,6 +198,7 @@
 }
 def monday( getResult ){
     FUNC_choices += adder( "FUNC", "basic", true, "M", getResult )
+    FUNC_choices += adder( "FUNC", "new_Test", true, "M", getResult )
     FUNC_choices += adder( "FUNC", "extra_A", true, "M", getResult )
     HA_choices += adder( "HA", "basic", true, "M", getResult )
     HA_choices += adder( "HA", "extra_A", true, "M", getResult )
@@ -205,6 +207,7 @@
 }
 def tuesday( getDay, getResult ){
     FUNC_choices += adder( "FUNC", "basic", getDay, "T", getResult )
+    FUNC_choices += adder( "FUNC", "new_Test", getDay, "T", getResult )
     FUNC_choices += adder( "FUNC", "extra_B", getDay, "T", getResult )
     HA_choices += adder( "HA", "basic", getDay, "T", getResult )
     HA_choices += adder( "HA", "extra_B", getDay, "T", getResult )
@@ -216,6 +219,7 @@
 }
 def wednesday( getDay, getResult ){
     FUNC_choices += adder( "FUNC", "basic", getDay, "W", getResult )
+    FUNC_choices += adder( "FUNC", "new_Test", getDay, "W", getResult )
     FUNC_choices += adder( "FUNC", "extra_A", getDay, "W", getResult )
     HA_choices += adder( "HA", "basic", getDay, "W", getResult )
     HA_choices += adder( "HA", "extra_A", getDay, "W", getResult )
@@ -225,6 +229,7 @@
 }
 def thursday( getDay, getResult ){
     FUNC_choices += adder( "FUNC", "basic", getDay, "Th", getResult )
+    FUNC_choices += adder( "FUNC", "new_Test", getDay, "Th", getResult )
     FUNC_choices += adder( "FUNC", "extra_B", getDay, "Th", getResult )
     HA_choices += adder( "HA", "basic", getDay, "Th", getResult )
     HA_choices += adder( "HA", "extra_B", getDay, "Th", getResult )
@@ -233,6 +238,7 @@
 }
 def friday( getDay, getResult ){
     FUNC_choices += adder( "FUNC", "basic", getDay, "F", getResult )
+    FUNC_choices += adder( "FUNC", "new_Test", getDay, "F", getResult )
     FUNC_choices += adder( "FUNC", "extra_A", getDay, "F", getResult )
     HA_choices += adder( "HA", "basic", getDay, "F", getResult )
     HA_choices += adder( "HA", "extra_A", getDay, "F", getResult )
@@ -422,7 +428,7 @@
 }
 def oldFlowCheck( jobOn, onos_branch ){
     result = ""
-    if( isOldFlow && jobOn == "SCPF" && onos_branch="master" )
+    if( isOldFlow && jobOn == "SCPF" && onos_branch== "master" )
         result = '''sed -i -e 's/@Component(immediate = true)/@Component(enabled = false)/g' ~/onos/core/store/dist/src/main/java/org/onosproject/store/flow/impl/DistributedFlowRuleStore.java
         sed -i -e 's/@Component(enabled = false)/@Component(immediate = true)/g' ~/onos/core/store/dist/src/main/java/org/onosproject/store/flow/impl/ECFlowRuleStore.java'''
     return result
diff --git a/TestON/JenkinsFile/scripts/SCPFIntentInstallWithdrawRerouteLat.R b/TestON/JenkinsFile/scripts/SCPFIntentInstallWithdrawRerouteLat.R
index 6c1a220..037b6d4 100644
--- a/TestON/JenkinsFile/scripts/SCPFIntentInstallWithdrawRerouteLat.R
+++ b/TestON/JenkinsFile/scripts/SCPFIntentInstallWithdrawRerouteLat.R
@@ -70,7 +70,7 @@
                                   "<using-old-flow>",
                                   "<directory-to-save-graphs>",
                                   sep=" " ) )
-    q()  # basically exit(), but in R
+    quit( status = 1 )  # basically exit(), but in R
 }
 
 # -----------------------------------
@@ -187,8 +187,22 @@
 print( "Combining Install, Withdraw, and Reroute Latencies Data" )
 
 if ( ncol( rerouteData ) == 0 ){  # Checks if rerouteData exists, so we can exclude it if necessary
-    avgs <- c( installWithdrawData[ 'install_avg' ],
-               installWithdrawData[ 'withdraw_avg' ] )
+
+    requiredColumns <- c( "install_avg",
+                          "withdraw_avg"  )
+
+    tryCatch( avgs <- c( installWithdrawData[ requiredColumns] ),
+              error = function( e ) {
+                  print( "[ERROR] One or more expected columns are missing from the data. Please check that the data and SQL command are valid, then try again." )
+                  print( "Required columns: " )
+                  print( requiredColumns )
+                  print( "Actual columns: " )
+                  print( names( fileData ) )
+                  print( "Error dump:" )
+                  print( e )
+                  quit( status = 1 )
+              }
+             )
 } else{
     colnames( rerouteData ) <- c( "date",
                                   "name",
@@ -201,9 +215,21 @@
                                   "reroute_avg",
                                   "reroute_std" )
 
-    avgs <- c( installWithdrawData[ 'install_avg' ],
-               installWithdrawData[ 'withdraw_avg' ],
-               rerouteData[ 'reroute_avg' ] )
+    tryCatch( avgs <- c( installWithdrawData[ 'install_avg' ],
+                         installWithdrawData[ 'withdraw_avg' ],
+                         rerouteData[ 'reroute_avg' ] ),
+              error = function( e ) {
+                  print( "[ERROR] One or more expected columns are missing from the data. Please check that the data and SQL command are valid, then try again." )
+                  print( "Required columns: " )
+                  print( requiredColumns )
+                  print( "Actual columns: " )
+                  print( names( fileData ) )
+                  print( "Error dump:" )
+                  print( e )
+                  quit( status = 1 )
+              }
+             )
+
 }
 
 # Combine lists into data frames.
@@ -341,9 +367,16 @@
 
 print( paste( "Saving bar chart with error bars to", errBarOutputFile ) )
 
-ggsave( errBarOutputFile,
-        width = imageWidth,
-        height = imageHeight,
-        dpi = imageDPI )
+tryCatch( ggsave( errBarOutputFile,
+                  width = imageWidth,
+                  height = imageHeight,
+                  dpi = imageDPI ),
+          error = function( e ){
+              print( "[ERROR] There was a problem saving the graph due to a graph formatting exception.  Error dump:" )
+              print( e )
+              quit( status = 1 )
+          }
+        )
 
 print( paste( "[SUCCESS] Successfully wrote bar chart with error bars out to", errBarOutputFile ) )
+quit( status = 0 )
diff --git a/TestON/JenkinsFile/scripts/SCPFLineGraph.R b/TestON/JenkinsFile/scripts/SCPFLineGraph.R
index 3f78953..ba84be4 100644
--- a/TestON/JenkinsFile/scripts/SCPFLineGraph.R
+++ b/TestON/JenkinsFile/scripts/SCPFLineGraph.R
@@ -78,7 +78,7 @@
                                     "<using-old-flow>",
                                     "<directory-to-save-graph>",
                   sep = " " ) )
-    q()  # basically exit(), but in R
+    quit( status = 1 )  # basically exit(), but in R
 }
 
 # -------------------------------
@@ -134,11 +134,21 @@
 # Create lists c() and organize data into their corresponding list.
 print( "Combine data retrieved from databases into a list." )
 
-if ( ncol( fileData ) > 1 ){
-    for ( i in 2:ncol( fileData ) ){
-        fileData[ i ] <- fileData[ i - 1 ] + fileData[ i ]
-    }
-}
+tryCatch( if ( ncol( fileData ) > 1 ){
+              for ( i in 2:ncol( fileData ) ){
+                  fileData[ i ] <- fileData[ i - 1 ] + fileData[ i ]
+              }
+          },
+          error = function( e ) {
+              print( "[ERROR] One or more expected columns are missing or invalid. Please check that the SQL command is valid, then try again." )
+              print( "Actual columns: " )
+              print( names( fileData ) )
+              print( "Error dump:" )
+              print( e )
+              quit( status = 1 )
+          }
+         )
+
 
 # --------------------
 # Construct Data Frame
@@ -266,9 +276,16 @@
 
 print( paste( "Saving result graph to", outputFile ) )
 
-ggsave( outputFile,
-        width = imageWidth,
-        height = imageHeight,
-        dpi = imageDPI )
+tryCatch( ggsave( outputFile,
+                  width = imageWidth,
+                  height = imageHeight,
+                  dpi = imageDPI ),
+          error = function( e ){
+              print( "[ERROR] There was a problem saving the graph due to a graph formatting exception.  Error dump:" )
+              print( e )
+              quit( status = 1 )
+          }
+        )
 
 print( paste( "[SUCCESS] Successfully wrote result graph out to", outputFile ) )
+quit( status = 0 )
diff --git a/TestON/JenkinsFile/scripts/SCPFbatchFlowResp.R b/TestON/JenkinsFile/scripts/SCPFbatchFlowResp.R
index a2166c1..d90c53e 100644
--- a/TestON/JenkinsFile/scripts/SCPFbatchFlowResp.R
+++ b/TestON/JenkinsFile/scripts/SCPFbatchFlowResp.R
@@ -57,7 +57,7 @@
 
 if ( is.na( args[ save_directory ] ) ){
 
-    print( paste( "Usage: Rscript SCPFbatchFlowResp",
+    print( paste( "Usage: Rscript SCPFbatchFlowResp.R",
                                   "<database-host>",
                                   "<database-port>",
                                   "<database-user-id>",
@@ -68,7 +68,7 @@
                                   "<directory-to-save-graphs>",
                                   sep=" " ) )
 
-    q()  # basically exit(), but in R
+    quit( status = 1 )  # basically exit(), but in R
 }
 
 # -----------------
@@ -149,8 +149,20 @@
 
 print( "Sorting data for Post." )
 
-postAvgs <- c( fileData[ 'posttoconfrm' ],
-               fileData[ 'elapsepost' ] )
+requiredColumns <- c( "posttoconfrm", "elapsepost" )
+
+tryCatch( postAvgs <- c( fileData[ requiredColumns] ),
+          error = function( e ) {
+              print( "[ERROR] One or more expected columns are missing from the data. Please check that the data and SQL command are valid, then try again." )
+              print( "Required columns: " )
+              print( requiredColumns )
+              print( "Actual columns: " )
+              print( names( fileData ) )
+              print( "Error dump:" )
+              print( e )
+              quit( status = 1 )
+          }
+         )
 
 # -------------------------
 # Post Construct Data Frame
@@ -181,15 +193,27 @@
 # Del Data Sorting
 # ----------------
 
-print( "Sorting data for Del." )
-avgs <- c( fileData[ 'deltoconfrm' ],
-           fileData[ 'elapsedel' ] )
+requiredColumns <- c( "deltoconfrm", "elapsedel" )
+
+tryCatch( delAvgs <- c( fileData[ requiredColumns] ),
+          error = function( e ) {
+              print( "[ERROR] One or more expected columns are missing from the data. Please check that the data and SQL command are valid, then try again." )
+              print( "Required columns: " )
+              print( requiredColumns )
+              print( "Actual columns: " )
+              print( names( fileData ) )
+              print( "Error dump:" )
+              print( e )
+              quit( status = 1 )
+          }
+         )
+
 
 # ------------------------
 # Del Construct Data Frame
 # ------------------------
 
-delDataFrame <- melt( avgs )
+delDataFrame <- melt( delAvgs )
 delDataFrame$scale <- fileData$scale
 delDataFrame$date <- fileData$date
 delDataFrame$iterative <- rev( seq( 1, nrow( fileData ), by = 1 ) )
@@ -303,10 +327,16 @@
 
 print( paste( "Saving Post bar chart to", postOutputFile ) )
 
-ggsave( postOutputFile,
-        width = imageWidth,
-        height = imageHeight,
-        dpi = imageDPI )
+tryCatch( ggsave( postOutputFile,
+                  width = imageWidth,
+                  height = imageHeight,
+                  dpi = imageDPI ),
+          error = function( e ){
+              print( "[ERROR] There was a problem saving the graph due to a graph formatting exception.  Error dump:" )
+              print( e )
+              quit( status = 1 )
+          }
+        )
 
 print( paste( "[SUCCESS] Successfully wrote stacked bar chart out to", postOutputFile ) )
 
@@ -369,9 +399,16 @@
 
 print( paste( "Saving Del bar chart to", delOutputFile ) )
 
-ggsave( delOutputFile,
-        width = imageWidth,
-        height = imageHeight,
-        dpi = imageDPI )
+tryCatch( ggsave( delOutputFile,
+                  width = imageWidth,
+                  height = imageHeight,
+                  dpi = imageDPI ),
+          error = function( e ){
+              print( "[ERROR] There was a problem saving the graph due to a graph formatting exception.  Error dump:" )
+              print( e )
+              quit( status = 1 )
+          }
+        )
 
 print( paste( "[SUCCESS] Successfully wrote stacked bar chart out to", delOutputFile ) )
+quit( status = 0 )
diff --git a/TestON/JenkinsFile/scripts/SCPFcbench.R b/TestON/JenkinsFile/scripts/SCPFcbench.R
index b62fa0f..0a28024 100644
--- a/TestON/JenkinsFile/scripts/SCPFcbench.R
+++ b/TestON/JenkinsFile/scripts/SCPFcbench.R
@@ -65,7 +65,7 @@
                                   "<directory-to-save-graphs>",
                                   sep=" " ) )
 
-    q()  # basically exit(), but in R
+    quit( status = 1 )  # basically exit(), but in R
 }
 
 # -----------------
@@ -126,7 +126,21 @@
 
 print( "Sorting data." )
 
-avgs <- c( fileData[ 'avg' ] )
+requiredColumns <- c( "avg" )
+
+tryCatch( avgs <- c( fileData[ requiredColumns] ),
+          error = function( e ) {
+              print( "[ERROR] One or more expected columns are missing from the data. Please check that the data and SQL command are valid, then try again." )
+              print( "Required columns: " )
+              print( requiredColumns )
+              print( "Actual columns: " )
+              print( names( fileData ) )
+              print( "Error dump:" )
+              print( e )
+              quit( status = 1 )
+          }
+         )
+
 
 # --------------------
 # Construct Data Frame
@@ -236,9 +250,15 @@
 
 print( paste( "Saving bar chart with error bars to", errBarOutputFile ) )
 
-ggsave( errBarOutputFile,
-        width = imageWidth,
-        height = imageHeight,
-        dpi = imageDPI )
+tryCatch( ggsave( errBarOutputFile,
+                  width = imageWidth,
+                  height = imageHeight,
+                  dpi = imageDPI ),
+          error = function( e ){
+              print( "[ERROR] There was a problem saving the graph due to a graph formatting exception.  Error dump:" )
+              print( e )
+              quit( status = 1 )
+          }
+        )
 
 print( paste( "[SUCCESS] Successfully wrote bar chart with error bars out to", errBarOutputFile ) )
diff --git a/TestON/JenkinsFile/scripts/SCPFflowTp1g.R b/TestON/JenkinsFile/scripts/SCPFflowTp1g.R
index 9123085..3d3a95e 100644
--- a/TestON/JenkinsFile/scripts/SCPFflowTp1g.R
+++ b/TestON/JenkinsFile/scripts/SCPFflowTp1g.R
@@ -72,7 +72,7 @@
                                   "<directory-to-save-graphs>",
                                   sep=" " ) )
 
-    q()  # basically exit(), but in R
+    quit( status = 1 )  # basically exit(), but in R
 }
 
 # -----------------
@@ -175,7 +175,20 @@
                            "avg",
                            "std" )
 
-avgs <- c( fileData[ 'avg' ] )
+requiredColumns <- c( "avg" )
+
+tryCatch( avgs <- c( fileData[ requiredColumns] ),
+          error = function( e ) {
+              print( "[ERROR] One or more expected columns are missing from the data. Please check that the data and SQL command are valid, then try again." )
+              print( "Required columns: " )
+              print( requiredColumns )
+              print( "Actual columns: " )
+              print( names( fileData ) )
+              print( "Error dump:" )
+              print( e )
+              quit( status = 1 )
+          }
+         )
 
 
 # ----------------------------
@@ -295,9 +308,16 @@
 
 print( paste( "Saving bar chart with error bars to", errBarOutputFile ) )
 
-ggsave( errBarOutputFile,
-        width = imageWidth,
-        height = imageHeight,
-        dpi = imageDPI )
+tryCatch( ggsave( errBarOutputFile,
+                  width = imageWidth,
+                  height = imageHeight,
+                  dpi = imageDPI ),
+          error = function( e ){
+              print( "[ERROR] There was a problem saving the graph due to a graph formatting exception.  Error dump:" )
+              print( e )
+              quit( status = 1 )
+          }
+        )
 
 print( paste( "[SUCCESS] Successfully wrote bar chart with error bars out to", errBarOutputFile ) )
+quit( status = 0 )
diff --git a/TestON/JenkinsFile/scripts/SCPFhostLat.R b/TestON/JenkinsFile/scripts/SCPFhostLat.R
index 56d0f11..90781a3 100644
--- a/TestON/JenkinsFile/scripts/SCPFhostLat.R
+++ b/TestON/JenkinsFile/scripts/SCPFhostLat.R
@@ -65,7 +65,7 @@
                                   "<directory-to-save-graphs>",
                                   sep=" " ) )
 
-    q()  # basically exit(), but in R
+    quit( status = 1 )  # basically exit(), but in R
 }
 
 # -----------------
@@ -127,7 +127,21 @@
 # ------------
 
 print( "Sorting data." )
-avgs <- c( fileData[ 'avg' ] )
+
+requiredColumns <- c( "avg" )
+
+tryCatch( avgs <- c( fileData[ requiredColumns] ),
+          error = function( e ) {
+              print( "[ERROR] One or more expected columns are missing from the data. Please check that the data and SQL command are valid, then try again." )
+              print( "Required columns: " )
+              print( requiredColumns )
+              print( "Actual columns: " )
+              print( names( fileData ) )
+              print( "Error dump:" )
+              print( e )
+              quit( status = 1 )
+          }
+         )
 
 # --------------------
 # Construct Data Frame
@@ -230,9 +244,16 @@
 
 print( paste( "Saving bar chart with error bars to", errBarOutputFile ) )
 
-ggsave( errBarOutputFile,
-        width = imageWidth,
-        height = imageHeight,
-        dpi = imageDPI )
+tryCatch( ggsave( errBarOutputFile,
+                  width = imageWidth,
+                  height = imageHeight,
+                  dpi = imageDPI ),
+          error = function( e ){
+              print( "[ERROR] There was a problem saving the graph due to a graph formatting exception.  Error dump:" )
+              print( e )
+              quit( status = 1 )
+          }
+        )
 
 print( paste( "[SUCCESS] Successfully wrote bar chart out to", errBarOutputFile ) )
+quit( status = 0 )
diff --git a/TestON/JenkinsFile/scripts/SCPFintentEventTp.R b/TestON/JenkinsFile/scripts/SCPFintentEventTp.R
index e9540f2..0b168ba 100644
--- a/TestON/JenkinsFile/scripts/SCPFintentEventTp.R
+++ b/TestON/JenkinsFile/scripts/SCPFintentEventTp.R
@@ -72,7 +72,7 @@
                                   "<directory-to-save-graphs>",
                                   sep=" " ) )
 
-    q()  # basically exit(), but in R
+    quit( status = 1 )  # basically exit(), but in R
 }
 
 # -----------------
@@ -173,7 +173,21 @@
 # ------------
 
 print( "Sorting data." )
-avgs <- c( fileData[ 'avg' ] )
+
+requiredColumns <- c( "avg" )
+
+tryCatch( avgs <- c( fileData[ requiredColumns] ),
+          error = function( e ) {
+              print( "[ERROR] One or more expected columns are missing from the data. Please check that the data and SQL command are valid, then try again." )
+              print( "Required columns: " )
+              print( requiredColumns )
+              print( "Actual columns: " )
+              print( names( fileData ) )
+              print( "Error dump:" )
+              print( e )
+              quit( status = 1 )
+          }
+         )
 
 # --------------------
 # Construct Data Frame
@@ -278,9 +292,16 @@
 
 print( paste( "Saving bar chart to", errBarOutputFile ) )
 
-ggsave( errBarOutputFile,
-        width = imageWidth,
-        height = imageHeight,
-        dpi = imageDPI )
+tryCatch( ggsave( errBarOutputFile,
+                  width = imageWidth,
+                  height = imageHeight,
+                  dpi = imageDPI ),
+          error = function( e ){
+              print( "[ERROR] There was a problem saving the graph due to a graph formatting exception.  Error dump:" )
+              print( e )
+              quit( status = 1 )
+          }
+        )
 
 print( paste( "[SUCCESS] Successfully wrote bar chart out to", errBarOutputFile ) )
+quit( status = 0 )
diff --git a/TestON/JenkinsFile/scripts/SCPFmastershipFailoverLat.R b/TestON/JenkinsFile/scripts/SCPFmastershipFailoverLat.R
index 8681f29..30f7bca 100644
--- a/TestON/JenkinsFile/scripts/SCPFmastershipFailoverLat.R
+++ b/TestON/JenkinsFile/scripts/SCPFmastershipFailoverLat.R
@@ -64,7 +64,7 @@
                                   "<directory-to-save-graphs>",
                                   sep=" " ) )
 
-        q()  # basically exit(), but in R
+        quit( status = 1 )  # basically exit(), but in R
 }
 
 # -----------------
@@ -134,8 +134,20 @@
 
 print( "Combining averages into a list." )
 
-avgs <- c( fileData[ 'kill_deact_avg' ],
-           fileData[ 'deact_role_avg' ] )
+requiredColumns <- c( "kill_deact_avg", "deact_role_avg" )
+
+tryCatch( avgs <- c( fileData[ requiredColumns] ),
+          error = function( e ) {
+              print( "[ERROR] One or more expected columns are missing from the data. Please check that the data and SQL command are valid, then try again." )
+              print( "Required columns: " )
+              print( requiredColumns )
+              print( "Actual columns: " )
+              print( names( fileData ) )
+              print( "Error dump:" )
+              print( e )
+              quit( status = 1 )
+          }
+         )
 
 # --------------------
 # Construct Data Frame
@@ -193,7 +205,7 @@
                 legend.key.size = unit( 1.5, 'lines' ) )
 
 barColors <- scale_fill_manual( values=c( "#F77670",
-                                       "#619DFA" ) )
+                                          "#619DFA" ) )
 
 wrapLegend <- guides( fill=guide_legend( nrow=1, byrow=TRUE ) )
 
@@ -264,10 +276,16 @@
 
 print( paste( "Saving bar chart with error bars to", errBarOutputFile ) )
 
-ggsave( errBarOutputFile,
-        width = imageWidth,
-        height = imageHeight,
-        dpi = imageDPI )
+tryCatch( ggsave( errBarOutputFile,
+                  width = imageWidth,
+                  height = imageHeight,
+                  dpi = imageDPI ),
+          error = function( e ){
+              print( "[ERROR] There was a problem saving the graph due to a graph formatting exception.  Error dump:" )
+              print( e )
+              quit( status = 1 )
+          }
+        )
 
 print( paste( "[SUCCESS] Successfully wrote bar chart with error bars out to", errBarOutputFile ) )
 
@@ -317,9 +335,16 @@
 
 print( paste( "Saving stacked bar chart to", stackedBarOutputFile ) )
 
-ggsave( stackedBarOutputFile,
-        width = imageWidth,
-        height = imageHeight,
-        dpi = imageDPI )
+tryCatch( ggsave( stackedBarOutputFile,
+                  width = imageWidth,
+                  height = imageHeight,
+                  dpi = imageDPI ),
+          error = function( e ){
+              print( "[ERROR] There was a problem saving the graph due to a graph formatting exception.  Error dump:" )
+              print( e )
+              quit( status = 1 )
+          }
+        )
 
 print( paste( "[SUCCESS] Successfully wrote stacked bar chart out to", stackedBarOutputFile ) )
+quit( status = 0 )
diff --git a/TestON/JenkinsFile/scripts/SCPFportLat.R b/TestON/JenkinsFile/scripts/SCPFportLat.R
index 254b718..4637072 100644
--- a/TestON/JenkinsFile/scripts/SCPFportLat.R
+++ b/TestON/JenkinsFile/scripts/SCPFportLat.R
@@ -65,7 +65,7 @@
                                   "<directory-to-save-graphs>",
                                   sep=" " ) )
 
-    q()  # basically exit(), but in R
+    quit( status = 1 )  # basically exit(), but in R
 }
 
 # -----------------
@@ -129,9 +129,21 @@
 
 print( "Sorting data for Port Up Averages." )
 
-upAvgs <- c( fileData[ 'up_ofp_to_dev_avg' ],
-             fileData[ 'up_dev_to_link_avg' ],
-             fileData[ 'up_link_to_graph_avg' ] )
+requiredColumns <- c( "up_ofp_to_dev_avg", "up_dev_to_link_avg", "up_link_to_graph_avg" )
+
+tryCatch( upAvgs <- c( fileData[ requiredColumns] ),
+          error = function( e ) {
+              print( "[ERROR] One or more expected columns are missing from the data. Please check that the data and SQL command are valid, then try again." )
+              print( "Required columns: " )
+              print( requiredColumns )
+              print( "Actual columns: " )
+              print( names( fileData ) )
+              print( "Error dump:" )
+              print( e )
+              quit( status = 1 )
+          }
+         )
+
 
 # ----------------------------
 # Port Up Construct Data Frame
@@ -166,9 +178,20 @@
 
 print( "Sorting data for Port Down Averages." )
 
-downAvgs <- c( fileData[ 'down_ofp_to_dev_avg' ],
-               fileData[ 'down_dev_to_link_avg' ],
-               fileData[ 'down_link_to_graph_avg' ] )
+requiredColumns <- c( "down_ofp_to_dev_avg", "down_dev_to_link_avg", "down_link_to_graph_avg" )
+
+tryCatch( downAvgs <- c( fileData[ requiredColumns] ),
+          error = function( e ) {
+              print( "[ERROR] One or more expected columns are missing from the data. Please check that the data and SQL command are valid, then try again." )
+              print( "Required columns: " )
+              print( requiredColumns )
+              print( "Actual columns: " )
+              print( names( fileData ) )
+              print( "Error dump:" )
+              print( e )
+              quit( status = 1 )
+          }
+         )
 
 # ------------------------------
 # Port Down Construct Data Frame
@@ -294,10 +317,16 @@
 
 print( paste( "Saving bar chart with error bars (Port Up Latency) to", errBarOutputFileUp ) )
 
-ggsave( errBarOutputFileUp,
-        width = imageWidth,
-        height = imageHeight,
-        dpi = imageDPI )
+tryCatch( ggsave( errBarOutputFileUp,
+                  width = imageWidth,
+                  height = imageHeight,
+                  dpi = imageDPI ),
+          error = function( e ){
+              print( "[ERROR] There was a problem saving the graph due to a graph formatting exception.  Error dump:" )
+              print( e )
+              quit( status = 1 )
+          }
+        )
 
 print( paste( "[SUCCESS] Successfully wrote bar chart with error bars (Port Up Latency) out to", errBarOutputFileUp ) )
 
@@ -362,9 +391,16 @@
 
 print( paste( "Saving bar chart with error bars (Port Down Latency) to", errBarOutputFileDown ) )
 
-ggsave( errBarOutputFileDown,
-        width = imageWidth,
-        height = imageHeight,
-        dpi = imageDPI )
+tryCatch( ggsave( errBarOutputFileDown,
+                  width = imageWidth,
+                  height = imageHeight,
+                  dpi = imageDPI ),
+          error = function( e ){
+              print( "[ERROR] There was a problem saving the graph due to a graph formatting exception.  Error dump:" )
+              print( e )
+              quit( status = 1 )
+          }
+        )
 
 print( paste( "[SUCCESS] Successfully wrote bar chart with error bars (Port Down Latency) out to", errBarOutputFileDown ) )
+quit( status = 0 )
diff --git a/TestON/JenkinsFile/scripts/SCPFscaleTopo.R b/TestON/JenkinsFile/scripts/SCPFscaleTopo.R
index fdb39e4..e69a383 100644
--- a/TestON/JenkinsFile/scripts/SCPFscaleTopo.R
+++ b/TestON/JenkinsFile/scripts/SCPFscaleTopo.R
@@ -65,7 +65,7 @@
                                   "<directory-to-save-graphs>",
                                   sep=" ") )
 
-    q()  # basically exit(), but in R
+    quit( status = 1 )  # basically exit(), but in R
 }
 
 # -----------------
@@ -126,9 +126,21 @@
 # ------------
 
 print( "Sorting data." )
-avgs <- c( fileData[ 'last_role_request_to_last_topology' ],
-           fileData[ 'last_connection_to_last_role_request' ],
-           fileData[ 'first_connection_to_last_connection' ] )
+
+requiredColumns <- c( "last_role_request_to_last_topology", "last_connection_to_last_role_request", "first_connection_to_last_connection" )
+
+tryCatch( avgs <- c( fileData[ requiredColumns] ),
+          error = function( e ) {
+              print( "[ERROR] One or more expected columns are missing from the data. Please check that the data and SQL command are valid, then try again." )
+              print( "Required columns: " )
+              print( requiredColumns )
+              print( "Actual columns: " )
+              print( names( fileData ) )
+              print( "Error dump:" )
+              print( e )
+              quit( status = 1 )
+          }
+         )
 
 # --------------------
 # Construct Data Frame
@@ -238,9 +250,16 @@
 
 print( paste( "Saving bar chart to", outputFile ) )
 
-ggsave( outputFile,
-        width = imageWidth,
-        height = imageHeight,
-        dpi = imageDPI )
+tryCatch( ggsave( outputFile,
+                  width = imageWidth,
+                  height = imageHeight,
+                  dpi = imageDPI ),
+          error = function( e ){
+              print( "[ERROR] There was a problem saving the graph due to a graph formatting exception.  Error dump:" )
+              print( e )
+              quit( status = 1 )
+          }
+        )
 
 print( paste( "[SUCCESS] Successfully wrote bar chart out to", outputFile ) )
+quit( status = 0 )
diff --git a/TestON/JenkinsFile/scripts/SCPFscalingMaxIntents.R b/TestON/JenkinsFile/scripts/SCPFscalingMaxIntents.R
index aed27e5..21dd70f 100644
--- a/TestON/JenkinsFile/scripts/SCPFscalingMaxIntents.R
+++ b/TestON/JenkinsFile/scripts/SCPFscalingMaxIntents.R
@@ -68,7 +68,7 @@
                                   "<directory-to-save-graphs>",
                                   sep=" " ) )
 
-    q()  # basically exit(), but in R
+    quit( status = 1 )  # basically exit(), but in R
 }
 
 # -----------------
@@ -152,8 +152,20 @@
 
 print( "Sorting data." )
 
-avgs <- c( fileData[ 'max_intents_ovs' ],
-           fileData[ 'max_flows_ovs' ] )
+requiredColumns <- c( "max_intents_ovs", "max_flows_ovs" )
+
+tryCatch( avgs <- c( fileData[ requiredColumns] ),
+          error = function( e ) {
+              print( "[ERROR] One or more expected columns are missing from the data. Please check that the data and SQL command are valid, then try again." )
+              print( "Required columns: " )
+              print( requiredColumns )
+              print( "Actual columns: " )
+              print( names( fileData ) )
+              print( "Error dump:" )
+              print( e )
+              quit( status = 1 )
+          }
+         )
 
 # --------------------
 # Construct Data Frame
@@ -259,9 +271,16 @@
 
 print( paste( "Saving bar chart to", outputFile ) )
 
-ggsave( outputFile,
-        width = imageWidth,
-        height = imageHeight,
-        dpi = imageDPI )
+tryCatch( ggsave( outputFile,
+                  width = imageWidth,
+                  height = imageHeight,
+                  dpi = imageDPI ),
+          error = function( e ){
+              print( "[ERROR] There was a problem saving the graph due to a graph formatting exception.  Error dump:" )
+              print( e )
+              quit( status = 1 )
+          }
+        )
 
 print( paste( "[SUCCESS] Successfully wrote bar chart out to", outputFile ) )
+quit( status = 0 )
diff --git a/TestON/JenkinsFile/scripts/SCPFswitchLat.R b/TestON/JenkinsFile/scripts/SCPFswitchLat.R
index 8a68e08..de506a3 100644
--- a/TestON/JenkinsFile/scripts/SCPFswitchLat.R
+++ b/TestON/JenkinsFile/scripts/SCPFswitchLat.R
@@ -66,7 +66,7 @@
                             "<directory-to-save-graphs>",
                             sep=" ") )
 
-    q()  # basically exit(), but in R
+    quit( status = 1 )  # basically exit(), but in R
 }
 
 # -----------------
@@ -131,11 +131,24 @@
 
 print( "Sorting data for Switch Up Averages." )
 
-upAvgs <- c( fileData[ 'up_device_to_graph_avg' ],
-             fileData[ 'role_reply_to_device_avg' ],
-             fileData[ 'role_request_to_role_reply_avg' ],
-             fileData[ 'feature_reply_to_role_request_avg' ],
-             fileData[ 'tcp_to_feature_reply_avg' ] )
+requiredColumns <- c( "up_device_to_graph_avg",
+                      "role_reply_to_device_avg",
+                      "role_request_to_role_reply_avg",
+                      "feature_reply_to_role_request_avg",
+                      "tcp_to_feature_reply_avg" )
+
+tryCatch( upAvgs <- c( fileData[ requiredColumns] ),
+          error = function( e ) {
+              print( "[ERROR] One or more expected columns are missing from the data. Please check that the data and SQL command are valid, then try again." )
+              print( "Required columns: " )
+              print( requiredColumns )
+              print( "Actual columns: " )
+              print( names( fileData ) )
+              print( "Error dump:" )
+              print( e )
+              quit( status = 1 )
+          }
+         )
 
 # ------------------------------
 # Switch Up Construct Data Frame
@@ -171,9 +184,22 @@
 
 print( "Sorting data for Switch Down Averages." )
 
-downAvgs <- c( fileData[ 'down_device_to_graph_avg' ],
-               fileData[ 'ack_to_device_avg' ],
-               fileData[ 'fin_ack_to_ack_avg' ] )
+requiredColumns <- c( "down_device_to_graph_avg",
+                      "ack_to_device_avg",
+                      "fin_ack_to_ack_avg" )
+
+tryCatch( downAvgs <- c( fileData[ requiredColumns] ),
+          error = function( e ) {
+              print( "[ERROR] One or more expected columns are missing from the data. Please check that the data and SQL command are valid, then try again." )
+              print( "Required columns: " )
+              print( requiredColumns )
+              print( "Actual columns: " )
+              print( names( fileData ) )
+              print( "Error dump:" )
+              print( e )
+              quit( status = 1 )
+          }
+         )
 
 # --------------------------------
 # Switch Down Construct Data Frame
@@ -291,10 +317,16 @@
 
 print( paste( "Saving bar chart with error bars (Switch Up Latency) to", errBarOutputFileUp ) )
 
-ggsave( errBarOutputFileUp,
-        width = imageWidth,
-        height = imageHeight,
-        dpi = imageDPI )
+tryCatch( ggsave( errBarOutputFileUp,
+                  width = imageWidth,
+                  height = imageHeight,
+                  dpi = imageDPI ),
+          error = function( e ){
+              print( "[ERROR] There was a problem saving the graph due to a graph formatting exception.  Error dump:" )
+              print( e )
+              quit( status = 1 )
+          }
+        )
 
 print( paste( "[SUCCESS] Successfully wrote bar chart with error bars (Switch Up Latency) out to", errBarOutputFileUp ) )
 
@@ -361,9 +393,16 @@
 
 print( paste( "Saving bar chart with error bars (Switch Down Latency) to", errBarOutputFileDown ) )
 
-ggsave( errBarOutputFileDown,
-        width = imageWidth,
-        height = imageHeight,
-        dpi = imageDPI )
+tryCatch( ggsave( errBarOutputFileDown,
+                  width = imageWidth,
+                  height = imageHeight,
+                  dpi = imageDPI ),
+          error = function( e ){
+              print( "[ERROR] There was a problem saving the graph due to a graph formatting exception.  Error dump:" )
+              print( e )
+              quit( status = 1 )
+          }
+        )
 
 print( paste( "[SUCCESS] Successfully wrote bar chart with error bars (Switch Down Latency) out to", errBarOutputFileDown ) )
+quit( status = 0 )
diff --git a/TestON/JenkinsFile/scripts/testCaseGraphGenerator.R b/TestON/JenkinsFile/scripts/testCaseGraphGenerator.R
index fb70d9c..1938ceb 100644
--- a/TestON/JenkinsFile/scripts/testCaseGraphGenerator.R
+++ b/TestON/JenkinsFile/scripts/testCaseGraphGenerator.R
@@ -62,7 +62,7 @@
                                   "<directory-to-save-graphs>",
                                   sep=" " ) )
 
-    q()  # basically exit(), but in R
+    quit( status = 1 )  # basically exit(), but in R
 }
 
 # -------------------------------
@@ -135,9 +135,20 @@
 
 print( "Combining Passed, Failed, and Planned Data." )
 
-categories <- c( fileData[ 'num_failed' ],
-                 fileData[ 'num_passed' ],
-                 fileData[ 'num_planned' ] )
+requiredColumns <- c( "num_failed", "num_passed", "num_planned" )
+
+tryCatch( categories <- c( fileData[ requiredColumns] ),
+          error = function( e ) {
+              print( "[ERROR] One or more expected columns are missing from the data. Please check that the data and SQL command are valid, then try again." )
+              print( "Required columns: " )
+              print( requiredColumns )
+              print( "Actual columns: " )
+              print( names( fileData ) )
+              print( "Error dump:" )
+              print( e )
+              quit( status = 1 )
+          }
+         )
 
 # --------------------
 # Construct Data Frame
@@ -294,9 +305,16 @@
 
 print( paste( "Saving result graph to", outputFile ) )
 
-ggsave( outputFile,
-        width = imageWidth,
-        height = imageHeight,
-        dpi = imageDPI )
+tryCatch( ggsave( outputFile,
+                  width = imageWidth,
+                  height = imageHeight,
+                  dpi = imageDPI ),
+          error = function( e ){
+              print( "[ERROR] There was a problem saving the graph due to a graph formatting exception.  Error dump:" )
+              print( e )
+              quit( status = 1 )
+          }
+        )
 
 print( paste( "[SUCCESS] Successfully wrote result graph out to", outputFile ) )
+quit( status = 0 )
diff --git a/TestON/drivers/common/cli/onosdriver.py b/TestON/drivers/common/cli/onosdriver.py
index 73b1755..31f26d5 100755
--- a/TestON/drivers/common/cli/onosdriver.py
+++ b/TestON/drivers/common/cli/onosdriver.py
@@ -2437,3 +2437,37 @@
         except Exception:
             main.log.exception( self.name + ": Uncaught exception!" )
             main.cleanAndExit()
+
+    def formCluster( self, onosIPs ):
+        """
+            From ONOS cluster for IP addresses in onosIPs list
+        """
+        try:
+            onosIPs = " ".join( onosIPs )
+            command = "onos-form-cluster {}".format(  onosIPs )
+            main.log.info( "Sending: " + command )
+            self.handle.sendline( "" )
+            self.handle.expect( self.prompt )
+            self.handle.sendline( command )
+            self.handle.expect( self.prompt )
+            handle = self.handle.before
+            main.log.debug( handle )
+            assert handle is not None, "Error in sendline"
+            assert "Command not found:" not in handle, handle
+            assert "Error" not in handle, handle
+            assert "Exception:" not in handle, handle
+            assert "curl:" not in handle, handle
+            return main.TRUE
+        except AssertionError:
+            main.log.exception( "{} Error in onos-form-cluster output:".format( self.name ) )
+            return main.FALSE
+        except TypeError:
+            main.log.exception( self.name + ": Object not as expected" )
+            return main.FALSE
+        except pexpect.EOF:
+            main.log.error( self.name + ": EOF exception found" )
+            main.log.error( self.name + ":    " + self.handle.before )
+            main.cleanAndExit()
+        except Exception:
+            main.log.exception( self.name + ": Uncaught exception!" )
+            main.cleanAndExit()
diff --git a/TestON/tests/FUNC/FUNCformCluster/FUNCformCluster.params b/TestON/tests/FUNC/FUNCformCluster/FUNCformCluster.params
new file mode 100644
index 0000000..b8da936
--- /dev/null
+++ b/TestON/tests/FUNC/FUNCformCluster/FUNCformCluster.params
@@ -0,0 +1,44 @@
+<PARAMS>
+    # CASE - Description
+    # 0    - Variable initialization and optional pull and build ONOS package
+    # 1    - install ONOS with single node
+    # 2    - Starting ONOS with forming clusters.
+    # 3    - Checking the ONOS configuration with single node
+    # 4    - Checking the ONOS configuration with cluster formed
+    # 5    - Starting Mininet and verifying topology
+
+    <testcases>0,1,3,2,4,5</testcases>
+
+    <DEPENDENCY>
+        <path>/tests/FUNC/FUNCformCluster/dependencies/</path>
+        <file>formClusterFuncs</file>
+    </DEPENDENCY>
+    <GRAPH>
+        <nodeCluster>BM</nodeCluster>
+        <builds>20</builds>
+    </GRAPH>
+    <ENV>
+        <cellApps>drivers,openflow</cellApps>
+        <additionalApp>org.onosproject.fwd</additionalApp>
+        <cellBasicName>singleTemp</cellBasicName>
+    </ENV>
+    <GIT>
+        <pull>False</pull>
+        <branch>master</branch>
+    </GIT>
+    <TEST>
+        <numNodes>7</numNodes>
+    </TEST>
+    <RETRY>
+        <pingall>2</pingall>
+        <topoCheck>2</topoCheck>
+    </RETRY>
+    <SLEEP>
+        <afterONOSStart>15</afterONOSStart>
+        <pingall>3</pingall>
+    </SLEEP>
+    <MININET>
+        <topo>mn --topo tree,2,2</topo>
+    </MININET>
+
+</PARAMS>
diff --git a/TestON/tests/FUNC/FUNCformCluster/FUNCformCluster.py b/TestON/tests/FUNC/FUNCformCluster/FUNCformCluster.py
new file mode 100644
index 0000000..55b6e41
--- /dev/null
+++ b/TestON/tests/FUNC/FUNCformCluster/FUNCformCluster.py
@@ -0,0 +1,261 @@
+"""
+Copyright 2017 Open Networking Foundation ( ONF )
+
+Please refer questions to either the onos test mailing list at <onos-test@onosproject.org>,
+the System Testing Plans and Results wiki page at <https://wiki.onosproject.org/x/voMg>,
+or the System Testing Guide page at <https://wiki.onosproject.org/x/WYQg>
+
+    TestON is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 2 of the License, or
+    ( at your option ) any later version.
+
+    TestON is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with TestON.  If not, see <http://www.gnu.org/licenses/>.
+"""
+
+
+class FUNCformCluster:
+
+    def __init__( self ):
+        self.default = ''
+
+    def CASE0( self, main ):
+        import imp
+        import re
+
+        try:
+            from tests.dependencies.ONOSSetup import ONOSSetup
+            main.testSetUp = ONOSSetup()
+        except ImportError:
+            main.log.error( "ONOSSetup not found. exiting the test" )
+            main.cleanAndExit()
+        main.testSetUp.envSetupDescription()
+        stepResult = main.TRUE
+        try:
+            main.apps = main.params[ 'ENV' ][ 'cellApps' ]
+            main.additionalApp = main.params[ 'ENV' ][ 'additionalApp' ]
+            main.cellBasicName = main.params[ 'ENV' ][ 'cellBasicName' ]
+            main.mnTopo = main.params[ 'MININET' ][ 'topo' ]
+            main.startSleep = int( main.params[ 'SLEEP' ][ 'afterONOSStart' ] )
+            dependencyPath = main.testOnDirectory + \
+                             main.params[ 'DEPENDENCY' ][ 'path' ]
+            dependencyFile = main.params[ 'DEPENDENCY' ][ 'file' ]
+            main.numNodes = int( main.params[ 'TEST' ][ 'numNodes' ] )
+            main.funcs = imp.load_source( dependencyFile,
+                                            dependencyPath +
+                                            dependencyFile +
+                                            ".py" )
+            main.pingallRetry = int( main.params[ 'RETRY' ][ 'pingall' ] )
+            main.topoCheckRetry = int( main.params[ 'RETRY' ][ 'topoCheck' ] )
+            main.pingallSleep = int( main.params[ 'SLEEP' ][ 'pingall' ] )
+
+        except Exception as e:
+            main.testSetUp.envSetupException( e )
+        if len( main.Cluster.runningNodes ) != main.numNodes:
+            main.log.error( "The number of the nodes needs to be " + str( main.numNodes ) +
+                            "\nExiting Test..." )
+            main.cleanAndExit()
+        main.testSetUp.evnSetupConclusion( stepResult )
+
+    def CASE1( self, main ):
+        """
+        - Create cells with single node
+            - apply each cell to each cluster
+        - install ONOS
+        - ssh-secure
+        - start the ONOS
+        - activate org.onosproject.fwd to cluster 1 only.
+        """
+        main.case( "Starting ONOS with indepenent configuration" )
+        main.caseExplanation = "Starting ONOS with one node itself."
+        main.testSetUp.killingAllOnos( main.Cluster, True, False )
+        threads = []
+        i = 0
+        for cluster in main.Cluster.runningNodes:
+            i += 1
+            t = main.Thread( target=cluster.Bench.createCellFile,
+                             name="create-cell",
+                             args=[ main.ONOSbench.ip_address,
+                                    main.cellBasicName + str( i ),
+                                    main.Mininet1.ip_address,
+                                    main.apps,
+                                    cluster.ip_address,
+                                    main.ONOScell.karafUser,
+                                    True ] )
+            threads.append( t )
+            t.start()
+        cellResult = main.TRUE
+        for t in threads:
+            t.join()
+            cellResult = cellResult and t.result
+
+        threads = []
+        i = 0
+        for cluster in main.Cluster.runningNodes:
+            i += 1
+            t = main.Thread( target=cluster.Bench.setCell,
+                             name="set-cell",
+                             args=[ main.cellBasicName + str( i ) ] )
+            threads.append( t )
+            t.start()
+        for t in threads:
+            t.join()
+            cellResult = cellResult and t.result
+
+        threads = []
+        i = 0
+        for cluster in main.Cluster.runningNodes:
+            i += 1
+            t = main.Thread( target=cluster.Bench.verifyCell,
+                             name="verify-cell" )
+            threads.append( t )
+            t.start()
+        for t in threads:
+            t.join()
+            cellResult = cellResult and t.result
+
+        uninstallResult = main.testSetUp.uninstallOnos( main.Cluster, True )
+        buildResult = main.testSetUp.buildOnos( main.Cluster )
+        installResult = main.testSetUp.installOnos( main.Cluster, True, True )
+        secureSshResult = main.testSetUp.setupSsh( main.Cluster )
+        onosServiceResult = main.testSetUp.checkOnosService( main.Cluster )
+        onosCliResult = main.testSetUp.startOnosClis( main.Cluster )
+        activateResult = main.Cluster.active( 0 ).CLI.activateApp( main.additionalApp )
+
+        result = cellResult and uninstallResult and buildResult and installResult and \
+                 secureSshResult and onosServiceResult and onosCliResult and activateResult
+        utilities.assert_equals( expect=main.TRUE,
+                                 actual=result,
+                                 onpass="Successfully started the ONOS",
+                                 onfail="Failed to start the ONOS" )
+
+    def CASE2( self, main ):
+        """
+        - Execute onos-form-cluster to all the nodes.
+        - start the ONOS.
+        - activate org.onosproject.fwd to cluster 1.
+        """
+        main.case( "Starting ONOS with form cluster." )
+        main.caseExplanation = "This will connect all the clusters of the ONOS."
+        main.step( "Executing onos-form-cluster" )
+        formClusterResult = main.ONOSbench.formCluster( main.Cluster.getIps( True, True ) )
+        utilities.assert_equals( expect=main.TRUE,
+                                 actual=result,
+                                 onpass="Successfully formed clusters to ONOS",
+                                 onfail="Failed to form clusters to ONOS" )
+        onosServiceResult = main.testSetUp.checkOnosService( main.Cluster )
+        onosCliResult = main.testSetUp.startOnosClis( main.Cluster )
+        activateResult = main.Cluster.active( 0 ).CLI.activateApp( main.additionalApp )
+        result = formClusterResult and onosServiceResult and onosCliResult and activateResult
+        utilities.assert_equals( expect=main.TRUE,
+                                 actual=result,
+                                 onpass="Successfully formed clusters to ONOS and started",
+                                 onfail="Failed to form clusters to ONOS and started" )
+
+    def CASE3( self, main ):
+        """
+            Checking the configuration of the ONOS with single-node ONOS.
+            It will check :
+                - the number of the node : They should only have 1 node.
+                - App status : Only the first node should have additional app installed.
+        """
+        import time
+        main.case( "Checking the configuration of the ONOS" )
+        main.caseExplanation = "Checking the number of the nodes and apps"
+        main.step( "Checking the number of the nodes" )
+        main.log.info( "Sleep for " + str( main.startSleep ) + " to give enough time to ONOS")
+        time.sleep( main.startSleep )
+        result = main.funcs.checkingNumNodes( main, 1 )
+        utilities.assert_equals( expect=main.TRUE,
+                                 actual=result,
+                                 onpass="Successfully checking the nodes numbers of the ONOS",
+                                 onfail="Failed to checking the nodes numbers of the ONOS" )
+        main.step( "Checking the app status. Only the first node should have " +
+                   main.additionalApp + " installed." )
+        i = 0
+        appResult = main.TRUE
+        for cluster in main.Cluster.active():
+            appResult = appResult and main.funcs.checkingApp( main, main.additionalApp, cluster, True if i == 0 else False )
+            i += 1
+        main.Cluster.active( 0 ).CLI.deactivateApp( main.additionalApp )
+        utilities.assert_equals( expect=main.TRUE,
+                                 actual=appResult,
+                                 onpass="Successfully checking the app status of the ONOS",
+                                 onfail="Failed to checking the app status of the ONOS" )
+
+    def CASE4( self, main ):
+        """
+            Checking the configuration of the ONOS with form-cluster.
+            It will check :
+                - the number of the node : They should only have 7 nodes.
+                - state of the node.
+                - App status : All the nodes should have additional app.
+        """
+        import time
+        main.case( "Checking the configuration of the ONOS after form-cluster" )
+        main.caseExplanation = "Checking the number of the nodes and apps"
+        main.step( "Checking the number of the nodes" )
+        main.log.info( "Sleep for " + str( main.startSleep ) + " to give enough time to ONOS")
+        time.sleep( main.startSleep )
+        result = main.funcs.checkingNumNodes( main, main.numNodes )
+        utilities.assert_equals( expect=main.TRUE,
+                                 actual=result,
+                                 onpass="Successfully checking the nodes numbers of the ONOS",
+                                 onfail="Failed to checking the nodes numbers of the ONOS" )
+        main.step( "Checking the status of the nodes" )
+        nodeStatusResult = main.TRUE if main.Cluster.nodesCheck() else main.FALSE
+        utilities.assert_equals( expect=main.TRUE,
+                                 actual=nodeStatusResult,
+                                 onpass="The status of the nodes were in READY as expected",
+                                 onfail="The status of the nodes were NOT in READY as expected" )
+        main.step( "Checking the app status. All nodes should have " +
+                   main.additionalApp + " installed." )
+        appResult = main.TRUE
+        for cluster in main.Cluster.active():
+            appResult = appResult and main.funcs.checkingApp( main, main.additionalApp, cluster, True )
+        utilities.assert_equals( expect=main.TRUE,
+                                 actual=appResult,
+                                 onpass="Successfully checking the app status of the ONOS",
+                                 onfail="Failed to checking the app status of the ONOS" )
+
+    def CASE5( self, main ):
+        """
+            Run simple mininet to check connectivity of ONOS clusters.
+                - It will do ping all
+                - It will compare topos between mininet and ONOS.
+        """
+        try:
+            from tests.dependencies.topology import Topology
+        except ImportError:
+            main.log.error( "Topology not found exiting the test" )
+            main.cleanAndExit()
+        try:
+            main.Topology
+        except ( NameError, AttributeError ):
+            main.Topology = Topology()
+        main.case( "Starting 2x2 Tree Mininet and compare the Topology" )
+        main.caseExplanation = "Starting 2x2 Mininet and assign ONOS controllers to switches."
+        main.step( "Starting Mininet" )
+        for ctrl in main.Cluster.runningNodes:
+            main.mnTopo += " --controller remote,ip=" + ctrl.ipAddress
+        startMnResult = main.Mininet1.startNet( mnCmd=main.mnTopo )
+        utilities.assert_equals( expect=main.TRUE,
+                                 actual=startMnResult,
+                                 onpass="Successfully started Mininet",
+                                 onfail="Failed to start Mininet" )
+        main.step( "Pingall hosts to confirm ONOS discovery" )
+        pingResult = utilities.retry( f=main.Mininet1.pingall,
+                                       retValue=main.FALSE,
+                                       attempts=main.pingallRetry,
+                                       sleep=main.pingallSleep )
+        utilities.assert_equals( expect=main.TRUE,
+                                 actual=pingResult,
+                                 onpass="Successfully discovered hosts",
+                                 onfail="Failed to discover hosts" )
+        main.Topology.compareTopos( main.Mininet1, main.topoCheckRetry )
diff --git a/TestON/tests/FUNC/FUNCformCluster/FUNCformCluster.topo b/TestON/tests/FUNC/FUNCformCluster/FUNCformCluster.topo
new file mode 100644
index 0000000..c96b419
--- /dev/null
+++ b/TestON/tests/FUNC/FUNCformCluster/FUNCformCluster.topo
@@ -0,0 +1,36 @@
+<TOPOLOGY>
+    <COMPONENT>
+
+        <ONOScell>
+            <host>localhost</host>  # ONOS "bench" machine
+            <user>sdn</user>
+            <password>rocks</password>
+            <type>OnosClusterDriver</type>
+            <connect_order>1</connect_order>
+            <COMPONENTS>
+                <cluster_name></cluster_name>  # Used as a prefix for cluster components. Defaults to 'ONOS'
+                <diff_clihost></diff_clihost> # if it has different host other than localhost for CLI. True or empty. OC# will be used for True.
+                <karaf_username></karaf_username>
+                <karaf_password></karaf_password>
+                <web_user></web_user>
+                <web_pass></web_pass>
+                <rest_port></rest_port>
+                <prompt></prompt>  # TODO: we technically need a few of these, one per component
+                <onos_home></onos_home>  # defines where onos home is
+                <nodes> 7 </nodes>  # number of nodes in the cluster
+            </COMPONENTS>
+        </ONOScell>
+
+        <Mininet1>
+            <host>OCN</host>
+            <user>sdn</user>
+            <password>rocks</password>
+            <type>MininetCliDriver</type>
+            <connect_order>2</connect_order>
+            <COMPONENTS>
+                <prompt></prompt>
+            </COMPONENTS>
+        </Mininet1>
+
+    </COMPONENT>
+</TOPOLOGY>
diff --git a/TestON/tests/FUNC/FUNCformCluster/README b/TestON/tests/FUNC/FUNCformCluster/README
new file mode 100644
index 0000000..4ab2cc6
--- /dev/null
+++ b/TestON/tests/FUNC/FUNCformCluster/README
@@ -0,0 +1,13 @@
+Summary:
+        This test is checking the functionality of onos-form-cluster.
+        It will first run 7 single node of ONOS and check the number of the node and app.
+        Since it is single node, each of them should have 1 node.
+        Then, it will form 7 clusters to the ONOS and re-check the number of the nodes, status of nodes,
+        and app.
+        This time, it should have 7 nodes and installing app from one node should affect the other nodes.
+        The status of the Nodes should be "READY"
+        Lastly, it will run the Mininet with controllers of 7 nodes to pingall and compare topology
+        of ONOS and Mininet.
+
+Required:
+        Since it is fixed with 7 nodes, test will be forced to exit unless it has 7 clusters.
\ No newline at end of file
diff --git a/TestON/tests/FUNC/FUNCformCluster/__init__.py b/TestON/tests/FUNC/FUNCformCluster/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/TestON/tests/FUNC/FUNCformCluster/__init__.py
diff --git a/TestON/tests/FUNC/FUNCformCluster/dependencies/formClusterFuncs.py b/TestON/tests/FUNC/FUNCformCluster/dependencies/formClusterFuncs.py
new file mode 100644
index 0000000..044c8a3
--- /dev/null
+++ b/TestON/tests/FUNC/FUNCformCluster/dependencies/formClusterFuncs.py
@@ -0,0 +1,64 @@
+"""
+Copyright 2017 Open Networking Foundation ( ONF )
+
+Please refer questions to either the onos test mailing list at <onos-test@onosproject.org>,
+the System Testing Plans and Results wiki page at <https://wiki.onosproject.org/x/voMg>,
+or the System Testing Guide page at <https://wiki.onosproject.org/x/WYQg>
+
+    TestON is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 2 of the License, or
+    ( at your option ) any later version.
+
+    TestON is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with TestON.  If not, see <http://www.gnu.org/licenses/>.
+"""
+import json
+
+def checkingNumNodes( main, expected ):
+    """
+    check the number of nodes
+    :param expected:
+        Expected number of nodes
+    :return:
+        main.TRUE if all the number of the nodes are matched
+        main.FALSE if not.
+    """
+    result = main.TRUE
+    for cluster in main.Cluster.active():
+        actual = json.loads( cluster.CLI.summary() ).get( 'nodes' )
+        thisResult = main.TRUE if expected == actual else main.FALSE
+        if not thisResult:
+            main.log.error( "Number of the nodes not matched." +
+                            "\nExpected nodes: " + str( expected ) +
+                            "\nActual nodes: " + str( actual ) )
+    return result
+
+def checkingApp( main, appToBeChecked, cluster, expectedToBeThere ):
+    """
+    check the existence of app
+    :param appToBeChecked:
+        Name of the apps to be checked
+    :param cluster:
+        nth cluster to be checked
+    :param expectedToBeThere:
+        True if it is expected to be installed. False if it is expected not to be installed.
+    :return:
+        main.TRUE if they are all matched. Otherwise main.FALSE
+    """
+    result = False
+    appStatus = cluster.CLI.appStatus( appToBeChecked )
+    if appStatus == "ACTIVE" if expectedToBeThere else "UNINSTALL":
+        result = True
+    if result:
+        main.log.info( "App is " + ( "not " if not expectedToBeThere else "" ) + "there as expected" )
+        return main.TRUE
+    else:
+        main.log.error("App is " + ( "" if not expectedToBeThere else "not " ) + "there which should" +
+                       ( "n't" if not expectedToBeThere else "" ) + " be there.")
+        return main.FALSE
diff --git a/TestON/tests/HA/HAcontinuousStopNodes/HAcontinuousStopNodes.py b/TestON/tests/HA/HAcontinuousStopNodes/HAcontinuousStopNodes.py
index 6f3f262..8238b94 100644
--- a/TestON/tests/HA/HAcontinuousStopNodes/HAcontinuousStopNodes.py
+++ b/TestON/tests/HA/HAcontinuousStopNodes/HAcontinuousStopNodes.py
@@ -170,9 +170,8 @@
                                  onfail="ONOS nodes NOT successfully stopped" )
 
         main.step( "Checking ONOS nodes" )
-        nodeResults = utilities.retry( main.HA.nodesCheck,
+        nodeResults = utilities.retry( main.Cluster.nodesCheck,
                                        False,
-                                       args=[ main.Cluster.active() ],
                                        sleep=15,
                                        attempts=5 )
 
diff --git a/TestON/tests/HA/HAfullNetPartition/HAfullNetPartition.py b/TestON/tests/HA/HAfullNetPartition/HAfullNetPartition.py
index 12c2688..13424ed 100644
--- a/TestON/tests/HA/HAfullNetPartition/HAfullNetPartition.py
+++ b/TestON/tests/HA/HAfullNetPartition/HAfullNetPartition.py
@@ -243,9 +243,8 @@
             main.cleanAndExit()
         """
         main.step( "Checking ONOS nodes" )
-        nodeResults = utilities.retry( main.HA.nodesCheck,
+        nodeResults = utilities.retry( main.Cluster.nodesCheck,
                                        False,
-                                       args=[ main.Cluster.active() ],
                                        sleep=15,
                                        attempts=5 )
 
diff --git a/TestON/tests/HA/HAkillNodes/HAkillNodes.py b/TestON/tests/HA/HAkillNodes/HAkillNodes.py
index 30156af..1c82e88 100644
--- a/TestON/tests/HA/HAkillNodes/HAkillNodes.py
+++ b/TestON/tests/HA/HAkillNodes/HAkillNodes.py
@@ -160,9 +160,8 @@
                                  onfail="ONOS nodes NOT successfully killed" )
 
         main.step( "Checking ONOS nodes" )
-        nodeResults = utilities.retry( main.HA.nodesCheck,
+        nodeResults = utilities.retry( main.Cluster.nodesCheck,
                                        False,
-                                       args=[ main.Cluster.active() ],
                                        sleep=15,
                                        attempts=5 )
 
diff --git a/TestON/tests/HA/HAscaling/HAscaling.py b/TestON/tests/HA/HAscaling/HAscaling.py
index d0f81b6..d4b7b32 100644
--- a/TestON/tests/HA/HAscaling/HAscaling.py
+++ b/TestON/tests/HA/HAscaling/HAscaling.py
@@ -202,9 +202,8 @@
         main.Cluster.startCLIs()
 
         main.step( "Checking ONOS nodes" )
-        nodeResults = utilities.retry( main.HA.nodesCheck,
+        nodeResults = utilities.retry( main.Cluster.nodesCheck,
                                        False,
-                                       args=[ main.Cluster.active() ],
                                        attempts=5 )
         utilities.assert_equals( expect=True, actual=nodeResults,
                                  onpass="Nodes check successful",
diff --git a/TestON/tests/HA/HAsingleInstanceRestart/HAsingleInstanceRestart.py b/TestON/tests/HA/HAsingleInstanceRestart/HAsingleInstanceRestart.py
index 4b44f34..74b2fc1 100644
--- a/TestON/tests/HA/HAsingleInstanceRestart/HAsingleInstanceRestart.py
+++ b/TestON/tests/HA/HAsingleInstanceRestart/HAsingleInstanceRestart.py
@@ -715,9 +715,8 @@
                                  onpass="Topology Check Test successful",
                                  onfail="Topology Check Test NOT successful" )
         main.step( "Checking ONOS nodes" )
-        nodeResults = utilities.retry( main.HA.nodesCheck,
+        nodeResults = utilities.retry( main.Cluster.nodesCheck,
                                        False,
-                                       args=[ main.Cluster.active() ],
                                        attempts=5 )
 
         utilities.assert_equals( expect=True, actual=nodeResults,
diff --git a/TestON/tests/HA/HAstopNodes/HAstopNodes.py b/TestON/tests/HA/HAstopNodes/HAstopNodes.py
index be22160..2884535 100644
--- a/TestON/tests/HA/HAstopNodes/HAstopNodes.py
+++ b/TestON/tests/HA/HAstopNodes/HAstopNodes.py
@@ -159,9 +159,8 @@
                                  onfail="ONOS nodes NOT successfully stopped" )
 
         main.step( "Checking ONOS nodes" )
-        nodeResults = utilities.retry( main.HA.nodesCheck,
+        nodeResults = utilities.retry( main.Cluster.nodesCheck,
                                        False,
-                                       args=[ main.Cluster.active() ],
                                        sleep=15,
                                        attempts=5 )
 
diff --git a/TestON/tests/HA/HAswapNodes/HAswapNodes.py b/TestON/tests/HA/HAswapNodes/HAswapNodes.py
index d4c69e5..b221347 100644
--- a/TestON/tests/HA/HAswapNodes/HAswapNodes.py
+++ b/TestON/tests/HA/HAswapNodes/HAswapNodes.py
@@ -200,9 +200,8 @@
         main.testSetUp.startOnosClis( main.Cluster )
 
         main.step( "Checking ONOS nodes" )
-        nodeResults = utilities.retry( main.HA.nodesCheck,
+        nodeResults = utilities.retry( main.Cluster.nodesCheck,
                                        False,
-                                       args=[ main.Cluster.active() ],
                                        attempts=5 )
         utilities.assert_equals( expect=True, actual=nodeResults,
                                  onpass="Nodes check successful",
diff --git a/TestON/tests/HA/dependencies/HA.py b/TestON/tests/HA/dependencies/HA.py
index 9e7e2d5..c781a52 100644
--- a/TestON/tests/HA/dependencies/HA.py
+++ b/TestON/tests/HA/dependencies/HA.py
@@ -271,39 +271,6 @@
         main.log.error( "Inconsistent leaderboards:" + str( leaderList ) )
         return ( result, leaderList )
 
-    def nodesCheck( self, nodes ):
-        nodesOutput = []
-        results = True
-        threads = []
-        for node in nodes:
-            t = main.Thread( target=node.nodes,
-                             name="nodes-" + str( node ),
-                             args=[] )
-            threads.append( t )
-            t.start()
-
-        for t in threads:
-            t.join()
-            nodesOutput.append( t.result )
-        ips = sorted( main.Cluster.getIps( activeOnly=True ) )
-        for i in nodesOutput:
-            try:
-                current = json.loads( i )
-                activeIps = []
-                currentResult = False
-                for node in current:
-                    if node[ 'state' ] == 'READY':
-                        activeIps.append( node[ 'ip' ] )
-                activeIps.sort()
-                if ips == activeIps:
-                    currentResult = True
-            except ( ValueError, TypeError ):
-                main.log.error( "Error parsing nodes output" )
-                main.log.warn( repr( i ) )
-                currentResult = False
-            results = results and currentResult
-        return results
-
     def generateGraph( self, testName, plotName="Plot-HA", index=2 ):
         # DEPRECATED: ONOSSetup.py now creates these graphs.
 
@@ -329,9 +296,8 @@
             main.ONOSbench.handle.expect( "\$" )
 
         main.step( "Checking ONOS nodes" )
-        nodeResults = utilities.retry( self.nodesCheck,
+        nodeResults = utilities.retry( main.Cluster.nodesCheck,
                                        False,
-                                       args=[ main.Cluster.active() ],
                                        attempts=5 )
 
         utilities.assert_equals( expect=True, actual=nodeResults,
@@ -2746,9 +2712,8 @@
         main.log.debug( "Restart time: " + str( main.restartTime ) )
         # TODO: MAke this configurable. Also, we are breaking the above timer
         main.step( "Checking ONOS nodes" )
-        nodeResults = utilities.retry( self.nodesCheck,
+        nodeResults = utilities.retry( main.Cluster.nodesCheck,
                                        False,
-                                       args=[ main.Cluster.active() ],
                                        sleep=15,
                                        attempts=5 )
 
@@ -3365,9 +3330,8 @@
 
         # FIXME: move this to an ONOS state case
         main.step( "Checking ONOS nodes" )
-        nodeResults = utilities.retry( self.nodesCheck,
+        nodeResults = utilities.retry( main.Cluster.nodesCheck,
                                        False,
-                                       args=[ main.Cluster.active() ],
                                        attempts=5 )
         utilities.assert_equals( expect=True, actual=nodeResults,
                                  onpass="Nodes check successful",
diff --git a/TestON/tests/SCPF/SCPFmastershipFailoverLat/SCPFmastershipFailoverLat.py b/TestON/tests/SCPF/SCPFmastershipFailoverLat/SCPFmastershipFailoverLat.py
index c534841..e591381 100644
--- a/TestON/tests/SCPF/SCPFmastershipFailoverLat/SCPFmastershipFailoverLat.py
+++ b/TestON/tests/SCPF/SCPFmastershipFailoverLat/SCPFmastershipFailoverLat.py
@@ -261,9 +261,8 @@
                 criticalError = True
 
             main.log.info( "Checking ONOS nodes." )
-            nodeResults = utilities.retry( main.HA.nodesCheck,
+            nodeResults = utilities.retry( main.Cluster.nodesCheck,
                                            False,
-                                           args=[ main.Cluster.active() ],
                                            sleep=1,
                                            attempts=3 )
 
diff --git a/TestON/tests/USECASE/SegmentRouting/dependencies/Testcaselib.py b/TestON/tests/USECASE/SegmentRouting/dependencies/Testcaselib.py
index f542554..07b0188 100644
--- a/TestON/tests/USECASE/SegmentRouting/dependencies/Testcaselib.py
+++ b/TestON/tests/USECASE/SegmentRouting/dependencies/Testcaselib.py
@@ -422,7 +422,7 @@
 
         if len( nodes ) < main.Cluster.numCtrls:
 
-            nodeResults = utilities.retry( Testcaselib.nodesCheck,
+            nodeResults = utilities.retry( main.Cluster.nodesCheck,
                                            False,
                                            attempts=5,
                                            sleep=10 )
@@ -478,9 +478,8 @@
                                      onfail="ONOS CLI is not ready" )
 
         main.step( "Checking ONOS nodes" )
-        nodeResults = utilities.retry( Testcaselib.nodesCheck,
+        nodeResults = utilities.retry( main.Cluster.nodesCheck,
                                        False,
-                                       args=[ nodes ],
                                        attempts=5,
                                        sleep=10 )
         utilities.assert_equals( expect=True, actual=nodeResults,
@@ -587,28 +586,3 @@
         main.Cluster.active( 0 ).REST.removeNetCfg( subjectClass="apps",
                                                     subjectKey="org.onosproject.segmentrouting",
                                                     configKey="xconnect" )
-
-    @staticmethod
-    def nodesCheck( nodes ):
-        results = True
-        nodesOutput = main.Cluster.command( "nodes", specificDriver=2 )
-        ips = sorted( main.Cluster.getIps( activeOnly=True ) )
-        for i in nodesOutput:
-            try:
-                current = json.loads( i )
-                activeIps = []
-                currentResult = False
-                for node in current:
-                    if node[ 'state' ] == 'READY':
-                        activeIps.append( node[ 'ip' ] )
-                currentResult = True
-                for ip in ips:
-                    if ip not in activeIps:
-                        currentResult = False
-                        break
-            except ( ValueError, TypeError ):
-                main.log.error( "Error parsing nodes output" )
-                main.log.warn( repr( i ) )
-                currentResult = False
-            results = results and currentResult
-        return results
diff --git a/TestON/tests/USECASE/VPLS/VPLSfailsafe/VPLSfailsafe.py b/TestON/tests/USECASE/VPLS/VPLSfailsafe/VPLSfailsafe.py
index a7dfa3b..9778b68 100644
--- a/TestON/tests/USECASE/VPLS/VPLSfailsafe/VPLSfailsafe.py
+++ b/TestON/tests/USECASE/VPLS/VPLSfailsafe/VPLSfailsafe.py
@@ -421,9 +421,8 @@
 
             # Checking if all nodes appear with status READY using 'nodes' command
             main.step( "Checking ONOS nodes." )
-            nodeResults = utilities.retry( main.HA.nodesCheck,
+            nodeResults = utilities.retry( main.Cluster.nodesCheck,
                                            False,
-                                           args=[ main.Cluster.runningNodes ],
                                            sleep=main.timeSleep,
                                            attempts=main.numAttempts )
 
diff --git a/TestON/tests/dependencies/Cluster.py b/TestON/tests/dependencies/Cluster.py
index 210134f..b0af55e 100644
--- a/TestON/tests/dependencies/Cluster.py
+++ b/TestON/tests/dependencies/Cluster.py
@@ -18,6 +18,7 @@
     You should have received a copy of the GNU General Public License
     along with TestON.  If not, see <http://www.gnu.org/licenses/>.
 """
+import json
 class Cluster():
 
     def __str__( self ):
@@ -339,6 +340,28 @@
             self.controllers[ i ].active = True
         return result
 
+    def nodesCheck( self ):
+        results = True
+        nodesOutput = self.command( "nodes", specificDriver=2 )
+        ips = sorted( self.getIps( activeOnly=True ) )
+        for i in nodesOutput:
+            try:
+                current = json.loads( i )
+                activeIps = []
+                currentResult = False
+                for node in current:
+                    if node[ 'state' ] == 'READY':
+                        activeIps.append( node[ 'ip' ] )
+                activeIps.sort()
+                if ips == activeIps:
+                    currentResult = True
+            except ( ValueError, TypeError ):
+                main.log.error( "Error parsing nodes output" )
+                main.log.warn( repr( i ) )
+                currentResult = False
+            results = results and currentResult
+        return results
+
     def printResult( self, results, activeList, logLevel="debug" ):
         """
         Description: