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 2017/04/18 09:13:11 UTC

[2/2] lucene-solr:feature/autoscaling: SOLR-10278: moved everything to common package

SOLR-10278: moved everything to common package


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

Branch: refs/heads/feature/autoscaling
Commit: c5a9012d0cecbd4f35112ebd84d7f134c487ba23
Parents: bb48de8
Author: Noble Paul <no...@apache.org>
Authored: Tue Apr 18 18:43:00 2017 +0930
Committer: Noble Paul <no...@apache.org>
Committed: Tue Apr 18 18:43:00 2017 +0930

----------------------------------------------------------------------
 .../cloud/OverseerCollectionMessageHandler.java |   2 +-
 .../solr/cloud/autoscaling/TestPolicyCloud.java |  69 ++++
 .../solr/cloud/policy/TestPolicyCloud.java      |  69 ----
 .../client/solrj/impl/ClientDataProvider.java   |   4 +-
 .../cloud/autoscaling/AddReplicaSuggester.java  |  60 ++++
 .../org/apache/solr/cloud/autoscaling/Cell.java |  50 +++
 .../apache/solr/cloud/autoscaling/Clause.java   | 193 +++++++++++
 .../cloud/autoscaling/ClusterDataProvider.java  |  43 +++
 .../cloud/autoscaling/MoveReplicaSuggester.java |  81 +++++
 .../apache/solr/cloud/autoscaling/Operand.java  | 119 +++++++
 .../apache/solr/cloud/autoscaling/Policy.java   | 333 ++++++++++++++++++
 .../solr/cloud/autoscaling/PolicyHelper.java    |  74 ++++
 .../solr/cloud/autoscaling/Preference.java      |  76 ++++
 .../org/apache/solr/cloud/autoscaling/Row.java  | 113 ++++++
 .../solr/cloud/autoscaling/package-info.java    |  22 ++
 .../apache/solr/recipe/AddReplicaSuggester.java |  60 ----
 .../src/java/org/apache/solr/recipe/Cell.java   |  50 ---
 .../src/java/org/apache/solr/recipe/Clause.java | 195 -----------
 .../apache/solr/recipe/ClusterDataProvider.java |  43 ---
 .../solr/recipe/MoveReplicaSuggester.java       |  81 -----
 .../java/org/apache/solr/recipe/Operand.java    | 119 -------
 .../src/java/org/apache/solr/recipe/Policy.java | 339 ------------------
 .../org/apache/solr/recipe/PolicyHelper.java    |  74 ----
 .../java/org/apache/solr/recipe/Preference.java |  77 -----
 .../src/java/org/apache/solr/recipe/Row.java    | 113 ------
 .../org/apache/solr/recipe/package-info.java    |  22 --
 .../org/apache/solr/autoscaling/TestPolicy.java | 344 +++++++++++++++++++
 .../test/org/apache/solr/recipe/TestPolicy.java | 344 -------------------
 28 files changed, 1580 insertions(+), 1589 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/c5a9012d/solr/core/src/java/org/apache/solr/cloud/OverseerCollectionMessageHandler.java
----------------------------------------------------------------------
diff --git a/solr/core/src/java/org/apache/solr/cloud/OverseerCollectionMessageHandler.java b/solr/core/src/java/org/apache/solr/cloud/OverseerCollectionMessageHandler.java
index b434991..06509fc 100644
--- a/solr/core/src/java/org/apache/solr/cloud/OverseerCollectionMessageHandler.java
+++ b/solr/core/src/java/org/apache/solr/cloud/OverseerCollectionMessageHandler.java
@@ -76,7 +76,7 @@ import org.apache.solr.handler.component.ShardHandler;
 import org.apache.solr.handler.component.ShardHandlerFactory;
 import org.apache.solr.handler.component.ShardRequest;
 import org.apache.solr.handler.component.ShardResponse;
-import org.apache.solr.recipe.PolicyHelper;
+import org.apache.solr.autoscaling.PolicyHelper;
 import org.apache.solr.util.DefaultSolrThreadFactory;
 import org.apache.solr.util.RTimer;
 import org.apache.solr.util.TimeOut;

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/c5a9012d/solr/core/src/test/org/apache/solr/cloud/autoscaling/TestPolicyCloud.java
----------------------------------------------------------------------
diff --git a/solr/core/src/test/org/apache/solr/cloud/autoscaling/TestPolicyCloud.java b/solr/core/src/test/org/apache/solr/cloud/autoscaling/TestPolicyCloud.java
new file mode 100644
index 0000000..ffea699
--- /dev/null
+++ b/solr/core/src/test/org/apache/solr/cloud/autoscaling/TestPolicyCloud.java
@@ -0,0 +1,69 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.solr.cloud.autoscaling;
+
+import java.io.IOException;
+import java.lang.invoke.MethodHandles;
+import java.util.Arrays;
+import java.util.Map;
+
+import org.apache.lucene.util.LuceneTestCase;
+import org.apache.solr.client.solrj.SolrServerException;
+import org.apache.solr.client.solrj.impl.ClientDataProvider;
+import org.apache.solr.client.solrj.request.CollectionAdminRequest;
+import org.apache.solr.cloud.SolrCloudTestCase;
+import org.apache.solr.common.cloud.DocCollection;
+import org.apache.solr.common.util.Utils;
+import org.junit.After;
+import org.junit.BeforeClass;
+import org.junit.rules.ExpectedException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+@LuceneTestCase.Slow
+public class TestPolicyCloud extends SolrCloudTestCase {
+
+  private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
+
+  @BeforeClass
+  public static void setupCluster() throws Exception {
+    configureCluster(5)
+        .addConfig("conf", configset("cloud-minimal"))
+        .configure();
+  }
+
+  @org.junit.Rule
+  public ExpectedException expectedException = ExpectedException.none();
+
+  @After
+  public void removeCollections() throws Exception {
+    cluster.deleteAllCollections();
+  }
+
+
+  public void testDataProvider() throws IOException, SolrServerException {
+    CollectionAdminRequest.createCollectionWithImplicitRouter("policiesTest", "conf", "shard1", 2)
+        .process(cluster.getSolrClient());
+    DocCollection rulesCollection = getCollectionState("policiesTest");
+    ClientDataProvider provider = new ClientDataProvider(cluster.getSolrClient());
+
+    Map<String, Object> val = provider.getNodeValues(rulesCollection.getReplicas().get(0).getNodeName(), Arrays.asList("freedisk", "cores"));
+    assertTrue(((Number) val.get("cores")).intValue() > 0);
+    assertTrue("freedisk value is "+((Number) val.get("freedisk")).longValue() , ((Number) val.get("freedisk")).longValue() > 0);
+    System.out.println(Utils.toJSONString(val));
+  }
+}

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/c5a9012d/solr/core/src/test/org/apache/solr/cloud/policy/TestPolicyCloud.java
----------------------------------------------------------------------
diff --git a/solr/core/src/test/org/apache/solr/cloud/policy/TestPolicyCloud.java b/solr/core/src/test/org/apache/solr/cloud/policy/TestPolicyCloud.java
deleted file mode 100644
index 746ec7c..0000000
--- a/solr/core/src/test/org/apache/solr/cloud/policy/TestPolicyCloud.java
+++ /dev/null
@@ -1,69 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.solr.cloud.policy;
-
-import java.io.IOException;
-import java.lang.invoke.MethodHandles;
-import java.util.Arrays;
-import java.util.Map;
-
-import org.apache.lucene.util.LuceneTestCase;
-import org.apache.solr.client.solrj.SolrServerException;
-import org.apache.solr.client.solrj.impl.ClientDataProvider;
-import org.apache.solr.client.solrj.request.CollectionAdminRequest;
-import org.apache.solr.cloud.SolrCloudTestCase;
-import org.apache.solr.common.cloud.DocCollection;
-import org.apache.solr.common.util.Utils;
-import org.junit.After;
-import org.junit.BeforeClass;
-import org.junit.rules.ExpectedException;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-@LuceneTestCase.Slow
-public class TestPolicyCloud extends SolrCloudTestCase {
-
-  private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
-
-  @BeforeClass
-  public static void setupCluster() throws Exception {
-    configureCluster(5)
-        .addConfig("conf", configset("cloud-minimal"))
-        .configure();
-  }
-
-  @org.junit.Rule
-  public ExpectedException expectedException = ExpectedException.none();
-
-  @After
-  public void removeCollections() throws Exception {
-    cluster.deleteAllCollections();
-  }
-
-
-  public void testDataProvider() throws IOException, SolrServerException {
-    CollectionAdminRequest.createCollectionWithImplicitRouter("policiesTest", "conf", "shard1", 2)
-        .process(cluster.getSolrClient());
-    DocCollection rulesCollection = getCollectionState("policiesTest");
-    ClientDataProvider provider = new ClientDataProvider(cluster.getSolrClient());
-
-    Map<String, Object> val = provider.getNodeValues(rulesCollection.getReplicas().get(0).getNodeName(), Arrays.asList("freedisk", "cores"));
-    assertTrue(((Number) val.get("cores")).intValue() > 0);
-    assertTrue("freedisk value is "+((Number) val.get("freedisk")).longValue() , ((Number) val.get("freedisk")).longValue() > 0);
-    System.out.println(Utils.toJSONString(val));
-  }
-}

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/c5a9012d/solr/solrj/src/java/org/apache/solr/client/solrj/impl/ClientDataProvider.java
----------------------------------------------------------------------
diff --git a/solr/solrj/src/java/org/apache/solr/client/solrj/impl/ClientDataProvider.java b/solr/solrj/src/java/org/apache/solr/client/solrj/impl/ClientDataProvider.java
index f916cd2..e2b2913 100644
--- a/solr/solrj/src/java/org/apache/solr/client/solrj/impl/ClientDataProvider.java
+++ b/solr/solrj/src/java/org/apache/solr/client/solrj/impl/ClientDataProvider.java
@@ -45,8 +45,8 @@ import org.apache.solr.common.params.SolrParams;
 import org.apache.solr.common.util.NamedList;
 import org.apache.solr.common.util.StrUtils;
 import org.apache.solr.common.util.Utils;
-import org.apache.solr.recipe.ClusterDataProvider;
-import org.apache.solr.recipe.Policy.ReplicaInfo;
+import org.apache.solr.autoscaling.ClusterDataProvider;
+import org.apache.solr.autoscaling.Policy.ReplicaInfo;
 import org.apache.zookeeper.data.Stat;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/c5a9012d/solr/solrj/src/java/org/apache/solr/cloud/autoscaling/AddReplicaSuggester.java
----------------------------------------------------------------------
diff --git a/solr/solrj/src/java/org/apache/solr/cloud/autoscaling/AddReplicaSuggester.java b/solr/solrj/src/java/org/apache/solr/cloud/autoscaling/AddReplicaSuggester.java
new file mode 100644
index 0000000..a45280d
--- /dev/null
+++ b/solr/solrj/src/java/org/apache/solr/cloud/autoscaling/AddReplicaSuggester.java
@@ -0,0 +1,60 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.solr.cloud.autoscaling;
+
+import java.util.Map;
+
+import org.apache.solr.common.util.Utils;
+import org.apache.solr.cloud.autoscaling.Policy.Suggester;
+
+import static org.apache.solr.common.cloud.ZkStateReader.COLLECTION_PROP;
+import static org.apache.solr.common.cloud.ZkStateReader.SHARD_ID_PROP;
+import static org.apache.solr.common.params.CollectionParams.CollectionAction.ADDREPLICA;
+import static org.apache.solr.common.params.CoreAdminParams.NODE;
+
+class AddReplicaSuggester extends Suggester {
+
+  Map init() {
+    Map operation = tryEachNode(true);
+    if (operation == null) operation = tryEachNode(false);
+    return operation;
+  }
+
+  Map tryEachNode(boolean strict) {
+    //iterate through elements and identify the least loaded
+    for (int i = getMatrix().size() - 1; i >= 0; i--) {
+      Row row = getMatrix().get(i);
+      String coll = hints.get(Hint.COLL);
+      String shard = hints.get(Hint.SHARD);
+      row = row.addReplica(coll, shard);
+      row.violations.clear();
+      for (Clause clause : session.getPolicy().clauses) {
+        if (strict || clause.strict) clause.test(row);
+      }
+      if (row.violations.isEmpty()) {// there are no rule violations
+        getMatrix().set(i, getMatrix().get(i).addReplica(coll, shard));
+        return Utils.makeMap("operation", ADDREPLICA.toLower(),
+            COLLECTION_PROP, coll,
+            SHARD_ID_PROP, shard,
+            NODE, row.node);
+      }
+    }
+    return null;
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/c5a9012d/solr/solrj/src/java/org/apache/solr/cloud/autoscaling/Cell.java
----------------------------------------------------------------------
diff --git a/solr/solrj/src/java/org/apache/solr/cloud/autoscaling/Cell.java b/solr/solrj/src/java/org/apache/solr/cloud/autoscaling/Cell.java
new file mode 100644
index 0000000..c14490f
--- /dev/null
+++ b/solr/solrj/src/java/org/apache/solr/cloud/autoscaling/Cell.java
@@ -0,0 +1,50 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.solr.cloud.autoscaling;
+
+import java.io.IOException;
+
+import org.apache.solr.common.MapWriter;
+
+class Cell implements MapWriter {
+  final int index;
+  final String name;
+  Object val, val_;
+
+  Cell(int index, String name, Object val) {
+    this.index = index;
+    this.name = name;
+    this.val = val;
+  }
+
+  Cell(int index, String name, Object val, Object val_) {
+    this.index = index;
+    this.name = name;
+    this.val = val;
+    this.val_ = val_;
+  }
+
+  @Override
+  public void writeMap(EntryWriter ew) throws IOException {
+    ew.put(name, val);
+  }
+
+  public Cell copy() {
+    return new Cell(index, name, val, val_);
+  }
+}

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/c5a9012d/solr/solrj/src/java/org/apache/solr/cloud/autoscaling/Clause.java
----------------------------------------------------------------------
diff --git a/solr/solrj/src/java/org/apache/solr/cloud/autoscaling/Clause.java b/solr/solrj/src/java/org/apache/solr/cloud/autoscaling/Clause.java
new file mode 100644
index 0000000..a1f0af1
--- /dev/null
+++ b/solr/solrj/src/java/org/apache/solr/cloud/autoscaling/Clause.java
@@ -0,0 +1,193 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.solr.cloud.autoscaling;
+
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Set;
+import java.util.concurrent.atomic.AtomicReference;
+
+import org.apache.solr.common.MapWriter;
+import org.apache.solr.common.util.Utils;
+import org.apache.solr.cloud.autoscaling.Policy.ReplicaInfo;
+
+import static java.util.Collections.singletonMap;
+import static org.apache.solr.common.params.CoreAdminParams.COLLECTION;
+import static org.apache.solr.common.params.CoreAdminParams.REPLICA;
+import static org.apache.solr.common.params.CoreAdminParams.SHARD;
+import static org.apache.solr.autoscaling.Clause.TestStatus.FAIL;
+import static org.apache.solr.autoscaling.Clause.TestStatus.NOT_APPLICABLE;
+import static org.apache.solr.autoscaling.Clause.TestStatus.PASS;
+import static org.apache.solr.cloud.autoscaling.Operand.EQUAL;
+import static org.apache.solr.cloud.autoscaling.Operand.GREATER_THAN;
+import static org.apache.solr.cloud.autoscaling.Operand.LESS_THAN;
+import static org.apache.solr.cloud.autoscaling.Operand.NOT_EQUAL;
+import static org.apache.solr.cloud.autoscaling.Operand.WILDCARD;
+import static org.apache.solr.cloud.autoscaling.Policy.ANY;
+import static org.apache.solr.cloud.autoscaling.Policy.EACH;
+
+// a set of conditions in a policy
+public class Clause implements MapWriter, Comparable<Clause> {
+  Map<String, Object> original;
+  Condition collection, shard, replica, tag;
+  boolean strict = true;
+
+  Clause(Map<String, Object> m) {
+    this.original = m;
+    collection = parse(COLLECTION, m);
+    shard = parse(SHARD, m);
+    this.replica = parse(REPLICA, m);
+    strict = Boolean.parseBoolean(String.valueOf(m.getOrDefault("strict", "true")));
+    m.forEach((s, o) -> parseCondition(s, o));
+    if (tag == null)
+      throw new RuntimeException("Invalid op, must have one and only one tag other than collection, shard,replica " + Utils.toJSONString(m));
+  }
+
+  void parseCondition(String s, Object o) {
+    if (IGNORE_TAGS.contains(s)) return;
+    if (tag != null) {
+      throw new IllegalArgumentException("Only one tag other than collection, shard, replica is possible");
+    }
+    tag = parse(s, singletonMap(s, o));
+  }
+
+  @Override
+  public int compareTo(Clause that) {
+    int v = Integer.compare(this.tag.op.priority, that.tag.op.priority);
+    if (v != 0) return v;
+    return Integer.compare(this.replica.op.priority, that.replica.op.priority);
+  }
+
+  static class Condition {
+    final String name;
+    final Object val;
+    final Operand op;
+
+    Condition(String name, Object val, Operand op) {
+      this.name = name;
+      this.val = val;
+      this.op = op;
+    }
+
+    TestStatus match(Row row) {
+      return op.match(val, row.getVal(name));
+    }
+
+    boolean isPass(Object inputVal) {
+      return op.match(val, inputVal) == PASS;
+    }
+
+    boolean isPass(Row row) {
+      return op.match(val, row.getVal(name)) == PASS;
+    }
+
+    @Override
+    public boolean equals(Object that) {
+      if (that instanceof Condition) {
+        Condition c = (Condition) that;
+        return Objects.equals(c.name, name) && Objects.equals(c.val, val) && c.op == op;
+      }
+      return false;
+    }
+  }
+
+  static Condition parse(String s, Map m) {
+    Object expectedVal = null;
+    Object val = m.get(s);
+    try {
+      String conditionName = s.trim();
+      String value = val == null ? null : String.valueOf(val).trim();
+      Operand operand = null;
+      if ((expectedVal = WILDCARD.parse(value)) != null) {
+        operand = WILDCARD;
+      } else if ((expectedVal = NOT_EQUAL.parse(value)) != null) {
+        operand = NOT_EQUAL;
+      } else if ((expectedVal = GREATER_THAN.parse(value)) != null) {
+        operand = GREATER_THAN;
+      } else if ((expectedVal = LESS_THAN.parse(value)) != null) {
+        operand = LESS_THAN;
+      } else {
+        operand = EQUAL;
+        expectedVal = EQUAL.parse(value);
+      }
+
+      return new Condition(conditionName, expectedVal, operand);
+
+    } catch (Exception e) {
+      throw new IllegalArgumentException("Invalid tag : " + s + ":" + val, e);
+    }
+  }
+
+
+  TestStatus test(Row row) {
+    AtomicReference<TestStatus> result = new AtomicReference<>(NOT_APPLICABLE);
+
+    for (Map.Entry<String, Map<String, List<ReplicaInfo>>> colls : row.replicaInfo.entrySet()) {
+      if (result.get() == FAIL) break;
+      if (!collection.isPass(colls.getKey())) continue;
+      int count = 0;
+      for (Map.Entry<String, List<ReplicaInfo>> shards : colls.getValue().entrySet()) {
+        if (!shard.isPass(shards.getKey()) || result.get() == FAIL) break;
+        count += shards.getValue().size();
+        if (shard.val.equals(EACH)) testReplicaCount(row, result, count);
+        if (EACH.equals(shard.val)) count = 0;
+        }
+      if (shard.val.equals(ANY)) testReplicaCount(row, result, count);
+      }
+    if (result.get() == FAIL) row.violations.add(this);
+    return result.get();
+
+  }
+
+  private void testReplicaCount(Row row, AtomicReference<TestStatus> result, int count) {
+    if("node".equals(tag.name)) if(!tag.isPass(row.node)) return;
+    boolean checkCount = replica.op.match(replica.val, 0) != PASS || count > 0;
+    if (replica.op == WILDCARD && count > 0 && !tag.isPass(row)) {
+      result.set(FAIL);
+    } else if (checkCount && !replica.isPass(count)) {
+      if (tag.op != WILDCARD && tag.isPass(row)) {
+        result.set(FAIL);
+      } else {
+        result.set(FAIL);
+      }
+    }
+  }
+
+  public boolean isStrict() {
+    return strict;
+  }
+  @Override
+  public String toString() {
+    return Utils.toJSONString(original);
+  }
+
+  @Override
+  public void writeMap(EntryWriter ew) throws IOException {
+    for (Map.Entry<String, Object> e : original.entrySet()) ew.put(e.getKey(), e.getValue());
+  }
+
+  enum TestStatus {
+    NOT_APPLICABLE, FAIL, PASS
+  }
+
+  private static final Set<String> IGNORE_TAGS = new HashSet<>(Arrays.asList(REPLICA, COLLECTION, SHARD, "strict"));
+}

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/c5a9012d/solr/solrj/src/java/org/apache/solr/cloud/autoscaling/ClusterDataProvider.java
----------------------------------------------------------------------
diff --git a/solr/solrj/src/java/org/apache/solr/cloud/autoscaling/ClusterDataProvider.java b/solr/solrj/src/java/org/apache/solr/cloud/autoscaling/ClusterDataProvider.java
new file mode 100644
index 0000000..25d2ef5
--- /dev/null
+++ b/solr/solrj/src/java/org/apache/solr/cloud/autoscaling/ClusterDataProvider.java
@@ -0,0 +1,43 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.solr.cloud.autoscaling;
+
+import java.io.Closeable;
+import java.io.IOException;
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+
+
+public interface ClusterDataProvider extends Closeable {
+  Map<String, Object> getNodeValues(String node, Collection<String> keys);
+
+  /**
+   * Get the details of each replica in a node. It attempts to fetch as much details about
+   * the replica as mentioned in the keys list. It is not necessary to give al details
+   * <p>
+   * the format is {collection:shard :[{replicadetails}]}
+   */
+  Map<String, Map<String, List<Policy.ReplicaInfo>>> getReplicaInfo(String node, Collection<String> keys);
+
+  Collection<String> getNodes();
+
+  @Override
+  default void close() throws IOException {
+  }
+}

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/c5a9012d/solr/solrj/src/java/org/apache/solr/cloud/autoscaling/MoveReplicaSuggester.java
----------------------------------------------------------------------
diff --git a/solr/solrj/src/java/org/apache/solr/cloud/autoscaling/MoveReplicaSuggester.java b/solr/solrj/src/java/org/apache/solr/cloud/autoscaling/MoveReplicaSuggester.java
new file mode 100644
index 0000000..3a99169
--- /dev/null
+++ b/solr/solrj/src/java/org/apache/solr/cloud/autoscaling/MoveReplicaSuggester.java
@@ -0,0 +1,81 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.solr.cloud.autoscaling;
+
+import java.util.Map;
+
+import org.apache.solr.common.util.Pair;
+import org.apache.solr.common.util.Utils;
+import org.apache.solr.cloud.autoscaling.Policy.Suggester;
+
+import static org.apache.solr.common.cloud.ZkStateReader.COLLECTION_PROP;
+import static org.apache.solr.common.cloud.ZkStateReader.SHARD_ID_PROP;
+import static org.apache.solr.common.params.CollectionParams.CollectionAction.MOVEREPLICA;
+import static org.apache.solr.common.params.CoreAdminParams.NODE;
+import static org.apache.solr.common.params.CoreAdminParams.REPLICA;
+
+public class MoveReplicaSuggester extends Suggester {
+
+  @Override
+  Map init() {
+    Map operation = tryEachNode(true);
+    if (operation == null) operation = tryEachNode(false);
+    return operation;
+  }
+
+  Map tryEachNode(boolean strict) {
+    //iterate through elements and identify the least loaded
+    String coll = hints.get(Hint.COLL);
+    String shard = hints.get(Hint.SHARD);
+    for (int i = 0; i < getMatrix().size(); i++) {
+      Row fromRow = getMatrix().get(i);
+      Pair<Row, Policy.ReplicaInfo> pair = fromRow.removeReplica(coll, shard);
+      fromRow = pair.first();
+      if(fromRow == null){
+        //no such replica available
+        continue;
+      }
+
+      for (Clause clause : session.getPolicy().clauses) {
+        if (strict || clause.strict) clause.test(fromRow);
+      }
+      if (fromRow.violations.isEmpty()) {
+        for (int j = getMatrix().size() - 1; j > i; i--) {
+          Row targetRow = getMatrix().get(i);
+          targetRow = targetRow.addReplica(coll, shard);
+          targetRow.violations.clear();
+          for (Clause clause : session.getPolicy().clauses) {
+            if (strict || clause.strict) clause.test(targetRow);
+          }
+          if (targetRow.violations.isEmpty()) {
+            getMatrix().set(i, getMatrix().get(i).removeReplica(coll, shard).first());
+            getMatrix().set(j, getMatrix().get(j).addReplica(coll, shard));
+                return Utils.makeMap("operation", MOVEREPLICA.toLower(),
+                    COLLECTION_PROP, coll,
+                    SHARD_ID_PROP, shard,
+                    NODE, fromRow.node,
+                    REPLICA, pair.second().name,
+                    "target", targetRow.node);
+          }
+        }
+      }
+    }
+    return null;
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/c5a9012d/solr/solrj/src/java/org/apache/solr/cloud/autoscaling/Operand.java
----------------------------------------------------------------------
diff --git a/solr/solrj/src/java/org/apache/solr/cloud/autoscaling/Operand.java b/solr/solrj/src/java/org/apache/solr/cloud/autoscaling/Operand.java
new file mode 100644
index 0000000..81bdcf4
--- /dev/null
+++ b/solr/solrj/src/java/org/apache/solr/cloud/autoscaling/Operand.java
@@ -0,0 +1,119 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.solr.cloud.autoscaling;
+
+import java.util.Objects;
+
+import org.apache.solr.autoscaling.Clause.TestStatus;
+
+import static org.apache.solr.autoscaling.Clause.TestStatus.*;
+import static org.apache.solr.cloud.autoscaling.Policy.ANY;
+
+
+public enum Operand {
+  WILDCARD(ANY, Integer.MAX_VALUE) {
+    @Override
+    public TestStatus match(Object ruleVal, Object testVal) {
+      return testVal == null ? NOT_APPLICABLE : PASS;
+    }
+
+    @Override
+    public Object parse(String val) {
+      if (val == null) return ANY;
+      return ANY.equals(val) || Policy.EACH.equals(val) ? val : null;
+    }
+  },
+  EQUAL("", 0),
+  NOT_EQUAL("!", 2) {
+    @Override
+    public TestStatus match(Object ruleVal, Object testVal) {
+      return super.match(ruleVal, testVal) == PASS ? FAIL : PASS;
+    }
+  },
+  GREATER_THAN(">", 1) {
+    @Override
+    public Object parse(String val) {
+      return checkNumeric(super.parse(val));
+    }
+
+
+    @Override
+    public TestStatus match(Object ruleVal, Object testVal) {
+      if (testVal == null) return NOT_APPLICABLE;
+      return compareNum(ruleVal, testVal) == 1 ? PASS : FAIL;
+    }
+
+  },
+  LESS_THAN("<", 2) {
+    @Override
+    public TestStatus match(Object ruleVal, Object testVal) {
+      if (testVal == null) return NOT_APPLICABLE;
+      return compareNum(ruleVal, testVal) == -1 ? PASS : FAIL;
+    }
+
+    @Override
+    public Object parse(String val) {
+      return checkNumeric(super.parse(val));
+    }
+  };
+  public final String operand;
+  final int priority;
+
+  Operand(String val, int priority) {
+    this.operand = val;
+    this.priority = priority;
+  }
+
+  public String toStr(Object expectedVal) {
+    return operand + expectedVal.toString();
+  }
+
+  Object checkNumeric(Object val) {
+    if (val == null) return null;
+    try {
+      return Integer.parseInt(val.toString());
+    } catch (NumberFormatException e) {
+      throw new RuntimeException("for operand " + operand + " the value must be numeric");
+    }
+  }
+
+  public Object parse(String val) {
+    if (operand.isEmpty()) return val;
+    return val.startsWith(operand) ? val.substring(1) : null;
+  }
+
+  public TestStatus match(Object ruleVal, Object testVal) {
+    return Objects.equals(String.valueOf(ruleVal), String.valueOf(testVal)) ? PASS : FAIL;
+  }
+
+
+  public int compareNum(Object n1Val, Object n2Val) {
+    Integer n1 = (Integer) parseObj(n1Val, Integer.class);
+    Integer n2 = (Integer) parseObj(n2Val, Integer.class);
+    return n1 > n2 ? -1 : Objects.equals(n1, n2) ? 0 : 1;
+  }
+
+  Object parseObj(Object o, Class typ) {
+    if (o == null) return o;
+    if (typ == String.class) return String.valueOf(o);
+    if (typ == Integer.class) {
+      return Integer.parseInt(String.valueOf(o));
+    }
+    return o;
+  }
+}

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/c5a9012d/solr/solrj/src/java/org/apache/solr/cloud/autoscaling/Policy.java
----------------------------------------------------------------------
diff --git a/solr/solrj/src/java/org/apache/solr/cloud/autoscaling/Policy.java b/solr/solrj/src/java/org/apache/solr/cloud/autoscaling/Policy.java
new file mode 100644
index 0000000..76e1867
--- /dev/null
+++ b/solr/solrj/src/java/org/apache/solr/cloud/autoscaling/Policy.java
@@ -0,0 +1,333 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.solr.cloud.autoscaling;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.EnumMap;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.function.Supplier;
+import java.util.stream.Collectors;
+
+import org.apache.solr.common.IteratorWriter;
+import org.apache.solr.common.MapWriter;
+import org.apache.solr.common.params.CollectionParams.CollectionAction;
+import org.apache.solr.common.util.Utils;
+
+import static java.util.Collections.singletonList;
+import static java.util.stream.Collectors.toList;
+import static org.apache.solr.common.util.Utils.getDeepCopy;
+
+public class Policy implements MapWriter {
+  public static final String EACH = "#EACH";
+  public static final String ANY = "#ANY";
+  List<Clause> clauses = new ArrayList<>();
+  List<Preference> preferences = new ArrayList<>();
+  List<String> params = new ArrayList<>();
+
+
+  public Policy(Map<String, Object> jsonMap) {
+    List<Map<String, Object>> l = getListOfMap("conditions", jsonMap);
+    clauses = l.stream()
+        .map(Clause::new)
+        .sorted()
+        .collect(toList());
+    l = getListOfMap("preferences", jsonMap);
+    preferences = l.stream().map(Preference::new).collect(toList());
+    for (int i = 0; i < preferences.size() - 1; i++) {
+      Preference preference = preferences.get(i);
+      preference.next = preferences.get(i + 1);
+    }
+
+    for (Clause c : clauses) params.add(c.tag.name);
+    for (Preference preference : preferences) {
+      if (params.contains(preference.name.name())) {
+        throw new RuntimeException(preference.name + " is repeated");
+      }
+      params.add(preference.name.toString());
+      preference.idx = params.size() - 1;
+    }
+  }
+
+  @Override
+  public void writeMap(EntryWriter ew) throws IOException {
+    if (!clauses.isEmpty()) {
+      ew.put("conditions", (IteratorWriter) iw -> {
+        for (Clause clause : clauses) iw.add(clause);
+      });
+    }
+    if (!preferences.isEmpty()) {
+      ew.put("preferences", (IteratorWriter) iw -> {
+        for (Preference p : preferences) iw.add(p);
+      });
+    }
+
+  }
+
+  public class Session implements MapWriter {
+    final List<String> nodes;
+    final ClusterDataProvider dataProvider;
+    final List<Row> matrix;
+    Set<String> collections = new HashSet<>();
+
+    Session(List<String> nodes, ClusterDataProvider dataProvider, List<Row> matrix) {
+      this.nodes = nodes;
+      this.dataProvider = dataProvider;
+      this.matrix = matrix;
+    }
+
+    Session(ClusterDataProvider dataProvider) {
+      this.nodes = new ArrayList<>(dataProvider.getNodes());
+      this.dataProvider = dataProvider;
+      matrix = new ArrayList<>(nodes.size());
+      for (String node : nodes) matrix.add(new Row(node, params, dataProvider));
+      for (Row row : matrix) row.replicaInfo.forEach((s, e) -> collections.add(s));
+    }
+
+    Session copy() {
+      return new Session(nodes, dataProvider, getMatrixCopy());
+    }
+
+    List<Row> getMatrixCopy() {
+      return matrix.stream()
+          .map(Row::copy)
+          .collect(Collectors.toList());
+    }
+
+    Policy getPolicy() {
+      return Policy.this;
+
+    }
+
+    /**
+     * Apply the preferences and conditions
+     */
+    public void applyRules() {
+      if (!preferences.isEmpty()) {
+        //this is to set the approximate value according to the precision
+        ArrayList<Row> tmpMatrix = new ArrayList<>(matrix);
+        for (Preference p : preferences) {
+          Collections.sort(tmpMatrix, (r1, r2) -> p.compare(r1, r2, false));
+          p.setApproxVal(tmpMatrix);
+        }
+        //approximate values are set now. Let's do recursive sorting
+        Collections.sort(matrix, (r1, r2) -> preferences.get(0).compare(r1, r2, true));
+      }
+
+      if (!clauses.isEmpty()) {
+        for (Clause clause : clauses) {
+          for (Row row : matrix) {
+            clause.test(row);
+          }
+        }
+      }
+
+    }
+
+    public Map<String, List<Clause>> getViolations() {
+      return matrix.stream()
+          .filter(row -> !row.violations.isEmpty())
+          .collect(Collectors.toMap(r -> r.node, r -> r.violations));
+    }
+
+    public Suggester getSuggester(CollectionAction action) {
+      Suggester op = ops.get(action).get();
+      if (op == null) throw new UnsupportedOperationException(action.toString() + "is not supported");
+      op._init(this);
+      return op;
+    }
+
+    @Override
+    public void writeMap(EntryWriter ew) throws IOException {
+      for (int i = 0; i < matrix.size(); i++) {
+        Row row = matrix.get(i);
+        ew.put(row.node, row);
+      }
+    }
+
+    @Override
+    public String toString() {
+      return Utils.toJSONString(toMap(new LinkedHashMap<>()));
+    }
+
+    public List<Row> getSorted() {
+      return Collections.unmodifiableList(matrix);
+    }
+  }
+
+
+  public Session createSession(ClusterDataProvider snitch) {
+    return new Session(snitch);
+  }
+
+
+  static List<Map<String, Object>> getListOfMap(String key, Map<String, Object> jsonMap) {
+    Object o = jsonMap.get(key);
+    if (o != null) {
+      if (!(o instanceof List)) o = singletonList(o);
+      return (List) o;
+    } else {
+      return Collections.emptyList();
+    }
+  }
+
+
+  enum SortParam {
+    replica, freedisk, cores, heap, cpu;
+
+    static SortParam get(String m) {
+      for (SortParam p : values()) if (p.name().equals(m)) return p;
+      throw new RuntimeException("Invalid sort " + m + " Sort must be on one of these " + Arrays.asList(values()));
+    }
+
+  }
+
+  enum Sort {
+    maximize(1), minimize(-1);
+    final int sortval;
+
+    Sort(int i) {
+      sortval = i;
+    }
+
+    static Sort get(Map<String, Object> m) {
+      if (m.containsKey(maximize.name()) && m.containsKey(minimize.name())) {
+        throw new RuntimeException("Cannot have both 'maximize' and 'minimize'");
+      }
+      if (m.containsKey(maximize.name())) return maximize;
+      if (m.containsKey(minimize.name())) return minimize;
+      throw new RuntimeException("must have either 'maximize' or 'minimize'");
+    }
+  }
+
+
+  public static class ReplicaInfo implements MapWriter {
+    final String name;
+    Map<String, Object> variables;
+
+    public ReplicaInfo(String name, Map<String, Object> vals) {
+      this.name = name;
+      this.variables = vals;
+    }
+
+    @Override
+    public void writeMap(EntryWriter ew) throws IOException {
+      ew.put(name, variables);
+    }
+  }
+
+
+  public static abstract class Suggester {
+    protected final EnumMap<Hint, String> hints = new EnumMap<>(Hint.class);
+    Policy.Session session;
+    Map operation;
+    private boolean isInitialized = false;
+
+    private void _init(Session session) {
+      this.session = session.copy();
+    }
+
+    public Suggester hint(Hint hint, String value) {
+      hints.put(hint, value);
+      return this;
+    }
+
+    abstract Map init();
+
+
+    public Map getOperation() {
+      if (!isInitialized) {
+        this.operation = init();
+        isInitialized = true;
+      }
+      return operation;
+    }
+
+    public Session getSession() {
+      return session;
+    }
+
+    List<Row> getMatrix() {
+      return session.matrix;
+
+    }
+
+    public enum Hint {
+      COLL, SHARD, SRC_NODE, TARGET_NODE
+    }
+
+
+  }
+
+  public static Map<String, Object> mergePolicies(String coll,
+                                                  Map<String, Object> collPolicy,
+                                                  Map<String, Object> defaultPolicy) {
+    Collection<Map<String, Object>> conditions = getDeepCopy(getListOfMap("conditions", collPolicy), 4, true);
+    insertColl(coll, conditions);
+    List<Clause> parsedConditions = conditions.stream().map(Clause::new).collect(toList());
+    Collection<Map<String, Object>> preferences = getDeepCopy(getListOfMap("preferences", collPolicy), 4, true);
+    List<Preference> parsedPreferences = preferences.stream().map(Preference::new).collect(toList());
+    if (defaultPolicy != null) {
+      Collection<Map<String, Object>> defaultConditions = getDeepCopy(getListOfMap("conditions", defaultPolicy), 4, true);
+      insertColl(coll, defaultConditions);
+      defaultConditions.forEach(e -> {
+        Clause clause = new Clause(e);
+        for (Clause c : parsedConditions) {
+          if (c.collection.equals(clause.collection) &&
+              c.tag.name.equals(clause.tag.name)) return;
+        }
+        conditions.add(e);
+      });
+      Collection<Map<String, Object>> defaultPreferences = getDeepCopy(getListOfMap("preferences", defaultPolicy), 4, true);
+      defaultPreferences.forEach(e -> {
+        Preference preference = new Preference(e);
+        for (Preference p : parsedPreferences) {
+          if (p.name == preference.name) return;
+        }
+        preferences.add(e);
+
+      });
+    }
+    return Utils.makeMap("conditions", conditions, "preferences", preferences);
+
+  }
+
+  private static Collection<Map<String, Object>> insertColl(String coll, Collection<Map<String, Object>> conditions) {
+    conditions.forEach(e -> {
+      if (!e.containsKey("collection")) e.put("collection", coll);
+    });
+    return conditions;
+  }
+
+  private static final Map<CollectionAction, Supplier<Suggester>> ops = new HashMap<>();
+
+  static {
+    ops.put(CollectionAction.ADDREPLICA, () -> new AddReplicaSuggester());
+    ops.put(CollectionAction.MOVEREPLICA, () -> new MoveReplicaSuggester());
+  }
+
+
+}

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/c5a9012d/solr/solrj/src/java/org/apache/solr/cloud/autoscaling/PolicyHelper.java
----------------------------------------------------------------------
diff --git a/solr/solrj/src/java/org/apache/solr/cloud/autoscaling/PolicyHelper.java b/solr/solrj/src/java/org/apache/solr/cloud/autoscaling/PolicyHelper.java
new file mode 100644
index 0000000..9af3e96
--- /dev/null
+++ b/solr/solrj/src/java/org/apache/solr/cloud/autoscaling/PolicyHelper.java
@@ -0,0 +1,74 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.solr.cloud.autoscaling;
+
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.solr.common.SolrException;
+import org.apache.solr.common.params.CoreAdminParams;
+import org.apache.solr.common.util.Utils;
+
+import static java.util.Arrays.asList;
+import static org.apache.solr.common.params.CollectionParams.CollectionAction.ADDREPLICA;
+import static org.apache.solr.autoscaling.Policy.Suggester.Hint.COLL;
+import static org.apache.solr.autoscaling.Policy.Suggester.Hint.SHARD;
+
+public class PolicyHelper {
+  public static Map<String, List<String>> getReplicaLocations(String collName, Map<String, Object> autoScalingJson,
+                                                              String policyName, ClusterDataProvider cdp,
+                                                              List<String> shardNames,
+                                                              int repFactor) {
+    Map<String, List<String>> positionMapping = new HashMap<>();
+    for (String shardName : shardNames) positionMapping.put(shardName, new ArrayList<>(repFactor));
+    Map policyJson = (Map) Utils.getObjectByPath(autoScalingJson, false, asList("policies", policyName));
+    if (policyJson == null) {
+      throw new SolrException(SolrException.ErrorCode.BAD_REQUEST, "no such policy exists " + policyName);
+    }
+    Map defaultPolicy = (Map) Utils.getObjectByPath(autoScalingJson, false, asList("policies", "default"));
+
+    Map<String, Object> merged = Policy.mergePolicies(collName, policyJson, defaultPolicy);
+    Policy policy = new Policy(merged);
+    Policy.Session session = policy.createSession(cdp);
+    for (String shardName : shardNames) {
+      for (int i = 0; i < repFactor; i++) {
+        Policy.Suggester suggester = session.getSuggester(ADDREPLICA)
+            .hint(COLL, collName)
+            .hint(SHARD, shardName);
+        Map op = suggester.getOperation();
+        if (op == null) {
+          throw new SolrException(SolrException.ErrorCode.BAD_REQUEST, "No node can satisfy the rules "+ Utils.toJSONString(policy));
+        }
+        session = suggester.getSession();
+        positionMapping.get(shardName).add((String) op.get(CoreAdminParams.NODE));
+      }
+    }
+
+    return positionMapping;
+  }
+
+  public List<Map> addNode(Map<String, Object> autoScalingJson, String node,  ClusterDataProvider cdp){
+    return null;
+
+  }
+
+
+}

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/c5a9012d/solr/solrj/src/java/org/apache/solr/cloud/autoscaling/Preference.java
----------------------------------------------------------------------
diff --git a/solr/solrj/src/java/org/apache/solr/cloud/autoscaling/Preference.java b/solr/solrj/src/java/org/apache/solr/cloud/autoscaling/Preference.java
new file mode 100644
index 0000000..9538338
--- /dev/null
+++ b/solr/solrj/src/java/org/apache/solr/cloud/autoscaling/Preference.java
@@ -0,0 +1,76 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.solr.cloud.autoscaling;
+
+import java.io.IOException;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.solr.common.MapWriter;
+import org.apache.solr.common.util.Utils;
+
+class Preference implements MapWriter {
+  final Policy.SortParam name;
+  Integer precision;
+  final Policy.Sort sort;
+  Preference next;
+  public int idx;
+  private final Map original;
+
+  Preference(Map<String, Object> m) {
+    this.original = Utils.getDeepCopy(m,3);
+    sort = Policy.Sort.get(m);
+    name = Policy.SortParam.get(m.get(sort.name()).toString());
+    Object p = m.getOrDefault("precision", 0);
+    precision = p instanceof Number ? ((Number) p).intValue() : Integer.parseInt(p.toString());
+
+  }
+
+  // there are 2 modes of compare.
+  // recursive, it uses the precision to tie & when there is a tie use the next preference to compare
+  // in non-recursive mode, precision is not taken into consideration and sort is done on actual value
+  int compare(Row r1, Row r2, boolean recursive) {
+    Object o1 = recursive ? r1.cells[idx].val_ : r1.cells[idx].val;
+    Object o2 = recursive ? r2.cells[idx].val_ : r2.cells[idx].val;
+    int result = 0;
+    if (o1 instanceof Integer && o2 instanceof Integer) result = ((Integer) o1).compareTo((Integer) o2);
+    if (o1 instanceof Long && o2 instanceof Long) result = ((Long) o1).compareTo((Long) o2);
+    if (o1 instanceof Float && o2 instanceof Float) result = ((Float) o1).compareTo((Float) o2);
+    if (o1 instanceof Double && o2 instanceof Double) result = ((Double) o1).compareTo((Double) o2);
+    return result == 0 ? next == null ? 0 : next.compare(r1, r2, recursive) : sort.sortval * result;
+  }
+
+  //sets the new value according to precision in val_
+  void setApproxVal(List<Row> tmpMatrix) {
+    Object prevVal = null;
+    for (Row row : tmpMatrix) {
+      prevVal = row.cells[idx].val_ =
+          prevVal == null || Math.abs(((Number) prevVal).longValue() - ((Number) row.cells[idx].val).longValue()) > precision ?
+              row.cells[idx].val :
+              prevVal;
+    }
+  }
+
+  @Override
+  public void writeMap(EntryWriter ew) throws IOException {
+    for (Object o : original.entrySet()) {
+      Map.Entry e = (Map.Entry) o;
+      ew.put(String.valueOf(e.getKey()), e.getValue());
+    }
+  }
+}

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/c5a9012d/solr/solrj/src/java/org/apache/solr/cloud/autoscaling/Row.java
----------------------------------------------------------------------
diff --git a/solr/solrj/src/java/org/apache/solr/cloud/autoscaling/Row.java b/solr/solrj/src/java/org/apache/solr/cloud/autoscaling/Row.java
new file mode 100644
index 0000000..c62ccdb
--- /dev/null
+++ b/solr/solrj/src/java/org/apache/solr/cloud/autoscaling/Row.java
@@ -0,0 +1,113 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.solr.cloud.autoscaling;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Random;
+
+import org.apache.solr.common.IteratorWriter;
+import org.apache.solr.common.MapWriter;
+import org.apache.solr.common.util.Pair;
+import org.apache.solr.common.util.Utils;
+import org.apache.solr.autoscaling.Policy.ReplicaInfo;
+
+import static org.apache.solr.common.params.CoreAdminParams.NODE;
+
+
+class Row implements MapWriter {
+  public final String node;
+  final Cell[] cells;
+  Map<String, Map<String, List<ReplicaInfo>>> replicaInfo;
+  List<Clause> violations = new ArrayList<>();
+  boolean anyValueMissing = false;
+
+  Row(String node, List<String> params, ClusterDataProvider snitch) {
+    replicaInfo = snitch.getReplicaInfo(node, params);
+    if (replicaInfo == null) replicaInfo = Collections.emptyMap();
+    this.node = node;
+    cells = new Cell[params.size()];
+    Map<String, Object> vals = snitch.getNodeValues(node, params);
+    for (int i = 0; i < params.size(); i++) {
+      String s = params.get(i);
+      cells[i] = new Cell(i, s, vals.get(s));
+      if (NODE.equals(s)) cells[i].val = node;
+      if (cells[i].val == null) anyValueMissing = true;
+    }
+  }
+
+  Row(String node, Cell[] cells, boolean anyValueMissing, Map<String, Map<String, List<ReplicaInfo>>> replicaInfo, List<Clause> violations) {
+    this.node = node;
+    this.cells = new Cell[cells.length];
+    for (int i = 0; i < this.cells.length; i++) {
+      this.cells[i] = cells[i].copy();
+
+    }
+    this.anyValueMissing = anyValueMissing;
+    this.replicaInfo = replicaInfo;
+    this.violations = violations;
+  }
+
+  @Override
+  public void writeMap(EntryWriter ew) throws IOException {
+    ew.put(node, (IteratorWriter) iw -> {
+      iw.add((MapWriter) e -> e.put("replicas", replicaInfo));
+      for (Cell cell : cells) iw.add(cell);
+    });
+  }
+
+  Row copy() {
+    return new Row(node, cells, anyValueMissing, Utils.getDeepCopy(replicaInfo, 3), new ArrayList<>(violations));
+  }
+
+  Object getVal(String name) {
+    for (Cell cell : cells) if (cell.name.equals(name)) return cell.val;
+    return null;
+  }
+
+  @Override
+  public String toString() {
+    return node;
+  }
+
+  Row addReplica(String coll, String shard) {
+    Row row = copy();
+    Map<String, List<ReplicaInfo>> c = row.replicaInfo.get(coll);
+    if (c == null) row.replicaInfo.put(coll, c = new HashMap<>());
+    List<ReplicaInfo> s = c.get(shard);
+    if (s == null) c.put(shard, s = new ArrayList<>());
+    s.add(new ReplicaInfo(""+new Random().nextInt(10000)+10000 , new HashMap<>()));
+    return row;
+
+
+  }
+
+  Pair<Row, ReplicaInfo> removeReplica(String coll, String shard) {
+    Row row = copy();
+    Map<String, List<ReplicaInfo>> c = row.replicaInfo.get(coll);
+    if(c == null) return null;
+    List<ReplicaInfo> s = c.get(shard);
+    if (s == null || s.isEmpty()) return null;
+    return new Pair(row,s.remove(0));
+
+  }
+}

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/c5a9012d/solr/solrj/src/java/org/apache/solr/cloud/autoscaling/package-info.java
----------------------------------------------------------------------
diff --git a/solr/solrj/src/java/org/apache/solr/cloud/autoscaling/package-info.java b/solr/solrj/src/java/org/apache/solr/cloud/autoscaling/package-info.java
new file mode 100644
index 0000000..a0167fa
--- /dev/null
+++ b/solr/solrj/src/java/org/apache/solr/cloud/autoscaling/package-info.java
@@ -0,0 +1,22 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * Common classes for autoscaling parsing filtering nodes and sorting
+ */
+
+package org.apache.solr.cloud.autoscaling;
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/c5a9012d/solr/solrj/src/java/org/apache/solr/recipe/AddReplicaSuggester.java
----------------------------------------------------------------------
diff --git a/solr/solrj/src/java/org/apache/solr/recipe/AddReplicaSuggester.java b/solr/solrj/src/java/org/apache/solr/recipe/AddReplicaSuggester.java
deleted file mode 100644
index 450225e..0000000
--- a/solr/solrj/src/java/org/apache/solr/recipe/AddReplicaSuggester.java
+++ /dev/null
@@ -1,60 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.apache.solr.recipe;
-
-import java.util.Map;
-
-import org.apache.solr.common.util.Utils;
-import org.apache.solr.recipe.Policy.Suggester;
-
-import static org.apache.solr.common.cloud.ZkStateReader.COLLECTION_PROP;
-import static org.apache.solr.common.cloud.ZkStateReader.SHARD_ID_PROP;
-import static org.apache.solr.common.params.CollectionParams.CollectionAction.ADDREPLICA;
-import static org.apache.solr.common.params.CoreAdminParams.NODE;
-
-class AddReplicaSuggester extends Suggester {
-
-  Map init() {
-    Map operation = tryEachNode(true);
-    if (operation == null) operation = tryEachNode(false);
-    return operation;
-  }
-
-  Map tryEachNode(boolean strict) {
-    //iterate through elements and identify the least loaded
-    for (int i = getMatrix().size() - 1; i >= 0; i--) {
-      Row row = getMatrix().get(i);
-      String coll = hints.get(Hint.COLL);
-      String shard = hints.get(Hint.SHARD);
-      row = row.addReplica(coll, shard);
-      row.violations.clear();
-      for (Clause clause : session.getPolicy().clauses) {
-        if (strict || clause.strict) clause.test(row);
-      }
-      if (row.violations.isEmpty()) {// there are no rule violations
-        getMatrix().set(i, getMatrix().get(i).addReplica(coll, shard));
-        return Utils.makeMap("operation", ADDREPLICA.toLower(),
-            COLLECTION_PROP, coll,
-            SHARD_ID_PROP, shard,
-            NODE, row.node);
-      }
-    }
-    return null;
-  }
-
-}

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/c5a9012d/solr/solrj/src/java/org/apache/solr/recipe/Cell.java
----------------------------------------------------------------------
diff --git a/solr/solrj/src/java/org/apache/solr/recipe/Cell.java b/solr/solrj/src/java/org/apache/solr/recipe/Cell.java
deleted file mode 100644
index 7a8bf23..0000000
--- a/solr/solrj/src/java/org/apache/solr/recipe/Cell.java
+++ /dev/null
@@ -1,50 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.apache.solr.recipe;
-
-import java.io.IOException;
-
-import org.apache.solr.common.MapWriter;
-
-class Cell implements MapWriter {
-  final int index;
-  final String name;
-  Object val, val_;
-
-  Cell(int index, String name, Object val) {
-    this.index = index;
-    this.name = name;
-    this.val = val;
-  }
-
-  Cell(int index, String name, Object val, Object val_) {
-    this.index = index;
-    this.name = name;
-    this.val = val;
-    this.val_ = val_;
-  }
-
-  @Override
-  public void writeMap(EntryWriter ew) throws IOException {
-    ew.put(name, val);
-  }
-
-  public Cell copy() {
-    return new Cell(index, name, val, val_);
-  }
-}

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/c5a9012d/solr/solrj/src/java/org/apache/solr/recipe/Clause.java
----------------------------------------------------------------------
diff --git a/solr/solrj/src/java/org/apache/solr/recipe/Clause.java b/solr/solrj/src/java/org/apache/solr/recipe/Clause.java
deleted file mode 100644
index 894da0b..0000000
--- a/solr/solrj/src/java/org/apache/solr/recipe/Clause.java
+++ /dev/null
@@ -1,195 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.apache.solr.recipe;
-
-import java.io.IOException;
-import java.util.Arrays;
-import java.util.Comparator;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Objects;
-import java.util.Set;
-import java.util.concurrent.atomic.AtomicReference;
-
-import com.sun.istack.internal.NotNull;
-import org.apache.solr.common.MapWriter;
-import org.apache.solr.common.util.Utils;
-import org.apache.solr.recipe.Policy.ReplicaInfo;
-
-import static java.util.Collections.singletonMap;
-import static org.apache.solr.common.params.CoreAdminParams.COLLECTION;
-import static org.apache.solr.common.params.CoreAdminParams.REPLICA;
-import static org.apache.solr.common.params.CoreAdminParams.SHARD;
-import static org.apache.solr.recipe.Clause.TestStatus.FAIL;
-import static org.apache.solr.recipe.Clause.TestStatus.NOT_APPLICABLE;
-import static org.apache.solr.recipe.Clause.TestStatus.PASS;
-import static org.apache.solr.recipe.Operand.EQUAL;
-import static org.apache.solr.recipe.Operand.GREATER_THAN;
-import static org.apache.solr.recipe.Operand.LESS_THAN;
-import static org.apache.solr.recipe.Operand.NOT_EQUAL;
-import static org.apache.solr.recipe.Operand.WILDCARD;
-import static org.apache.solr.recipe.Policy.ANY;
-import static org.apache.solr.recipe.Policy.EACH;
-
-// a set of conditions in a policy
-public class Clause implements MapWriter, Comparable<Clause> {
-  Map<String, Object> original;
-  Condition collection, shard, replica, tag;
-  boolean strict = true;
-
-  Clause(Map<String, Object> m) {
-    this.original = m;
-    collection = parse(COLLECTION, m);
-    shard = parse(SHARD, m);
-    this.replica = parse(REPLICA, m);
-    strict = Boolean.parseBoolean(String.valueOf(m.getOrDefault("strict", "true")));
-    m.forEach((s, o) -> parseCondition(s, o));
-    if (tag == null)
-      throw new RuntimeException("Invalid op, must have one and only one tag other than collection, shard,replica " + Utils.toJSONString(m));
-  }
-
-  void parseCondition(String s, Object o) {
-    if (IGNORE_TAGS.contains(s)) return;
-    if (tag != null) {
-      throw new IllegalArgumentException("Only one tag other than collection, shard, replica is possible");
-    }
-    tag = parse(s, singletonMap(s, o));
-  }
-
-  @Override
-  public int compareTo(Clause that) {
-    int v = Integer.compare(this.tag.op.priority, that.tag.op.priority);
-    if (v != 0) return v;
-    return Integer.compare(this.replica.op.priority, that.replica.op.priority);
-  }
-
-  static class Condition {
-    final String name;
-    final Object val;
-    final Operand op;
-
-    Condition(String name, Object val, Operand op) {
-      this.name = name;
-      this.val = val;
-      this.op = op;
-    }
-
-    TestStatus match(Row row) {
-      return op.match(val, row.getVal(name));
-    }
-
-    boolean isPass(Object inputVal) {
-      return op.match(val, inputVal) == PASS;
-    }
-
-    boolean isPass(Row row) {
-      return op.match(val, row.getVal(name)) == PASS;
-    }
-
-    @Override
-    public boolean equals(Object that) {
-      if (that instanceof Condition) {
-        Condition c = (Condition) that;
-        return Objects.equals(c.name, name) && Objects.equals(c.val, val) && c.op == op;
-      }
-      return false;
-    }
-  }
-
-  static Condition parse(String s, Map m) {
-    Object expectedVal = null;
-    Object val = m.get(s);
-    try {
-      String conditionName = s.trim();
-      String value = val == null ? null : String.valueOf(val).trim();
-      Operand operand = null;
-      if ((expectedVal = WILDCARD.parse(value)) != null) {
-        operand = WILDCARD;
-      } else if ((expectedVal = NOT_EQUAL.parse(value)) != null) {
-        operand = NOT_EQUAL;
-      } else if ((expectedVal = GREATER_THAN.parse(value)) != null) {
-        operand = GREATER_THAN;
-      } else if ((expectedVal = LESS_THAN.parse(value)) != null) {
-        operand = LESS_THAN;
-      } else {
-        operand = EQUAL;
-        expectedVal = EQUAL.parse(value);
-      }
-
-      return new Condition(conditionName, expectedVal, operand);
-
-    } catch (Exception e) {
-      throw new IllegalArgumentException("Invalid tag : " + s + ":" + val, e);
-    }
-  }
-
-
-  TestStatus test(Row row) {
-    AtomicReference<TestStatus> result = new AtomicReference<>(NOT_APPLICABLE);
-
-    for (Map.Entry<String, Map<String, List<ReplicaInfo>>> colls : row.replicaInfo.entrySet()) {
-      if (result.get() == FAIL) break;
-      if (!collection.isPass(colls.getKey())) continue;
-      int count = 0;
-      for (Map.Entry<String, List<ReplicaInfo>> shards : colls.getValue().entrySet()) {
-        if (!shard.isPass(shards.getKey()) || result.get() == FAIL) break;
-        count += shards.getValue().size();
-        if (shard.val.equals(EACH)) testReplicaCount(row, result, count);
-        if (EACH.equals(shard.val)) count = 0;
-        }
-      if (shard.val.equals(ANY)) testReplicaCount(row, result, count);
-      }
-    if (result.get() == FAIL) row.violations.add(this);
-    return result.get();
-
-  }
-
-  private void testReplicaCount(Row row, AtomicReference<TestStatus> result, int count) {
-    if("node".equals(tag.name)) if(!tag.isPass(row.node)) return;
-    boolean checkCount = replica.op.match(replica.val, 0) != PASS || count > 0;
-    if (replica.op == WILDCARD && count > 0 && !tag.isPass(row)) {
-      result.set(FAIL);
-    } else if (checkCount && !replica.isPass(count)) {
-      if (tag.op != WILDCARD && tag.isPass(row)) {
-        result.set(FAIL);
-      } else {
-        result.set(FAIL);
-      }
-    }
-  }
-
-  public boolean isStrict() {
-    return strict;
-  }
-  @Override
-  public String toString() {
-    return Utils.toJSONString(original);
-  }
-
-  @Override
-  public void writeMap(EntryWriter ew) throws IOException {
-    for (Map.Entry<String, Object> e : original.entrySet()) ew.put(e.getKey(), e.getValue());
-  }
-
-  enum TestStatus {
-    NOT_APPLICABLE, FAIL, PASS
-  }
-
-  private static final Set<String> IGNORE_TAGS = new HashSet<>(Arrays.asList(REPLICA, COLLECTION, SHARD, "strict"));
-}

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/c5a9012d/solr/solrj/src/java/org/apache/solr/recipe/ClusterDataProvider.java
----------------------------------------------------------------------
diff --git a/solr/solrj/src/java/org/apache/solr/recipe/ClusterDataProvider.java b/solr/solrj/src/java/org/apache/solr/recipe/ClusterDataProvider.java
deleted file mode 100644
index 735aa87..0000000
--- a/solr/solrj/src/java/org/apache/solr/recipe/ClusterDataProvider.java
+++ /dev/null
@@ -1,43 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.apache.solr.recipe;
-
-import java.io.Closeable;
-import java.io.IOException;
-import java.util.Collection;
-import java.util.List;
-import java.util.Map;
-
-
-public interface ClusterDataProvider extends Closeable {
-  Map<String, Object> getNodeValues(String node, Collection<String> keys);
-
-  /**
-   * Get the details of each replica in a node. It attempts to fetch as much details about
-   * the replica as mentioned in the keys list. It is not necessary to give al details
-   * <p>
-   * the format is {collection:shard :[{replicadetails}]}
-   */
-  Map<String, Map<String, List<Policy.ReplicaInfo>>> getReplicaInfo(String node, Collection<String> keys);
-
-  Collection<String> getNodes();
-
-  @Override
-  default void close() throws IOException {
-  }
-}

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/c5a9012d/solr/solrj/src/java/org/apache/solr/recipe/MoveReplicaSuggester.java
----------------------------------------------------------------------
diff --git a/solr/solrj/src/java/org/apache/solr/recipe/MoveReplicaSuggester.java b/solr/solrj/src/java/org/apache/solr/recipe/MoveReplicaSuggester.java
deleted file mode 100644
index 43e1fe6..0000000
--- a/solr/solrj/src/java/org/apache/solr/recipe/MoveReplicaSuggester.java
+++ /dev/null
@@ -1,81 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.apache.solr.recipe;
-
-import java.util.Map;
-
-import org.apache.solr.common.util.Pair;
-import org.apache.solr.common.util.Utils;
-import org.apache.solr.recipe.Policy.Suggester;
-
-import static org.apache.solr.common.cloud.ZkStateReader.COLLECTION_PROP;
-import static org.apache.solr.common.cloud.ZkStateReader.SHARD_ID_PROP;
-import static org.apache.solr.common.params.CollectionParams.CollectionAction.MOVEREPLICA;
-import static org.apache.solr.common.params.CoreAdminParams.NODE;
-import static org.apache.solr.common.params.CoreAdminParams.REPLICA;
-
-public class MoveReplicaSuggester extends Suggester {
-
-  @Override
-  Map init() {
-    Map operation = tryEachNode(true);
-    if (operation == null) operation = tryEachNode(false);
-    return operation;
-  }
-
-  Map tryEachNode(boolean strict) {
-    //iterate through elements and identify the least loaded
-    String coll = hints.get(Hint.COLL);
-    String shard = hints.get(Hint.SHARD);
-    for (int i = 0; i < getMatrix().size(); i++) {
-      Row fromRow = getMatrix().get(i);
-      Pair<Row, Policy.ReplicaInfo> pair = fromRow.removeReplica(coll, shard);
-      fromRow = pair.first();
-      if(fromRow == null){
-        //no such replica available
-        continue;
-      }
-
-      for (Clause clause : session.getPolicy().clauses) {
-        if (strict || clause.strict) clause.test(fromRow);
-      }
-      if (fromRow.violations.isEmpty()) {
-        for (int j = getMatrix().size() - 1; j > i; i--) {
-          Row targetRow = getMatrix().get(i);
-          targetRow = targetRow.addReplica(coll, shard);
-          targetRow.violations.clear();
-          for (Clause clause : session.getPolicy().clauses) {
-            if (strict || clause.strict) clause.test(targetRow);
-          }
-          if (targetRow.violations.isEmpty()) {
-            getMatrix().set(i, getMatrix().get(i).removeReplica(coll, shard).first());
-            getMatrix().set(j, getMatrix().get(j).addReplica(coll, shard));
-                return Utils.makeMap("operation", MOVEREPLICA.toLower(),
-                    COLLECTION_PROP, coll,
-                    SHARD_ID_PROP, shard,
-                    NODE, fromRow.node,
-                    REPLICA, pair.second().name,
-                    "target", targetRow.node);
-          }
-        }
-      }
-    }
-    return null;
-  }
-
-}

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/c5a9012d/solr/solrj/src/java/org/apache/solr/recipe/Operand.java
----------------------------------------------------------------------
diff --git a/solr/solrj/src/java/org/apache/solr/recipe/Operand.java b/solr/solrj/src/java/org/apache/solr/recipe/Operand.java
deleted file mode 100644
index 140c665..0000000
--- a/solr/solrj/src/java/org/apache/solr/recipe/Operand.java
+++ /dev/null
@@ -1,119 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.apache.solr.recipe;
-
-import java.util.Objects;
-
-import org.apache.solr.recipe.Clause.TestStatus;
-
-import static org.apache.solr.recipe.Clause.TestStatus.*;
-import static org.apache.solr.recipe.Policy.ANY;
-
-
-public enum Operand {
-  WILDCARD(ANY, Integer.MAX_VALUE) {
-    @Override
-    public TestStatus match(Object ruleVal, Object testVal) {
-      return testVal == null ? NOT_APPLICABLE : PASS;
-    }
-
-    @Override
-    public Object parse(String val) {
-      if (val == null) return ANY;
-      return ANY.equals(val) || Policy.EACH.equals(val) ? val : null;
-    }
-  },
-  EQUAL("", 0),
-  NOT_EQUAL("!", 2) {
-    @Override
-    public TestStatus match(Object ruleVal, Object testVal) {
-      return super.match(ruleVal, testVal) == PASS ? FAIL : PASS;
-    }
-  },
-  GREATER_THAN(">", 1) {
-    @Override
-    public Object parse(String val) {
-      return checkNumeric(super.parse(val));
-    }
-
-
-    @Override
-    public TestStatus match(Object ruleVal, Object testVal) {
-      if (testVal == null) return NOT_APPLICABLE;
-      return compareNum(ruleVal, testVal) == 1 ? PASS : FAIL;
-    }
-
-  },
-  LESS_THAN("<", 2) {
-    @Override
-    public TestStatus match(Object ruleVal, Object testVal) {
-      if (testVal == null) return NOT_APPLICABLE;
-      return compareNum(ruleVal, testVal) == -1 ? PASS : FAIL;
-    }
-
-    @Override
-    public Object parse(String val) {
-      return checkNumeric(super.parse(val));
-    }
-  };
-  public final String operand;
-  final int priority;
-
-  Operand(String val, int priority) {
-    this.operand = val;
-    this.priority = priority;
-  }
-
-  public String toStr(Object expectedVal) {
-    return operand + expectedVal.toString();
-  }
-
-  Object checkNumeric(Object val) {
-    if (val == null) return null;
-    try {
-      return Integer.parseInt(val.toString());
-    } catch (NumberFormatException e) {
-      throw new RuntimeException("for operand " + operand + " the value must be numeric");
-    }
-  }
-
-  public Object parse(String val) {
-    if (operand.isEmpty()) return val;
-    return val.startsWith(operand) ? val.substring(1) : null;
-  }
-
-  public TestStatus match(Object ruleVal, Object testVal) {
-    return Objects.equals(String.valueOf(ruleVal), String.valueOf(testVal)) ? PASS : FAIL;
-  }
-
-
-  public int compareNum(Object n1Val, Object n2Val) {
-    Integer n1 = (Integer) parseObj(n1Val, Integer.class);
-    Integer n2 = (Integer) parseObj(n2Val, Integer.class);
-    return n1 > n2 ? -1 : Objects.equals(n1, n2) ? 0 : 1;
-  }
-
-  Object parseObj(Object o, Class typ) {
-    if (o == null) return o;
-    if (typ == String.class) return String.valueOf(o);
-    if (typ == Integer.class) {
-      return Integer.parseInt(String.valueOf(o));
-    }
-    return o;
-  }
-}