SOLR-6867: SolrCLI should check for existence before creating a new core/collection, more user-friendly error reporting (no stack trace), and the ability to pass a directory when using bin/solr to create a core or collection

git-svn-id: https://svn.apache.org/repos/asf/lucene/dev/trunk@1647726 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Timothy Potter 2014-12-24 04:00:49 +00:00
parent 4e65c4d1e0
commit 7204caba96
3 changed files with 403 additions and 233 deletions

View File

@ -542,6 +542,10 @@ Other Changes
* SOLR-6882: Misspelled collection API actions in ReplicaMutator exception messages.
(Steve Rowe via shalin)
* SOLR-6867: SolrCLI should check for existence before creating a new core/collection,
more user-friendly error reporting (no stack trace), and the ability to pass a
directory when using bin/solr to create a core or collection (Timothy Potter)
================== 4.10.3 ==================
Bug Fixes

View File

@ -215,29 +215,35 @@ function print_usage() {
echo ""
echo " -n <name> Name of core to create"
echo ""
echo " -c <configset> Name of configuration directory to use, valid options are:"
echo " -c <configset> Name of configuration directory to use, built-in options are:"
echo " basic_configs: Minimal Solr configuration"
echo " data_driven_schema_configs: Managed schema with field-guessing support enabled"
echo " sample_techproducts_configs: Example configuration with many optional features enabled to"
echo " demonstrate the full power of Solr"
echo " If not specified, default is: data_driven_schema_configs"
echo ""
echo " Alternatively, you can pass the path to your own configuration directory instead of using"
echo " one of the built-in configurations, such as: bin/solr create_core -n mycore -c /tmp/myconfig"
echo ""
elif [ "$CMD" == "create_collection" ]; then
echo ""
echo "Usage: solr create_collection [-n name] [-c configset] [-shards #] [-replicationFactor #]"
echo ""
echo " -n <name> Name of collection to create"
echo ""
echo " -c <configset> Name of configuration directory to use, valid options are:"
echo " -c <configset> Name of configuration directory to use, built-in options are:"
echo " basic_configs: Minimal Solr configuration"
echo " data_driven_schema_configs: Managed schema with field-guessing support enabled"
echo " sample_techproducts_configs: Example configuration with many optional features enabled to"
echo " demonstrate the full power of Solr"
echo " If not specified, default is: data_driven_schema_configs"
echo ""
echo " -shards <#> Number of shards to split the collection into"
echo " Alternatively, you can pass the path to your own configuration directory instead of using"
echo " one of the built-in configurations, such as: bin/solr create_collection -n mycoll -c /tmp/myconfig"
echo ""
echo " -replicationFactor <#> Number of copies of each document in the collection"
echo " -shards <#> Number of shards to split the collection into; default is 1"
echo ""
echo " -replicationFactor <#> Number of copies of each document in the collection, default is 1 (no replication)"
echo ""
echo ""
fi
@ -300,6 +306,7 @@ function run_tool() {
-classpath "$DEFAULT_SERVER_DIR/solr-webapp/webapp/WEB-INF/lib/*:$DEFAULT_SERVER_DIR/lib/ext/*" \
org.apache.solr.util.SolrCLI $*
return $?
} # end run_tool function
# get information about any Solr nodes running on this host
@ -548,6 +555,12 @@ if [[ "$SCRIPT_CMD" == "create_core" || "$SCRIPT_CMD" == "create_collection" ]];
CREATE_CONFIGSET=data_driven_schema_configs
fi
# validate the configset arg
if [[ ! -d "$SOLR_TIP/server/solr/configsets/$CREATE_CONFIGSET" && ! -d "$CREATE_CONFIGSET" ]]; then
echo -e "\nSpecified configset $CREATE_CONFIGSET not found!\n"
exit 1
fi
if [ -z "$CREATE_NAME" ]; then
echo "$CREATE_TYPE name is required!"
print_usage "$SCRIPT_CMD"
@ -573,12 +586,12 @@ if [[ "$SCRIPT_CMD" == "create_core" || "$SCRIPT_CMD" == "create_collection" ]];
if [ "$SCRIPT_CMD" == "create_collection" ]; then
run_tool create_collection -name $CREATE_NAME -shards $CREATE_NUM_SHARDS -replicationFactor $CREATE_REPFACT \
-config $CREATE_CONFIGSET -configsetsDir $SOLR_TIP/server/solr/configsets -solrUrl http://localhost:$CREATE_PORT/solr
exit $?
else
run_tool create_core -name $CREATE_NAME -solrUrl http://localhost:$CREATE_PORT/solr \
-config $CREATE_CONFIGSET -configsetsDir $SOLR_TIP/server/solr/configsets
exit $?
fi
exit $?
fi
@ -1186,9 +1199,15 @@ if [ "$EXAMPLE" != "cloud" ]; then
if [ "$SOLR_MODE" == "solrcloud" ]; then
run_tool create_collection -name $EXAMPLE -shards 1 -replicationFactor 1 \
-config $EXAMPLE_CONFIGSET -configsetsDir $SOLR_TIP/server/solr/configsets -solrUrl http://localhost:$SOLR_PORT/solr
if [ $? -ne 0 ]; then
exit 1
fi
else
run_tool create_core -name $EXAMPLE -solrUrl http://localhost:$SOLR_PORT/solr \
-config $EXAMPLE_CONFIGSET -configsetsDir $SOLR_TIP/server/solr/configsets
if [ $? -ne 0 ]; then
exit 1
fi
fi
if [ "$EXAMPLE" == "techproducts" ]; then
@ -1285,13 +1304,26 @@ else
USER_INPUT=
echo "Please choose a configuration for the $CLOUD_COLLECTION collection, available options are:"
read -e -p "basic_configs, data_driven_schema_configs, or sample_techproducts_configs [data_driven_schema_configs] " USER_INPUT
# trim whitespace out of the user input
CLOUD_CONFIG=`echo $USER_INPUT | tr -d ' '`
while true
do
# trim whitespace out of the user input
CLOUD_CONFIG=`echo $USER_INPUT | tr -d ' '`
# handle the default selection or empty input
if [ -z "$CLOUD_CONFIG" ]; then
CLOUD_CONFIG=data_driven_schema_configs
fi
# validate the configset arg
if [[ ! -d "$SOLR_TIP/server/solr/configsets/$CLOUD_CONFIG" && ! -d "$CLOUD_CONFIG" ]]; then
echo -e "\nOops! Specified configuration $CLOUD_CONFIG not found!"
read -e -p "Choose one of: basic_configs, data_driven_schema_configs, or sample_techproducts_configs [data_driven_schema_configs] " USER_INPUT
CLOUD_CONFIG=
else
break;
fi
done
# handle the default selection or empty input
if [ -z "$CLOUD_CONFIG" ]; then
CLOUD_CONFIG=data_driven_schema_configs
fi
fi
run_tool create_collection -name $CLOUD_COLLECTION -shards $CLOUD_NUM_SHARDS -replicationFactor $CLOUD_REPFACT \

View File

@ -111,6 +111,7 @@ public class SolrCLI {
String zkHost = cli.getOptionValue("zkHost", ZK_HOST);
log.debug("Connecting to Solr cluster: " + zkHost);
int exitStatus = 0;
CloudSolrServer cloudSolrServer = null;
try {
cloudSolrServer = new CloudSolrServer(zkHost);
@ -119,8 +120,17 @@ public class SolrCLI {
if (collection != null)
cloudSolrServer.setDefaultCollection(collection);
cloudSolrServer.connect();
runCloudTool(cloudSolrServer, cli);
cloudSolrServer.connect();
exitStatus = runCloudTool(cloudSolrServer, cli);
} catch (Exception exc) {
// since this is a CLI, spare the user the stacktrace
String excMsg = exc.getMessage();
if (excMsg != null) {
System.err.println("\nERROR:"+excMsg+"\n");
exitStatus = 1;
} else {
throw exc;
}
} finally {
if (cloudSolrServer != null) {
try {
@ -129,13 +139,13 @@ public class SolrCLI {
}
}
return 0;
return exitStatus;
}
/**
* Runs a SolrCloud tool with CloudSolrServer initialized
*/
protected abstract void runCloudTool(CloudSolrServer cloudSolrServer, CommandLine cli)
protected abstract int runCloudTool(CloudSolrServer cloudSolrServer, CommandLine cli)
throws Exception;
}
@ -182,9 +192,7 @@ public class SolrCLI {
processCommandLineArgs(joinCommonAndToolOptions(tool.getOptions()), toolArgs);
// run the tool
int exitCode = tool.runTool(cli);
System.exit(exitCode);
System.exit(tool.runTool(cli));
}
/**
@ -327,9 +335,8 @@ public class SolrCLI {
for (String classInPackage : classes) {
Class<?> theClass = Class.forName(classInPackage);
if (Tool.class.isAssignableFrom(theClass)) {
if (Tool.class.isAssignableFrom(theClass))
toolClasses.add((Class<Tool>) theClass);
}
}
} catch (Exception e) {
// safe to squelch this as it's just looking for tools to run
@ -350,9 +357,8 @@ public class SolrCLI {
if (entry.getName().endsWith(".class")) {
String className = entry.getName().replaceAll("[$].*", "")
.replaceAll("[.]class", "").replace('/', '.');
if (className.startsWith(packageName)) {
if (className.startsWith(packageName))
classes.add(className);
}
}
}
}
@ -367,9 +373,9 @@ public class SolrCLI {
Throwable rootCause = SolrException.getRootCause(exc);
boolean wasCommError =
(rootCause instanceof ConnectException ||
rootCause instanceof ConnectTimeoutException ||
rootCause instanceof NoHttpResponseException ||
rootCause instanceof SocketException);
rootCause instanceof ConnectTimeoutException ||
rootCause instanceof NoHttpResponseException ||
rootCause instanceof SocketException);
return wasCommError;
}
@ -470,7 +476,7 @@ public class SolrCLI {
String errMsg = asString("/error/msg", json);
if (errMsg == null)
errMsg = String.valueOf(json);
throw new SolrServerException("Request to "+getUrl+" failed due to: "+errMsg);
throw new SolrServerException(errMsg);
}
return json;
}
@ -623,7 +629,9 @@ public class SolrCLI {
throws Exception
{
Map<String,Object> status = new LinkedHashMap<String,Object>();
String solrHome = (String)info.get("solr_home");
status.put("solr_home", solrHome != null ? solrHome : "?");
status.put("version", asString("/lucene/solr-impl-version", info));
status.put("startTime", asString("/jvm/jmx/startTime", info));
status.put("uptime", uptime(asLong("/jvm/jmx/upTimeMS", info)));
@ -706,198 +714,6 @@ public class SolrCLI {
private static final String DEFAULT_CONFIG_SET = "data_driven_schema_configs";
/**
* Supports create_collection command in the bin/solr script.
*/
public static class CreateCollectionTool implements Tool {
@Override
public String getName() {
return "create_collection";
}
@SuppressWarnings("static-access")
@Override
public Option[] getOptions() {
return new Option[] {
OptionBuilder
.withArgName("HOST")
.hasArg()
.isRequired(false)
.withDescription("Address of the Zookeeper ensemble; defaults to: "+ZK_HOST)
.create("zkHost"),
OptionBuilder
.withArgName("HOST")
.hasArg()
.isRequired(false)
.withDescription("Base Solr URL, which can be used to determine the zkHost if that's not known")
.create("solrUrl"),
OptionBuilder
.withArgName("NAME")
.hasArg()
.isRequired(true)
.withDescription("Name of collection to create.")
.create("name"),
OptionBuilder
.withArgName("#")
.hasArg()
.isRequired(false)
.withDescription("Number of shards; default is 1")
.create("shards"),
OptionBuilder
.withArgName("#")
.hasArg()
.isRequired(false)
.withDescription("Number of copies of each document across the collection (replicas per shard); default is 1")
.create("replicationFactor"),
OptionBuilder
.withArgName("#")
.hasArg()
.isRequired(false)
.withDescription("Maximum number of shards per Solr node; default is determined based on the number of shards, replication factor, and live nodes.")
.create("maxShardsPerNode"),
OptionBuilder
.withArgName("NAME")
.hasArg()
.isRequired(false)
.withDescription("Name of the configuration for this collection; default is "+DEFAULT_CONFIG_SET)
.create("config"),
OptionBuilder
.withArgName("DIR")
.hasArg()
.isRequired(true)
.withDescription("Path to configsets directory on the local system.")
.create("configsetsDir")
};
}
public int runTool(CommandLine cli) throws Exception {
// quiet down the ZK logging for cli tools
LogManager.getLogger("org.apache.zookeeper").setLevel(Level.ERROR);
LogManager.getLogger("org.apache.solr.common.cloud").setLevel(Level.WARN);
String zkHost = cli.getOptionValue("zkHost");
if (zkHost == null) {
// find it using the localPort
String solrUrl = cli.getOptionValue("solrUrl");
if (solrUrl == null)
throw new IllegalStateException(
"Must provide either the -zkHost or -solrUrl parameters to use the create_collection command!");
if (!solrUrl.endsWith("/"))
solrUrl += "/";
String systemInfoUrl = solrUrl+"admin/info/system";
HttpClient httpClient = getHttpClient();
try {
// hit Solr to get system info
Map<String,Object> systemInfo = getJson(httpClient, systemInfoUrl, 2);
// convert raw JSON into user-friendly output
StatusTool statusTool = new StatusTool();
Map<String,Object> status = statusTool.reportStatus(solrUrl, systemInfo, httpClient);
Map<String,Object> cloud = (Map<String, Object>)status.get("cloud");
if (cloud == null) {
System.err.println("\nERROR: Solr at "+solrUrl+
" is running in standalone server mode, please use the create_core command instead;\n" +
"create_collection can only be used when running in SolrCloud mode.\n");
return 1;
}
String zookeeper = (String) cloud.get("ZooKeeper");
if (zookeeper.endsWith("(embedded)")) {
zookeeper = zookeeper.substring(0,zookeeper.length()-"(embedded)".length());
}
zkHost = zookeeper;
} finally {
closeHttpClient(httpClient);
}
}
CloudSolrServer cloudSolrServer = null;
try {
cloudSolrServer = new CloudSolrServer(zkHost);
System.out.println("Connecting to ZooKeeper at "+zkHost);
cloudSolrServer.connect();
runCloudTool(cloudSolrServer, cli);
} finally {
if (cloudSolrServer != null) {
try {
cloudSolrServer.shutdown();
} catch (Exception ignore) {}
}
}
return 0;
}
protected void runCloudTool(CloudSolrServer cloudSolrServer, CommandLine cli) throws Exception {
Set<String> liveNodes = cloudSolrServer.getZkStateReader().getClusterState().getLiveNodes();
if (liveNodes.isEmpty())
throw new IllegalStateException("No live nodes found! Cannot create a collection until " +
"there is at least 1 live node in the cluster.");
String firstLiveNode = liveNodes.iterator().next();
// build a URL to create the collection
int numShards = optionAsInt(cli, "shards", 1);
int replicationFactor = optionAsInt(cli, "replicationFactor", 1);
int maxShardsPerNode = -1;
if (cli.hasOption("maxShardsPerNode")) {
maxShardsPerNode = Integer.parseInt(cli.getOptionValue("maxShardsPerNode"));
} else {
// need number of live nodes to determine maxShardsPerNode if it is not set
int numNodes = liveNodes.size();
maxShardsPerNode = ((numShards*replicationFactor)+numNodes-1)/numNodes;
}
String configSet = cli.getOptionValue("config", DEFAULT_CONFIG_SET);
// first, test to see if that config exists in ZK
if (!cloudSolrServer.getZkStateReader().getZkClient().exists("/configs/"+configSet, true)) {
File configsetsDir = new File(cli.getOptionValue("configsetsDir"));
if (!configsetsDir.isDirectory())
throw new FileNotFoundException(configsetsDir.getAbsolutePath()+" not found!");
// upload the configset if it exists
File configSetDir = new File(configsetsDir, configSet);
if (!configSetDir.isDirectory())
throw new FileNotFoundException("Specified config "+configSet+
" not found in "+configsetsDir.getAbsolutePath());
File confDir = new File(configSetDir,"conf");
System.out.println("Uploading "+confDir.getAbsolutePath()+
" for config "+configSet+" to ZooKeeper at "+cloudSolrServer.getZkHost());
ZkController.uploadConfigDir(cloudSolrServer.getZkStateReader().getZkClient(), confDir, configSet);
}
String baseUrl = cloudSolrServer.getZkStateReader().getBaseUrlForNodeName(firstLiveNode);
String collectionName = cli.getOptionValue("name");
String createCollectionUrl =
String.format(Locale.ROOT,
"%s/admin/collections?action=CREATE&name=%s&numShards=%d&replicationFactor=%d&maxShardsPerNode=%d&collection.configName=%s",
baseUrl,
collectionName,
numShards,
replicationFactor,
maxShardsPerNode,
configSet);
System.out.println("Creating new collection '"+collectionName+"' using command:\n\n"+createCollectionUrl+"\n");
Map<String,Object> json = getJson(createCollectionUrl);
CharArr arr = new CharArr();
new JSONWriter(arr, 2).write(json);
System.out.println(arr.toString());
System.out.println();
}
protected int optionAsInt(CommandLine cli, String option, int defaultVal) {
return Integer.parseInt(cli.getOptionValue(option, String.valueOf(defaultVal)));
}
} // end CreateCollectionTool class
private static final long MS_IN_MIN = 60 * 1000L;
private static final long MS_IN_HOUR = MS_IN_MIN * 60L;
private static final long MS_IN_DAY = MS_IN_HOUR * 24L;
@ -1056,7 +872,7 @@ public class SolrCLI {
}
@Override
protected void runCloudTool(CloudSolrServer cloudSolrServer, CommandLine cli) throws Exception {
protected int runCloudTool(CloudSolrServer cloudSolrServer, CommandLine cli) throws Exception {
String collection = cli.getOptionValue("collection");
if (collection == null)
@ -1171,9 +987,273 @@ public class SolrCLI {
CharArr arr = new CharArr();
new JSONWriter(arr, 2).write(report);
System.out.println(arr.toString());
return 0;
}
} // end HealthcheckTool
/**
* Supports create_collection command in the bin/solr script.
*/
public static class CreateCollectionTool implements Tool {
@Override
public String getName() {
return "create_collection";
}
@SuppressWarnings("static-access")
@Override
public Option[] getOptions() {
return new Option[] {
OptionBuilder
.withArgName("HOST")
.hasArg()
.isRequired(false)
.withDescription("Address of the Zookeeper ensemble; defaults to: "+ZK_HOST)
.create("zkHost"),
OptionBuilder
.withArgName("HOST")
.hasArg()
.isRequired(false)
.withDescription("Base Solr URL, which can be used to determine the zkHost if that's not known")
.create("solrUrl"),
OptionBuilder
.withArgName("NAME")
.hasArg()
.isRequired(true)
.withDescription("Name of collection to create.")
.create("name"),
OptionBuilder
.withArgName("#")
.hasArg()
.isRequired(false)
.withDescription("Number of shards; default is 1")
.create("shards"),
OptionBuilder
.withArgName("#")
.hasArg()
.isRequired(false)
.withDescription("Number of copies of each document across the collection (replicas per shard); default is 1")
.create("replicationFactor"),
OptionBuilder
.withArgName("#")
.hasArg()
.isRequired(false)
.withDescription("Maximum number of shards per Solr node; default is determined based on the number of shards, replication factor, and live nodes.")
.create("maxShardsPerNode"),
OptionBuilder
.withArgName("NAME")
.hasArg()
.isRequired(false)
.withDescription("Name of the configuration for this collection; default is "+DEFAULT_CONFIG_SET)
.create("config"),
OptionBuilder
.withArgName("DIR")
.hasArg()
.isRequired(true)
.withDescription("Path to configsets directory on the local system.")
.create("configsetsDir")
};
}
public int runTool(CommandLine cli) throws Exception {
// quiet down the ZK logging for cli tools
LogManager.getLogger("org.apache.zookeeper").setLevel(Level.ERROR);
LogManager.getLogger("org.apache.solr.common.cloud").setLevel(Level.WARN);
String zkHost = cli.getOptionValue("zkHost");
if (zkHost == null) {
// find it using the localPort
String solrUrl = cli.getOptionValue("solrUrl");
if (solrUrl == null)
throw new IllegalStateException(
"Must provide either the -zkHost or -solrUrl parameters to use the create_collection command!");
if (!solrUrl.endsWith("/"))
solrUrl += "/";
String systemInfoUrl = solrUrl+"admin/info/system";
HttpClient httpClient = getHttpClient();
try {
// hit Solr to get system info
Map<String,Object> systemInfo = getJson(httpClient, systemInfoUrl, 2);
// convert raw JSON into user-friendly output
StatusTool statusTool = new StatusTool();
Map<String,Object> status = statusTool.reportStatus(solrUrl, systemInfo, httpClient);
Map<String,Object> cloud = (Map<String, Object>)status.get("cloud");
if (cloud == null) {
System.err.println("\nERROR: Solr at "+solrUrl+
" is running in standalone server mode, please use the create_core command instead;\n" +
"create_collection can only be used when running in SolrCloud mode.\n");
return 1;
}
String zookeeper = (String) cloud.get("ZooKeeper");
if (zookeeper.endsWith("(embedded)")) {
zookeeper = zookeeper.substring(0,zookeeper.length()-"(embedded)".length());
}
zkHost = zookeeper;
} finally {
closeHttpClient(httpClient);
}
}
int toolExitStatus = 0;
CloudSolrServer cloudSolrServer = null;
try {
cloudSolrServer = new CloudSolrServer(zkHost);
System.out.println("Connecting to ZooKeeper at " + zkHost);
cloudSolrServer.connect();
toolExitStatus = runCloudTool(cloudSolrServer, cli);
} catch (Exception exc) {
// since this is a CLI, spare the user the stacktrace
String excMsg = exc.getMessage();
if (excMsg != null) {
System.err.println("\nERROR: "+excMsg+"\n");
toolExitStatus = 1;
} else {
throw exc;
}
} finally {
if (cloudSolrServer != null) {
try {
cloudSolrServer.shutdown();
} catch (Exception ignore) {}
}
}
return toolExitStatus;
}
protected int runCloudTool(CloudSolrServer cloudSolrServer, CommandLine cli) throws Exception {
Set<String> liveNodes = cloudSolrServer.getZkStateReader().getClusterState().getLiveNodes();
if (liveNodes.isEmpty())
throw new IllegalStateException("No live nodes found! Cannot create a collection until " +
"there is at least 1 live node in the cluster.");
String firstLiveNode = liveNodes.iterator().next();
// build a URL to create the collection
int numShards = optionAsInt(cli, "shards", 1);
int replicationFactor = optionAsInt(cli, "replicationFactor", 1);
int maxShardsPerNode = -1;
if (cli.hasOption("maxShardsPerNode")) {
maxShardsPerNode = Integer.parseInt(cli.getOptionValue("maxShardsPerNode"));
} else {
// need number of live nodes to determine maxShardsPerNode if it is not set
int numNodes = liveNodes.size();
maxShardsPerNode = ((numShards*replicationFactor)+numNodes-1)/numNodes;
}
String configSet = cli.getOptionValue("config", DEFAULT_CONFIG_SET);
String configSetNameInZk = configSet;
File configSetDir = null;
// we try to be flexible and allow the user to specify a configuration directory instead of a configset name
File possibleConfigDir = new File(configSet);
if (possibleConfigDir.isDirectory()) {
configSetDir = possibleConfigDir;
configSetNameInZk = possibleConfigDir.getName();
} else {
File configsetsDir = new File(cli.getOptionValue("configsetsDir"));
if (!configsetsDir.isDirectory())
throw new FileNotFoundException(configsetsDir.getAbsolutePath()+" not found!");
// upload the configset if it exists
configSetDir = new File(configsetsDir, configSet);
if (!configSetDir.isDirectory()) {
throw new FileNotFoundException("Specified config " + configSet +
" not found in " + configsetsDir.getAbsolutePath());
}
}
File confDir = new File(configSetDir, "conf");
if (!confDir.isDirectory()) {
// config dir should contain a conf sub-directory but if not and there's a solrconfig.xml, then use it
if ((new File(configSetDir, "solrconfig.xml")).isFile()) {
confDir = configSetDir;
} else {
System.err.println("Specified configuration directory "+configSetDir.getAbsolutePath()+
" is invalid;\nit should contain either conf sub-directory or solrconfig.xml");
return 1;
}
}
// test to see if that config exists in ZK
if (!cloudSolrServer.getZkStateReader().getZkClient().exists("/configs/"+configSetNameInZk, true)) {
System.out.println("Uploading "+confDir.getAbsolutePath()+
" for config "+configSetNameInZk+" to ZooKeeper at "+cloudSolrServer.getZkHost());
ZkController.uploadConfigDir(cloudSolrServer.getZkStateReader().getZkClient(), confDir, configSetNameInZk);
}
String baseUrl = cloudSolrServer.getZkStateReader().getBaseUrlForNodeName(firstLiveNode);
String collectionName = cli.getOptionValue("name");
// since creating a collection is a heavy-weight operation, check for existence first
String collectionListUrl = baseUrl+"/admin/collections?action=list";
if (safeCheckCollectionExists(collectionListUrl, collectionName)) {
System.err.println("\nCollection '"+collectionName+"' already exists!");
System.err.println("\nChecked collection existence using Collections API command:\n"+collectionListUrl);
System.err.println();
return 1;
}
// doesn't seem to exist ... try to create
String createCollectionUrl =
String.format(Locale.ROOT,
"%s/admin/collections?action=CREATE&name=%s&numShards=%d&replicationFactor=%d&maxShardsPerNode=%d&collection.configName=%s",
baseUrl,
collectionName,
numShards,
replicationFactor,
maxShardsPerNode,
configSetNameInZk);
System.out.println("\nCreating new collection '"+collectionName+"' using command:\n"+createCollectionUrl+"\n");
Map<String,Object> json = null;
try {
json = getJson(createCollectionUrl);
} catch (SolrServerException sse) {
// check if already exists
if (safeCheckCollectionExists(collectionListUrl, collectionName)) {
System.err.println("Collection '"+collectionName+"' already exists!");
System.err.println("\nChecked collection existence using Collections API command:\n"+collectionListUrl);
} else {
System.err.println("Failed to create collection '"+collectionName+"' due to: "+sse.getMessage());
}
System.err.println();
return 1;
}
CharArr arr = new CharArr();
new JSONWriter(arr, 2).write(json);
System.out.println(arr.toString());
System.out.println();
return 0;
}
protected boolean safeCheckCollectionExists(String url, String collection) {
boolean exists = false;
try {
Map<String,Object> existsCheckResult = getJson(url);
List<String> collections = (List<String>) existsCheckResult.get("collections");
exists = collections != null && collections.contains(collection);
} catch (Exception exc) {
// just ignore it since we're only interested in a positive result here
}
return exists;
}
protected int optionAsInt(CommandLine cli, String option, int defaultVal) {
return Integer.parseInt(cli.getOptionValue(option, String.valueOf(defaultVal)));
}
} // end CreateCollectionTool class
public static class CreateCoreTool implements Tool {
@Override
@ -1221,15 +1301,20 @@ public class SolrCLI {
File configsetsDir = new File(cli.getOptionValue("configsetsDir"));
if (!configsetsDir.isDirectory())
throw new FileNotFoundException(configsetsDir.getAbsolutePath()+" not found!");
throw new FileNotFoundException(configsetsDir.getAbsolutePath() + " not found!");
String configSet = cli.getOptionValue("config", DEFAULT_CONFIG_SET);
File configSetDir = new File(configsetsDir, configSet);
if (!configSetDir.isDirectory())
throw new FileNotFoundException("Specified config "+configSet+
" not found in "+configsetsDir.getAbsolutePath());
File confDir = new File(configSetDir,"conf");
if (!configSetDir.isDirectory()) {
// we allow them to pass a directory instead of a configset name
File possibleConfigDir = new File(configSet);
if (possibleConfigDir.isDirectory()) {
configSetDir = possibleConfigDir;
} else {
throw new FileNotFoundException("Specified config " + configSet +
" not found in " + configsetsDir.getAbsolutePath());
}
}
String coreName = cli.getOptionValue("name");
@ -1246,21 +1331,42 @@ public class SolrCLI {
// convert raw JSON into user-friendly output
solrHome = (String)systemInfo.get("solr_home");
if (solrHome == null) {
if (solrHome == null)
solrHome = configsetsDir.getParentFile().getAbsolutePath();
}
} finally {
closeHttpClient(httpClient);
}
String coreStatusUrl = solrUrl+"admin/cores?action=STATUS&core="+coreName;
if (safeCheckCoreExists(coreStatusUrl, coreName)) {
System.err.println("\nCore '"+coreName+"' already exists!");
System.err.println("\nChecked core existence using Core API command:\n"+coreStatusUrl);
System.err.println();
return 1;
}
File coreInstanceDir = new File(solrHome, coreName);
File confDir = new File(configSetDir,"conf");
if (!coreInstanceDir.isDirectory()) {
coreInstanceDir.mkdirs();
if (!coreInstanceDir.isDirectory())
throw new IOException("Failed to create new core instance directory: "+coreInstanceDir.getAbsolutePath());
}
FileUtils.copyDirectoryToDirectory(confDir, coreInstanceDir);
if (confDir.isDirectory()) {
FileUtils.copyDirectoryToDirectory(confDir, coreInstanceDir);
} else {
// hmmm ... the configset we're cloning doesn't have a conf sub-directory,
// we'll just assume it is OK if it has solrconfig.xml
if ((new File(configSetDir, "solrconfig.xml")).isFile()) {
FileUtils.copyDirectory(configSetDir, new File(coreInstanceDir, "conf"));
} else {
System.err.println("\n"+configSetDir.getAbsolutePath()+" doesn't contain a conf subdirectory or solrconfig.xml\n");
return 1;
}
}
System.out.println("\nSetup new core instance directory:\n"+coreInstanceDir.getAbsolutePath());
}
String createCoreUrl =
String.format(Locale.ROOT,
@ -1269,9 +1375,24 @@ public class SolrCLI {
coreName,
coreName);
System.out.println("Creating new core '"+coreName+"' using command:\n\n"+createCoreUrl+"\n");
System.out.println("\nCreating new core '"+coreName+"' using command:\n"+createCoreUrl+"\n");
Map<String,Object> json = null;
try {
json = getJson(createCoreUrl);
} catch (SolrServerException sse) {
// mostly likely the core already exists ...
if (safeCheckCoreExists(coreStatusUrl, coreName)) {
// core already exists
System.err.println("Core '"+coreName+"' already exists!");
System.err.println("\nChecked core existence using Core API command:\n"+coreStatusUrl);
} else {
System.err.println("Failed to create core '"+coreName+"' due to: "+sse.getMessage());
}
System.err.println();
return 1;
}
Map<String,Object> json = getJson(createCoreUrl);
CharArr arr = new CharArr();
new JSONWriter(arr, 2).write(json);
System.out.println(arr.toString());
@ -1279,5 +1400,18 @@ public class SolrCLI {
return 0;
}
protected boolean safeCheckCoreExists(String coreStatusUrl, String coreName) {
boolean exists = false;
try {
Map<String,Object> existsCheckResult = getJson(coreStatusUrl);
Map<String,Object> status = (Map<String, Object>)existsCheckResult.get("status");
Map<String,Object> coreStatus = (Map<String, Object>)status.get(coreName);
exists = coreStatus != null && coreStatus.containsKey("name");
} catch (Exception exc) {
// just ignore it since we're only interested in a positive result here
}
return exists;
}
} // end CreateCoreTool class
}