You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@lucene.apache.org by no...@apache.org on 2018/11/06 11:30:46 UTC

lucene-solr:branch_7x: SOLR-11997: Suggestions API/UI should show an entry where a violation could not be resolved

Repository: lucene-solr
Updated Branches:
  refs/heads/branch_7x 7ddaff6f8 -> 9c5626d68


SOLR-11997: Suggestions API/UI should show an entry where a violation could not be resolved


Project: http://git-wip-us.apache.org/repos/asf/lucene-solr/repo
Commit: http://git-wip-us.apache.org/repos/asf/lucene-solr/commit/9c5626d6
Tree: http://git-wip-us.apache.org/repos/asf/lucene-solr/tree/9c5626d6
Diff: http://git-wip-us.apache.org/repos/asf/lucene-solr/diff/9c5626d6

Branch: refs/heads/branch_7x
Commit: 9c5626d6862bd5833b3e6fdd502746a1e6dfd9b7
Parents: 7ddaff6
Author: Noble Paul <no...@apache.org>
Authored: Tue Nov 6 22:27:26 2018 +1100
Committer: Noble Paul <no...@apache.org>
Committed: Tue Nov 6 22:30:31 2018 +1100

----------------------------------------------------------------------
 solr/CHANGES.txt                                |   2 +
 .../cloud/autoscaling/AddReplicaSuggester.java  |   2 +-
 .../client/solrj/cloud/autoscaling/Clause.java  |   3 +
 .../cloud/autoscaling/MoveReplicaSuggester.java |   2 +-
 .../solrj/cloud/autoscaling/PolicyHelper.java   |  16 +-
 .../solrj/cloud/autoscaling/Suggester.java      |  24 ++-
 .../solrj/cloud/autoscaling/Suggestion.java     |   2 +-
 .../autoscaling/testUnresolvedSuggestion.json   | 212 +++++++++++++++++++
 .../solrj/cloud/autoscaling/TestPolicy2.java    |  47 ++--
 .../angular/controllers/cluster-suggestions.js  |   4 +-
 .../web/partials/cluster_suggestions.html       |  10 +-
 11 files changed, 289 insertions(+), 35 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/9c5626d6/solr/CHANGES.txt
----------------------------------------------------------------------
diff --git a/solr/CHANGES.txt b/solr/CHANGES.txt
index 8e25a4d..14a9f08 100644
--- a/solr/CHANGES.txt
+++ b/solr/CHANGES.txt
@@ -92,6 +92,8 @@ New Features
 
 * SOLR-12938: Cluster Status returns results for aliases, instead of throwing exceptions (Gus Heck)
 
+* SOLR-11997: Suggestions API/UI should show an entry where a violation could not be resolved (noble)
+
 Other Changes
 ----------------------
 

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/9c5626d6/solr/solrj/src/java/org/apache/solr/client/solrj/cloud/autoscaling/AddReplicaSuggester.java
----------------------------------------------------------------------
diff --git a/solr/solrj/src/java/org/apache/solr/client/solrj/cloud/autoscaling/AddReplicaSuggester.java b/solr/solrj/src/java/org/apache/solr/client/solrj/cloud/autoscaling/AddReplicaSuggester.java
index 2bb214c..87b831a 100644
--- a/solr/solrj/src/java/org/apache/solr/client/solrj/cloud/autoscaling/AddReplicaSuggester.java
+++ b/solr/solrj/src/java/org/apache/solr/client/solrj/cloud/autoscaling/AddReplicaSuggester.java
@@ -49,7 +49,7 @@ class AddReplicaSuggester extends Suggester {
       Row bestNode = null;
       for (int i = getMatrix().size() - 1; i >= 0; i--) {
         Row row = getMatrix().get(i);
-        if (!isNodeSuitableForReplicaAddition(row)) continue;
+        if (!isNodeSuitableForReplicaAddition(row, null)) continue;
         Row tmpRow = row.addReplica(shard.first(), shard.second(), type, strict);
         List<Violation> errs = testChangedMatrix(strict, tmpRow.session);
         if (!containsNewErrors(errs)) {

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/9c5626d6/solr/solrj/src/java/org/apache/solr/client/solrj/cloud/autoscaling/Clause.java
----------------------------------------------------------------------
diff --git a/solr/solrj/src/java/org/apache/solr/client/solrj/cloud/autoscaling/Clause.java b/solr/solrj/src/java/org/apache/solr/client/solrj/cloud/autoscaling/Clause.java
index 2138ee8..4185312 100644
--- a/solr/solrj/src/java/org/apache/solr/client/solrj/cloud/autoscaling/Clause.java
+++ b/solr/solrj/src/java/org/apache/solr/client/solrj/cloud/autoscaling/Clause.java
@@ -127,6 +127,9 @@ public class Clause implements MapWriter, Comparable<Clause> {
     hasComputedValue = hasComputedValue();
   }
 
+  public Condition getThirdTag() {
+    return globalTag == null ? tag : globalTag;
+  }
   private void doPostValidate(Condition... conditions) {
     for (Condition condition : conditions) {
       if (condition == null) continue;

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/9c5626d6/solr/solrj/src/java/org/apache/solr/client/solrj/cloud/autoscaling/MoveReplicaSuggester.java
----------------------------------------------------------------------
diff --git a/solr/solrj/src/java/org/apache/solr/client/solrj/cloud/autoscaling/MoveReplicaSuggester.java b/solr/solrj/src/java/org/apache/solr/client/solrj/cloud/autoscaling/MoveReplicaSuggester.java
index ba59eec..40dacdd 100644
--- a/solr/solrj/src/java/org/apache/solr/client/solrj/cloud/autoscaling/MoveReplicaSuggester.java
+++ b/solr/solrj/src/java/org/apache/solr/client/solrj/cloud/autoscaling/MoveReplicaSuggester.java
@@ -60,7 +60,7 @@ public class MoveReplicaSuggester extends Suggester {
       for (int j = session.matrix.size() - 1; j >= stopAt; j--) {
         targetRow = session.matrix.get(j);
         if (targetRow.node.equals(fromRow.node)) continue;
-        if (!isNodeSuitableForReplicaAddition(targetRow)) continue;
+        if (!isNodeSuitableForReplicaAddition(targetRow, fromRow)) continue;
         targetRow = targetRow.addReplica(ri.getCollection(), ri.getShard(), ri.getType(), strict); // add replica to target first
         Row srcRowModified = targetRow.session.getNode(fromRow.node).removeReplica(ri.getCollection(), ri.getShard(), ri.getType());//then remove replica from source node
         List<Violation> errs = testChangedMatrix(strict, srcRowModified.session);

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/9c5626d6/solr/solrj/src/java/org/apache/solr/client/solrj/cloud/autoscaling/PolicyHelper.java
----------------------------------------------------------------------
diff --git a/solr/solrj/src/java/org/apache/solr/client/solrj/cloud/autoscaling/PolicyHelper.java b/solr/solrj/src/java/org/apache/solr/client/solrj/cloud/autoscaling/PolicyHelper.java
index 3cf10df..f09d694 100644
--- a/solr/solrj/src/java/org/apache/solr/client/solrj/cloud/autoscaling/PolicyHelper.java
+++ b/solr/solrj/src/java/org/apache/solr/client/solrj/cloud/autoscaling/PolicyHelper.java
@@ -237,14 +237,20 @@ public class PolicyHelper {
     ctx.session = policy.createSession(cloudManager);
     List<Violation> violations = ctx.session.getViolations();
     for (Violation violation : violations) {
-      String name = violation.getClause().isPerCollectiontag() ?
-          violation.getClause().tag.name :
-          violation.getClause().globalTag.name;
-      Variable.Type tagType = VariableBase.getTagType(name);
-      tagType.getSuggestions(ctx.setViolation(violation));
+      violation.getClause().getThirdTag().varType.getSuggestions(ctx.setViolation(violation));
       ctx.violation = null;
     }
 
+    for (Violation current : ctx.session.getViolations()) {
+      for (Violation old : violations) {
+        if (current.equals(old)) {
+          //could not be resolved
+          ctx.suggestions.add(new Suggester.SuggestionInfo(current, null, "unresolved-violation"));
+          break;
+        }
+      }
+    }
+
     if (ctx.needMore()) {
       try {
         addMissingReplicas(cloudManager, ctx);

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/9c5626d6/solr/solrj/src/java/org/apache/solr/client/solrj/cloud/autoscaling/Suggester.java
----------------------------------------------------------------------
diff --git a/solr/solrj/src/java/org/apache/solr/client/solrj/cloud/autoscaling/Suggester.java b/solr/solrj/src/java/org/apache/solr/client/solrj/cloud/autoscaling/Suggester.java
index f052ae9..dfda8a2 100644
--- a/solr/solrj/src/java/org/apache/solr/client/solrj/cloud/autoscaling/Suggester.java
+++ b/solr/solrj/src/java/org/apache/solr/client/solrj/cloud/autoscaling/Suggester.java
@@ -121,10 +121,26 @@ public abstract class Suggester implements MapWriter {
     return this;
   }
 
-  protected boolean isNodeSuitableForReplicaAddition(Row row) {
-    if (!row.isLive) return false;
-    if (!isAllowed(row.node, Hint.TARGET_NODE)) return false;
-    if (!isAllowed(row.getVal(ImplicitSnitch.DISK), Hint.MINFREEDISK)) return false;
+  protected boolean isNodeSuitableForReplicaAddition(Row targetRow, Row srcRow) {
+    if (!targetRow.isLive) return false;
+    if (!isAllowed(targetRow.node, Hint.TARGET_NODE)) return false;
+    if (!isAllowed(targetRow.getVal(ImplicitSnitch.DISK), Hint.MINFREEDISK)) return false;
+
+    if (srcRow != null) {// if the src row has the same violation it's not
+      for (Violation v1 : originalViolations) {
+        if (!v1.getClause().getThirdTag().varType.meta.isNodeSpecificVal()) continue;
+        if (v1.getClause().hasComputedValue) continue;
+        if (targetRow.node.equals(v1.node)) {
+          for (Violation v2 : originalViolations) {
+            if (srcRow.node.equals(v2.node)) {
+              if (v1.getClause().equals(v2.getClause()))
+                return false;
+            }
+          }
+        }
+      }
+    }
+
     return true;
   }
 

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/9c5626d6/solr/solrj/src/java/org/apache/solr/client/solrj/cloud/autoscaling/Suggestion.java
----------------------------------------------------------------------
diff --git a/solr/solrj/src/java/org/apache/solr/client/solrj/cloud/autoscaling/Suggestion.java b/solr/solrj/src/java/org/apache/solr/client/solrj/cloud/autoscaling/Suggestion.java
index 45d4582..b470985 100644
--- a/solr/solrj/src/java/org/apache/solr/client/solrj/cloud/autoscaling/Suggestion.java
+++ b/solr/solrj/src/java/org/apache/solr/client/solrj/cloud/autoscaling/Suggestion.java
@@ -35,7 +35,7 @@ public class Suggestion {
     int max = Integer.MAX_VALUE;
     public Policy.Session session;
     public Violation violation;
-    private List<Suggester.SuggestionInfo> suggestions = new ArrayList<>();
+    List<Suggester.SuggestionInfo> suggestions = new ArrayList<>();
     SolrRequest addSuggestion(Suggester suggester) {
       return addSuggestion(suggester, "violation");
     }

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/9c5626d6/solr/solrj/src/test-files/solrj/solr/autoscaling/testUnresolvedSuggestion.json
----------------------------------------------------------------------
diff --git a/solr/solrj/src/test-files/solrj/solr/autoscaling/testUnresolvedSuggestion.json b/solr/solrj/src/test-files/solrj/solr/autoscaling/testUnresolvedSuggestion.json
new file mode 100644
index 0000000..19401de
--- /dev/null
+++ b/solr/solrj/src/test-files/solrj/solr/autoscaling/testUnresolvedSuggestion.json
@@ -0,0 +1,212 @@
+{
+  "diagnostics": {
+    "sortedNodes": [
+      {
+        "node": "10.0.0.80:7574_solr",
+        "isLive": true,
+        "cores": 2.0,
+        "freedisk": 661.6284255981445,
+        "totaldisk": 1037.938980102539,
+        "replicas": {
+          "go": {
+            "shard2": [
+              {
+                "core_node7": {
+                  "core": "go_shard2_replica_n4",
+                  "shard": "shard2",
+                  "collection": "go",
+                  "node_name": "10.0.0.80:7574_solr",
+                  "type": "NRT",
+                  "base_url": "http://10.0.0.80:7574/solr",
+                  "state": "active",
+                  "force_set_state": "false",
+                  "INDEX.sizeInGB": 6.426125764846802E-8
+                }
+              },
+              {
+                "core_node9": {
+                  "core": "go_shard2_replica_n6",
+                  "shard": "shard2",
+                  "collection": "go",
+                  "node_name": "10.0.0.80:7574_solr",
+                  "type": "NRT",
+                  "leader": "true",
+                  "base_url": "http://10.0.0.80:7574/solr",
+                  "state": "active",
+                  "force_set_state": "false",
+                  "INDEX.sizeInGB": 6.426125764846802E-8
+                }
+              }
+            ]
+          }
+        }
+      },
+      {
+        "node": "10.0.0.80:8983_solr",
+        "isLive": true,
+        "cores": 2.0,
+        "freedisk": 661.6284217834473,
+        "totaldisk": 1037.938980102539,
+        "replicas": {
+          "go": {
+            "shard3": [
+              {
+                "core_node11": {
+                  "core": "go_shard3_replica_n8",
+                  "shard": "shard3",
+                  "collection": "go",
+                  "node_name": "10.0.0.80:8983_solr",
+                  "type": "NRT",
+                  "leader": "true",
+                  "base_url": "http://10.0.0.80:8983/solr",
+                  "state": "active",
+                  "force_set_state": "false",
+                  "INDEX.sizeInGB": 6.426125764846802E-8
+                }
+              }
+            ],
+            "shard1": [
+              {
+                "core_node5": {
+                  "core": "go_shard1_replica_n2",
+                  "shard": "shard1",
+                  "collection": "go",
+                  "node_name": "10.0.0.80:8983_solr",
+                  "type": "NRT",
+                  "leader": "true",
+                  "base_url": "http://10.0.0.80:8983/solr",
+                  "state": "active",
+                  "force_set_state": "false",
+                  "INDEX.sizeInGB": 6.426125764846802E-8
+                }
+              }
+            ]
+          }
+        }
+      },
+      {
+        "node": "10.0.0.80:8984_solr",
+        "isLive": true,
+        "cores": 2.0,
+        "freedisk": 661.6284217834473,
+        "totaldisk": 1037.938980102539,
+        "replicas": {
+          "go": {
+            "shard3": [
+              {
+                "core_node12": {
+                  "core": "go_shard3_replica_n10",
+                  "shard": "shard3",
+                  "collection": "go",
+                  "node_name": "10.0.0.80:8984_solr",
+                  "type": "NRT",
+                  "base_url": "http://10.0.0.80:8984/solr",
+                  "state": "active",
+                  "force_set_state": "false",
+                  "INDEX.sizeInGB": 6.426125764846802E-8
+                }
+              }
+            ],
+            "shard1": [
+              {
+                "core_node3": {
+                  "core": "go_shard1_replica_n1",
+                  "shard": "shard1",
+                  "collection": "go",
+                  "node_name": "10.0.0.80:8984_solr",
+                  "type": "NRT",
+                  "base_url": "http://10.0.0.80:8984/solr",
+                  "state": "active",
+                  "force_set_state": "false",
+                  "INDEX.sizeInGB": 6.426125764846802E-8
+                }
+              }
+            ]
+          }
+        }
+      }
+    ],
+    "liveNodes": [
+      "10.0.0.80:7574_solr",
+      "10.0.0.80:8983_solr",
+      "10.0.0.80:8984_solr"
+    ],
+    "config": {
+      "cluster-preferences": [{"minimize": "cores"}],
+      "cluster-policy": [{"replica": "<2", "node": "#ANY"}]
+    }
+  },
+  "cluster":{
+    "collections":{
+      "go":{
+        "pullReplicas":"0",
+        "replicationFactor":"2",
+        "shards":{
+          "shard1":{
+            "range":"80000000-d554ffff",
+            "state":"active",
+            "replicas":{
+              "core_node3":{
+                "core":"go_shard1_replica_n1",
+                "base_url":"http://10.0.0.80:8984/solr",
+                "node_name":"10.0.0.80:8984_solr",
+                "state":"active",
+                "type":"NRT",
+                "force_set_state":"false"},
+              "core_node5":{
+                "core":"go_shard1_replica_n2",
+                "base_url":"http://10.0.0.80:8983/solr",
+                "node_name":"10.0.0.80:8983_solr",
+                "state":"active",
+                "type":"NRT",
+                "force_set_state":"false",
+                "leader":"true"}}},
+          "shard2":{
+            "range":"d5550000-2aa9ffff",
+            "state":"active",
+            "replicas":{
+              "core_node7":{
+                "core":"go_shard2_replica_n4",
+                "base_url":"http://10.0.0.80:7574/solr",
+                "node_name":"10.0.0.80:7574_solr",
+                "state":"active",
+                "type":"NRT",
+                "force_set_state":"false"},
+              "core_node9":{
+                "core":"go_shard2_replica_n6",
+                "base_url":"http://10.0.0.80:7574/solr",
+                "node_name":"10.0.0.80:7574_solr",
+                "state":"active",
+                "type":"NRT",
+                "force_set_state":"false",
+                "leader":"true"}}},
+          "shard3":{
+            "range":"2aaa0000-7fffffff",
+            "state":"active",
+            "replicas":{
+              "core_node11":{
+                "core":"go_shard3_replica_n8",
+                "base_url":"http://10.0.0.80:8983/solr",
+                "node_name":"10.0.0.80:8983_solr",
+                "state":"active",
+                "type":"NRT",
+                "force_set_state":"false",
+                "leader":"true"},
+              "core_node12":{
+                "core":"go_shard3_replica_n10",
+                "base_url":"http://10.0.0.80:8984/solr",
+                "node_name":"10.0.0.80:8984_solr",
+                "state":"active",
+                "type":"NRT",
+                "force_set_state":"false"}}}},
+        "router":{"name":"compositeId"},
+        "maxShardsPerNode":"-1",
+        "autoAddReplicas":"false",
+        "nrtReplicas":"2",
+        "tlogReplicas":"0",
+        "znodeVersion":9,
+        "configName":"go"}},
+    "live_nodes":["10.0.0.80:8983_solr",
+      "10.0.0.80:7574_solr",
+      "10.0.0.80:8984_solr"]}
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/9c5626d6/solr/solrj/src/test/org/apache/solr/client/solrj/cloud/autoscaling/TestPolicy2.java
----------------------------------------------------------------------
diff --git a/solr/solrj/src/test/org/apache/solr/client/solrj/cloud/autoscaling/TestPolicy2.java b/solr/solrj/src/test/org/apache/solr/client/solrj/cloud/autoscaling/TestPolicy2.java
index cbcf3cd..cf01a6b 100644
--- a/solr/solrj/src/test/org/apache/solr/client/solrj/cloud/autoscaling/TestPolicy2.java
+++ b/solr/solrj/src/test/org/apache/solr/client/solrj/cloud/autoscaling/TestPolicy2.java
@@ -49,6 +49,7 @@ import org.slf4j.LoggerFactory;
 import static java.util.Collections.EMPTY_MAP;
 import static java.util.Collections.emptyMap;
 import static org.apache.solr.client.solrj.cloud.autoscaling.Variable.Type.CORES;
+import static org.apache.solr.common.util.Utils.getObjectByPath;
 
 public class TestPolicy2 extends SolrTestCaseJ4 {
   private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
@@ -200,7 +201,7 @@ public class TestPolicy2 extends SolrTestCaseJ4 {
             for (String tag : tags) {
               if (tag.equals(CORES.tagName))
                 result.put(CORES.tagName, coreCount.getOrDefault(node, new AtomicInteger(0)).get());
-              result.put(tag, Utils.getObjectByPath(nodeVals, true, Arrays.asList(node, tag)));
+              result.put(tag, getObjectByPath(nodeVals, true, Arrays.asList(node, tag)));
             }
             return result;
           }
@@ -227,19 +228,20 @@ public class TestPolicy2 extends SolrTestCaseJ4 {
     };
   }
 
-  public void testAutoScalingHandlerFailure() throws IOException {
+  public void testAutoScalingHandlerFailure() {
     Map<String, Object> m = (Map<String, Object>) loadFromResource("testAutoScalingHandlerFailure.json");
 
-    Policy policy = new Policy((Map<String, Object>) Utils.getObjectByPath(m, false, "diagnostics/config"));
+    Policy policy = new Policy((Map<String, Object>) getObjectByPath(m, false, "diagnostics/config"));
     SolrCloudManager cloudManagerFromDiagnostics = createCloudManagerFromDiagnostics(m);
     Policy.Session session = policy.createSession(cloudManagerFromDiagnostics);
-    List<Suggester.SuggestionInfo> suggestions = PolicyHelper.getSuggestions(new AutoScalingConfig((Map<String, Object>) Utils.getObjectByPath(m, false, "diagnostics/config")), cloudManagerFromDiagnostics);
-    assertEquals(2, suggestions.size());
+    List<Suggester.SuggestionInfo> suggestions = PolicyHelper.getSuggestions(new AutoScalingConfig((Map<String, Object>) getObjectByPath(m, false, "diagnostics/config")), cloudManagerFromDiagnostics);
+//    System.out.println(Utils.writeJson(suggestions, new StringWriter(),true).toString());
+    assertEquals(4, suggestions.size());
 
   }
 
   static SolrCloudManager createCloudManagerFromDiagnostics(Map<String, Object> m) {
-    List<Map> sortedNodes = (List<Map>) Utils.getObjectByPath(m, false, "diagnostics/sortedNodes");
+    List<Map> sortedNodes = (List<Map>) getObjectByPath(m, false, "diagnostics/sortedNodes");
     Set<String> liveNodes = new HashSet<>();
     SolrClientNodeStateProvider nodeStateProvider = new SolrClientNodeStateProvider(null) {
       @Override
@@ -283,7 +285,7 @@ public class TestPolicy2 extends SolrTestCaseJ4 {
       @Override
       public ClusterStateProvider getClusterStateProvider() {
         if (clusterState == null) {
-          Map map = (Map) Utils.getObjectByPath (m, false, "cluster/collections");
+          Map map = (Map) getObjectByPath(m, false, "cluster/collections");
           if (map == null) map = new HashMap<>();
           clusterState = ClusterState.load(0, map, liveNodes, "/clusterstate.json");
         }
@@ -311,7 +313,7 @@ public class TestPolicy2 extends SolrTestCaseJ4 {
 
   public void testHostAttribute() {
     Map<String, Object> m = (Map<String, Object>) loadFromResource("testHostAttribute.json");
-    Map<String, Object> conf = (Map<String, Object>) Utils.getObjectByPath(m, false, "diagnostics/config");
+    Map<String, Object> conf = (Map<String, Object>) getObjectByPath(m, false, "diagnostics/config");
     Policy policy = new Policy(conf);
     SolrCloudManager cloudManagerFromDiagnostics = createCloudManagerFromDiagnostics(m);
     Policy.Session session = policy.createSession(cloudManagerFromDiagnostics);
@@ -331,7 +333,7 @@ public class TestPolicy2 extends SolrTestCaseJ4 {
 
     Map<String, Object> m = (Map<String, Object>) loadFromResource("testSysPropSuggestions.json");
 
-    Map<String, Object> conf = (Map<String, Object>) Utils.getObjectByPath(m, false, "diagnostics/config");
+    Map<String, Object> conf = (Map<String, Object>) getObjectByPath(m, false, "diagnostics/config");
     Policy policy = new Policy(conf);
     SolrCloudManager cloudManagerFromDiagnostics = createCloudManagerFromDiagnostics(m);
     Policy.Session session = policy.createSession(cloudManagerFromDiagnostics);
@@ -349,7 +351,7 @@ public class TestPolicy2 extends SolrTestCaseJ4 {
     }
   }
 
-  public void testSuggestionsRebalanceOnly() throws IOException {
+  public void testSuggestionsRebalanceOnly() {
     String conf = " {" +
         "    'cluster-preferences':[{" +
         "      'minimize':'cores'," +
@@ -370,11 +372,11 @@ public class TestPolicy2 extends SolrTestCaseJ4 {
     assertEquals("127.0.0.1:63219_solr", suggestions.get(1)._get("operation/command/move-replica/targetNode", null));
   }
 
-  public void testSuggestionsRebalance2() throws IOException {
+  public void testSuggestionsRebalance2() {
     Map<String, Object> m = (Map<String, Object>) loadFromResource("testSuggestionsRebalance2.json");
     SolrCloudManager cloudManagerFromDiagnostics = createCloudManagerFromDiagnostics(m);
 
-    AutoScalingConfig autoScalingConfig = new AutoScalingConfig((Map<String, Object>) Utils.getObjectByPath(m, false, "diagnostics/config"));
+    AutoScalingConfig autoScalingConfig = new AutoScalingConfig((Map<String, Object>) getObjectByPath(m, false, "diagnostics/config"));
     List<Suggester.SuggestionInfo> suggestions = PolicyHelper.getSuggestions(autoScalingConfig, cloudManagerFromDiagnostics);
 
     assertEquals(3, suggestions.size());
@@ -386,10 +388,10 @@ public class TestPolicy2 extends SolrTestCaseJ4 {
 
   }
 
-  public void testAddMissingReplica() throws IOException {
+  public void testAddMissingReplica() {
     Map<String, Object> m = (Map<String, Object>) loadFromResource("testAddMissingReplica.json");
     SolrCloudManager cloudManagerFromDiagnostics = createCloudManagerFromDiagnostics(m);
-    AutoScalingConfig autoScalingConfig = new AutoScalingConfig((Map<String, Object>) Utils.getObjectByPath(m, false, "diagnostics/config"));
+    AutoScalingConfig autoScalingConfig = new AutoScalingConfig((Map<String, Object>) getObjectByPath(m, false, "diagnostics/config"));
 
     List<Suggester.SuggestionInfo> suggestions = PolicyHelper.getSuggestions(autoScalingConfig, cloudManagerFromDiagnostics);
 
@@ -401,11 +403,10 @@ public class TestPolicy2 extends SolrTestCaseJ4 {
 
   }
 
-  public void testCreateCollectionWithEmptyPolicy() throws IOException {
+  public void testCreateCollectionWithEmptyPolicy() {
     Map m = (Map) loadFromResource("testCreateCollectionWithEmptyPolicy.json");
     SolrCloudManager cloudManagerFromDiagnostics = createCloudManagerFromDiagnostics(m);
     AutoScalingConfig autoScalingConfig = new AutoScalingConfig(new HashMap());
-    ///Users/noble/work/4solr/solr/core/src/test/org/apache/solr/handler/V2ApiIntegrationTest.java
     //POSITIONS : [shard1:1[NRT] @127.0.0.1:49469_solr, shard1:2[NRT] @127.0.0.1:49469_solr]
     List<ReplicaPosition> positions = PolicyHelper.getReplicaLocations("coll_new", autoScalingConfig, cloudManagerFromDiagnostics,
         EMPTY_MAP, Collections.singletonList("shard1"), 2, 0, 0, null);
@@ -417,6 +418,20 @@ public class TestPolicy2 extends SolrTestCaseJ4 {
 
   }
 
+  public void testUnresolvedSuggestion() {
+    Map<String, Object> m = (Map<String, Object>) loadFromResource("testUnresolvedSuggestion.json");
+
+    SolrCloudManager cloudManagerFromDiagnostics = createCloudManagerFromDiagnostics(m);
+    List<Suggester.SuggestionInfo> suggestions = PolicyHelper.getSuggestions(new AutoScalingConfig((Map<String, Object>) getObjectByPath(m, false, "diagnostics/config"))
+        , cloudManagerFromDiagnostics);
+//    System.out.println(Utils.writeJson(suggestions, new StringWriter(), true).toString());
+    for (Suggester.SuggestionInfo suggestion : suggestions) {
+      assertEquals("unresolved-violation", suggestion._get("type", null));
+      assertEquals("1.0", suggestion._getStr("violation/violation/delta", null));
+    }
+
+
+  }
   public static Object loadFromResource(String file)  {
     try (InputStream is = TestPolicy2.class.getResourceAsStream("/solrj/solr/autoscaling/" + file)) {
       return Utils.fromJSON(is);

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/9c5626d6/solr/webapp/web/js/angular/controllers/cluster-suggestions.js
----------------------------------------------------------------------
diff --git a/solr/webapp/web/js/angular/controllers/cluster-suggestions.js b/solr/webapp/web/js/angular/controllers/cluster-suggestions.js
index 225f7be..01e1964 100644
--- a/solr/webapp/web/js/angular/controllers/cluster-suggestions.js
+++ b/solr/webapp/web/js/angular/controllers/cluster-suggestions.js
@@ -43,10 +43,10 @@ function($scope, $http, Constants) {
             x.loading = false;
             x.done = true;
             x.run=true;
-            $scope.msg = "Post Data Submitted Successfully!";
+            $scope.msg = "Command Submitted Successfully!";
         }, function (response) {
             x.failed=true;
-            $scope.msg = "Service not Exists";
+            $scope.msg = "Service does not exist";
             $scope.statusval = response.status;
             $scope.statustext = response.statusText;
             $scope.headers = response.headers();

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/9c5626d6/solr/webapp/web/partials/cluster_suggestions.html
----------------------------------------------------------------------
diff --git a/solr/webapp/web/partials/cluster_suggestions.html b/solr/webapp/web/partials/cluster_suggestions.html
index cbb05b2..892b0bb 100644
--- a/solr/webapp/web/partials/cluster_suggestions.html
+++ b/solr/webapp/web/partials/cluster_suggestions.html
@@ -22,24 +22,24 @@
         <tr>
             <td>Type</td>
             <td>Reason</td>
+            <td>Details</td>
             <td>Action</td>
         </tr>
         <tr ng-if="parsedData.length==0">
-            <td>NA</td>
-            <td>NA</td>
-            <td>NA</td>
+            <td align="center" colspan="4">NA</td>
         </tr>
         <tr ng-repeat="x in parsedData">
             <td>{{ x.type }}</td>
             <td>{{ x.violation.clause }}</td>
+            <td>node: {{ x.violation.node }}, collection :  {{ x.violation.collection }}, shard :  {{ x.violation.shard }} &nbsp; </td>
             <td>
                 <div class="s-container">
-                    <input class="s-box1" ng-hide="x.run" type="button"
+                    <input class="s-box1" ng-hide="x.run || x.type =='unresolved-violation'" type="button"
                            title="{{ x.operation }}" ng-click="postdata(x)" ng-mouseover="showPopover()"
                            ng-mouseleave="hidePopover()"/>
                     <div class="s-box2" ng-show="x.loading"><img src="img/loader.gif"></div>
                     <div class="s-box3" ng-show="x.done"><img src="img/ico/tick.png"></div>
-                    <div class="s-box4" ng-show="x.failed"><img src="img/ico/cross.png"></div>
+                    <div class="s-box4" ng-show="x.failed || x.type == 'unresolved-violation'"><img src="img/ico/cross.png"></div>
                 </div>
             </td>
         </tr>