/*
* 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.rule;
import java.lang.invoke.MethodHandles;
import java.nio.file.Paths;
import java.util.List;
import java.util.Map;
import org.apache.lucene.util.LuceneTestCase;
import org.apache.solr.client.solrj.SolrClient;
import org.apache.solr.client.solrj.embedded.JettySolrRunner;
import org.apache.solr.client.solrj.impl.HttpSolrClient;
import org.apache.solr.client.solrj.request.CollectionAdminRequest;
import org.apache.solr.client.solrj.request.GenericSolrRequest;
import org.apache.solr.client.solrj.response.SimpleSolrResponse;
import org.apache.solr.cloud.SolrCloudTestCase;
import org.apache.solr.common.cloud.DocCollection;
import org.apache.solr.common.params.ModifiableSolrParams;
import org.junit.After;
import org.junit.BeforeClass;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import static org.apache.solr.client.solrj.SolrRequest.METHOD.GET;
import static org.apache.solr.client.solrj.SolrRequest.METHOD.POST;
import static org.apache.solr.common.params.CommonParams.COLLECTIONS_HANDLER_PATH;
import static org.junit.matchers.JUnitMatchers.containsString;
@LuceneTestCase.Slow
public class RulesTest 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();
}
@Test
public void doIntegrationTest() throws Exception {
final long minGB = (random().nextBoolean() ? 1 : 0);
assumeTrue("doIntegrationTest needs minGB="+minGB+" usable disk space",
ImplicitSnitch.getUsableSpaceInGB(Paths.get("/")) > minGB);
String rulesColl = "rulesColl";
CollectionAdminRequest.createCollectionWithImplicitRouter(rulesColl, "conf", "shard1", 2)
.setRule("cores:<4", "node:*,replica:<2", "freedisk:>"+minGB)
.setSnitch("class:ImplicitSnitch")
.process(cluster.getSolrClient());
DocCollection rulesCollection = getCollectionState(rulesColl);
List list = (List) rulesCollection.get("rule");
assertEquals(3, list.size());
assertEquals ( "<4", ((Map)list.get(0)).get("cores"));
assertEquals("<2", ((Map) list.get(1)).get("replica"));
assertEquals(">"+minGB, ((Map) list.get(2)).get("freedisk"));
list = (List) rulesCollection.get("snitch");
assertEquals(1, list.size());
assertEquals ( "ImplicitSnitch", ((Map)list.get(0)).get("class"));
CollectionAdminRequest.createShard(rulesColl, "shard2").process(cluster.getSolrClient());
CollectionAdminRequest.addReplicaToShard(rulesColl, "shard2").process(cluster.getSolrClient());
}
@Test
public void testPortRule() throws Exception {
JettySolrRunner jetty = cluster.getRandomJetty(random());
String port = Integer.toString(jetty.getLocalPort());
String rulesColl = "portRuleColl";
CollectionAdminRequest.createCollectionWithImplicitRouter(rulesColl, "conf", "shard1", 2)
.setRule("port:" + port)
.setSnitch("class:ImplicitSnitch")
.process(cluster.getSolrClient());
DocCollection rulesCollection = getCollectionState(rulesColl);
List list = (List) rulesCollection.get("rule");
assertEquals(1, list.size());
assertEquals(port, ((Map) list.get(0)).get("port"));
list = (List) rulesCollection.get("snitch");
assertEquals(1, list.size());
assertEquals ( "ImplicitSnitch", ((Map)list.get(0)).get("class"));
}
@Test
public void testHostFragmentRule() throws Exception {
String rulesColl = "hostFragment";
JettySolrRunner jetty = cluster.getRandomJetty(random());
String host = jetty.getBaseUrl().getHost();
String[] ipFragments = host.split("\\.");
String ip_1 = ipFragments[ipFragments.length - 1];
String ip_2 = ipFragments[ipFragments.length - 2];
CollectionAdminRequest.createCollectionWithImplicitRouter(rulesColl, "conf", "shard1", 2)
.setRule("ip_2:" + ip_2, "ip_1:" + ip_1)
.setSnitch("class:ImplicitSnitch")
.process(cluster.getSolrClient());
DocCollection rulesCollection = getCollectionState(rulesColl);
List<Map> list = (List<Map>) rulesCollection.get("rule");
assertEquals(2, list.size());
assertEquals(ip_2, list.get(0).get("ip_2"));
assertEquals(ip_1, list.get(1).get("ip_1"));
list = (List) rulesCollection.get("snitch");
assertEquals(1, list.size());
assertEquals("ImplicitSnitch", list.get(0).get("class"));
}
@Test
public void testHostFragmentRuleThrowsExceptionWhenIpDoesNotMatch() throws Exception {
String rulesColl = "ipRuleColl";
JettySolrRunner jetty = cluster.getRandomJetty(random());
String host = jetty.getBaseUrl().getHost();
String[] ipFragments = host.split("\\.");
String ip_1 = ipFragments[ipFragments.length - 1];
String ip_2 = ipFragments[ipFragments.length - 2];
expectedException.expect(HttpSolrClient.RemoteSolrException.class);
expectedException.expectMessage(containsString("ip_1"));
CollectionAdminRequest.createCollectionWithImplicitRouter(rulesColl, "conf", "shard1", 2)
.setRule("ip_2:" + ip_2, "ip_1:" + ip_1 + "9999")
.setSnitch("class:ImplicitSnitch")
.process(cluster.getSolrClient());
}
@Test
public void testInvokeApi() throws Exception {
JettySolrRunner jetty = cluster.getRandomJetty(random());
try (SolrClient client = getHttpSolrClient(jetty.getBaseUrl().toString())) {
GenericSolrRequest req = new GenericSolrRequest(GET, "/____v2/node/invoke", new ModifiableSolrParams()
.add("class", ImplicitSnitch.class.getName())
.add("cores", "1")
.add("freedisk", "1")
);
SimpleSolrResponse rsp = req.process(client);
assertNotNull(((Map) rsp.getResponse().get(ImplicitSnitch.class.getName())).get("cores"));
assertNotNull(((Map) rsp.getResponse().get(ImplicitSnitch.class.getName())).get("freedisk"));
}
}
@Test
public void testModifyColl() throws Exception {
final long minGB1 = (random().nextBoolean() ? 1 : 0);
final long minGB2 = 5;
assumeTrue("testModifyColl needs minGB1="+minGB1+" usable disk space",
ImplicitSnitch.getUsableSpaceInGB(Paths.get("/")) > minGB1);
assumeTrue("testModifyColl needs minGB2="+minGB2+" usable disk space",
ImplicitSnitch.getUsableSpaceInGB(Paths.get("/")) > minGB2);
String rulesColl = "modifyColl";
CollectionAdminRequest.createCollection(rulesColl, "conf", 1, 2)
.setRule("cores:<4", "node:*,replica:1", "freedisk:>" + minGB1)
.setSnitch("class:ImplicitSnitch")
.process(cluster.getSolrClient());
// TODO: Make a MODIFYCOLLECTION SolrJ class
ModifiableSolrParams p = new ModifiableSolrParams();
p.add("collection", rulesColl);
p.add("action", "MODIFYCOLLECTION");
p.add("rule", "cores:<5");
p.add("rule", "node:*,replica:1");
p.add("rule", "freedisk:>"+minGB2);
p.add("autoAddReplicas", "true");
cluster.getSolrClient().request(new GenericSolrRequest(POST, COLLECTIONS_HANDLER_PATH, p));
DocCollection rulesCollection = getCollectionState(rulesColl);
log.info("version_of_coll {} ", rulesCollection.getZNodeVersion());
List list = (List) rulesCollection.get("rule");
assertEquals(3, list.size());
assertEquals("<5", ((Map) list.get(0)).get("cores"));
assertEquals("1", ((Map) list.get(1)).get("replica"));
assertEquals(">"+minGB2, ((Map) list.get(2)).get("freedisk"));
assertEquals("true", String.valueOf(rulesCollection.getProperties().get("autoAddReplicas")));
list = (List) rulesCollection.get("snitch");
assertEquals(1, list.size());
assertEquals("ImplicitSnitch", ((Map) list.get(0)).get("class"));
}
}