You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@lucene.apache.org by th...@apache.org on 2015/01/16 07:36:15 UTC
svn commit: r1652345 - in /lucene/dev/trunk/solr: CHANGES.txt bin/solr
bin/solr.cmd core/src/java/org/apache/solr/util/SolrCLI.java
Author: thelabdude
Date: Fri Jan 16 06:36:15 2015
New Revision: 1652345
URL: http://svn.apache.org/r1652345
Log:
SOLR-6981: add a delete action to the bin/solr script
Modified:
lucene/dev/trunk/solr/CHANGES.txt
lucene/dev/trunk/solr/bin/solr
lucene/dev/trunk/solr/bin/solr.cmd
lucene/dev/trunk/solr/core/src/java/org/apache/solr/util/SolrCLI.java
Modified: lucene/dev/trunk/solr/CHANGES.txt
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/solr/CHANGES.txt?rev=1652345&r1=1652344&r2=1652345&view=diff
==============================================================================
--- lucene/dev/trunk/solr/CHANGES.txt (original)
+++ lucene/dev/trunk/solr/CHANGES.txt Fri Jan 16 06:36:15 2015
@@ -716,6 +716,9 @@ Other Changes
* SOLR-6982: bin/solr and SolrCLI should support SSL-related Java System Properties
(Timothy Potter)
+* SOLR-6981: Add a delete action to the bin/solr script to allow deleting of cores /
+ collections (with delete collection config directory from ZK) (Timothy Potter)
+
================== 4.10.3 ==================
Bug Fixes
Modified: lucene/dev/trunk/solr/bin/solr
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/solr/bin/solr?rev=1652345&r1=1652344&r2=1652345&view=diff
==============================================================================
--- lucene/dev/trunk/solr/bin/solr (original)
+++ lucene/dev/trunk/solr/bin/solr Fri Jan 16 06:36:15 2015
@@ -129,7 +129,7 @@ function print_usage() {
if [ -z "$CMD" ]; then
echo ""
echo "Usage: solr COMMAND OPTIONS"
- echo " where COMMAND is one of: start, stop, restart, status, healthcheck, create, create_core, create_collection"
+ echo " where COMMAND is one of: start, stop, restart, status, healthcheck, create, create_core, create_collection, delete"
echo ""
echo " Standalone server example (start Solr running in the background on port 8984):"
echo ""
@@ -229,6 +229,23 @@ function print_usage() {
echo ""
echo " bin/solr create_collection -help"
echo ""
+ elif [ "$CMD" == "delete" ]; then
+ echo ""
+ echo "Usage: solr delete [-c name] [-deleteConfig true|false] [-p port]"
+ echo ""
+ echo " Deletes a core or collection depending on whether Solr is running in standalone (core) or SolrCloud"
+ echo " mode (collection). If you're deleting a collection in SolrCloud mode, the default behavior is to also"
+ echo " delete the configuration directory from ZooKeeper so long as it is not being used by another collection."
+ echo " You can override this behavior by passing -deleteConfig false when running this command."
+ echo ""
+ echo " -c <name> Name of the core / collection to delete"
+ echo ""
+ echo " -deleteConfig <boolean> Delete the configuration directory from ZooKeeper; default is true"
+ echo ""
+ echo " -p <port> Port of a local Solr instance where you want to delete the core/collection"
+ echo " If not specified, the script will search the local system for a running"
+ echo " Solr instance and will use the port of the first server it finds."
+ echo ""
elif [ "$CMD" == "create_core" ]; then
echo ""
echo "Usage: solr create_core [-c core] [-d confdir] [-p port]"
@@ -645,6 +662,87 @@ if [[ "$SCRIPT_CMD" == "create" || "$SCR
fi
fi
+# delete a core or collection
+if [[ "$SCRIPT_CMD" == "delete" ]]; then
+
+ if [ $# -gt 0 ]; then
+ while true; do
+ case $1 in
+ -c|-core|-collection)
+ if [[ -z "$2" || "${2:0:1}" == "-" ]]; then
+ print_usage "$SCRIPT_CMD" "name is required when using the $1 option!"
+ exit 1
+ fi
+ DELETE_NAME=$2
+ shift 2
+ ;;
+ -p|-port)
+ if [[ -z "$2" || "${2:0:1}" == "-" ]]; then
+ print_usage "$SCRIPT_CMD" "Solr port is required when using the $1 option!"
+ exit 1
+ fi
+ DELETE_PORT="$2"
+ shift 2
+ ;;
+ -deleteConfig)
+ if [[ -z "$2" || "${2:0:1}" == "-" ]]; then
+ print_usage "$SCRIPT_CMD" "true|false is required when using the $1 option!"
+ exit 1
+ fi
+ DELETE_CONFIG="$2"
+ shift 2
+ ;;
+ -help|-usage)
+ print_usage "$SCRIPT_CMD"
+ exit 0
+ ;;
+ --)
+ shift
+ break
+ ;;
+ *)
+ if [ "$1" != "" ]; then
+ print_usage "$SCRIPT_CMD" "Unrecognized or misplaced argument: $1!"
+ exit 1
+ else
+ break # out-of-args, stop looping
+ fi
+ ;;
+ esac
+ done
+ fi
+
+ if [ -z "$DELETE_NAME" ]; then
+ echo "Name (-c) argument is required!"
+ print_usage "$SCRIPT_CMD"
+ exit 1
+ fi
+
+ # If not defined, use the collection name for the name of the configuration in ZooKeeper
+ if [ -z "$DELETE_CONFIG" ]; then
+ DELETE_CONFIG=true
+ fi
+
+ if [ -z "$DELETE_PORT" ]; then
+ for ID in `ps auxww | grep java | grep start.jar | awk '{print $2}' | sort -r`
+ do
+ port=`jetty_port "$ID"`
+ if [ "$port" != "" ]; then
+ DELETE_PORT=$port
+ break
+ fi
+ done
+ fi
+
+ if [ -z "$DELETE_PORT" ]; then
+ echo "Failed to determine the port of a local Solr instance, cannot delete $DELETE_NAME!"
+ exit 1
+ fi
+
+ run_tool delete -name $DELETE_NAME -deleteConfig $DELETE_CONFIG \
+ -solrUrl $SOLR_URL_SCHEME://localhost:$DELETE_PORT/solr
+ exit $?
+fi
# verify the command given is supported
if [ "$SCRIPT_CMD" != "stop" ] && [ "$SCRIPT_CMD" != "start" ] && [ "$SCRIPT_CMD" != "restart" ] && [ "$SCRIPT_CMD" != "status" ]; then
Modified: lucene/dev/trunk/solr/bin/solr.cmd
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/solr/bin/solr.cmd?rev=1652345&r1=1652344&r2=1652345&view=diff
==============================================================================
--- lucene/dev/trunk/solr/bin/solr.cmd (original)
+++ lucene/dev/trunk/solr/bin/solr.cmd Fri Jan 16 06:36:15 2015
@@ -96,6 +96,11 @@ IF "%1"=="create_collection" (
SHIFT
goto parse_create_args
)
+IF "%1"=="delete" (
+ set SCRIPT_CMD=delete
+ SHIFT
+ goto parse_delete_args
+)
goto parse_args
:usage
@@ -111,12 +116,13 @@ IF "%SCRIPT_CMD%"=="healthcheck" goto he
IF "%SCRIPT_CMD%"=="create" goto create_usage
IF "%SCRIPT_CMD%"=="create_core" goto create_core_usage
IF "%SCRIPT_CMD%"=="create_collection" goto create_collection_usage
+IF "%SCRIPT_CMD%"=="delete" goto delete_usage
goto done
:script_usage
@echo.
@echo Usage: solr COMMAND OPTIONS
-@echo where COMMAND is one of: start, stop, restart, healthcheck, create, create_core, create_collection
+@echo where COMMAND is one of: start, stop, restart, healthcheck, create, create_core, create_collection, delete
@echo.
@echo Standalone server example (start Solr running in the background on port 8984):
@echo.
@@ -215,6 +221,25 @@ echo bin\solr create_collection -hel
echo.
goto done
+:delete_usage
+echo.
+echo Usage: solr delete [-c name] [-deleteConfig boolean] [-p port]
+echo.
+echo Deletes a core or collection depending on whether Solr is running in standalone (core) or SolrCloud
+echo mode (collection). If you're deleting a collection in SolrCloud mode, the default behavior is to also
+echo delete the configuration directory from ZooKeeper so long as it is not being used by another collection.
+echo You can override this behavior by passing -deleteConfig false when running this command.
+echo.
+echo -c name Name of core to create
+echo.
+echo -deleteConfig boolean Delete the configuration directory from ZooKeeper; default is true
+echo.
+echo -p port Port of a local Solr instance where you want to create the new core
+echo If not specified, the script will search the local system for a running
+echo Solr instance and will use the port of the first server it finds.
+echo.
+goto done
+
:create_core_usage
echo.
echo Usage: solr create_core [-c name] [-d confdir] [-p port]
@@ -1142,6 +1167,72 @@ if "%SCRIPT_CMD%"=="create_core" (
goto done
+:parse_delete_args
+IF [%1]==[] goto run_delete
+IF "%1"=="-c" goto set_delete_name
+IF "%1"=="-core" goto set_delete_name
+IF "%1"=="-collection" goto set_delete_name
+IF "%1"=="-p" goto set_delete_port
+IF "%1"=="-port" goto set_delete_port
+IF "%1"=="-deleteConfig" goto set_delete_config
+IF "%1"=="-help" goto usage
+IF "%1"=="-usage" goto usage
+IF "%1"=="/?" goto usage
+goto run_delete
+
+:set_delete_name
+set DELETE_NAME=%~2
+SHIFT
+SHIFT
+goto parse_delete_args
+
+:set_delete_port
+set DELETE_PORT=%~2
+SHIFT
+SHIFT
+goto parse_delete_args
+
+:set_delete_config
+set DELETE_CONFIG=%~2
+SHIFT
+SHIFT
+goto parse_delete_args
+
+:run_delete
+IF "!DELETE_NAME!"=="" (
+ set "SCRIPT_ERROR=Name (-c) is a required parameter for %SCRIPT_CMD%"
+ goto invalid_cmd_line
+)
+
+REM Find a port that Solr is running on
+if "!DELETE_PORT!"=="" (
+ for /f "usebackq" %%i in (`dir /b %SOLR_TIP%\bin ^| findstr /i "^solr-.*\.port$"`) do (
+ set SOME_SOLR_PORT=
+ For /F "Delims=" %%J In (%SOLR_TIP%\bin\%%i) do set SOME_SOLR_PORT=%%~J
+ if NOT "!SOME_SOLR_PORT!"=="" (
+ for /f "tokens=2,5" %%j in ('netstat -aon ^| find /i "listening" ^| find /i "!SOME_SOLR_PORT!"') do (
+ set DELETE_PORT=!SOME_SOLR_PORT!
+ )
+ )
+ )
+)
+if "!DELETE_PORT!"=="" (
+ set "SCRIPT_ERROR=Could not find a running Solr instance on this host! Please use the -p option to specify the port."
+ goto err
+)
+
+if "!DELETE_CONFIG!"=="" (
+ set DELETE_CONFIG=true
+)
+
+"%JAVA%" %SOLR_SSL_OPTS% -Dsolr.install.dir="%SOLR_TIP%" -Dlog4j.configuration="file:%DEFAULT_SERVER_DIR%\scripts\cloud-scripts\log4j.properties" ^
+-classpath "%DEFAULT_SERVER_DIR%\solr-webapp\webapp\WEB-INF\lib\*;%DEFAULT_SERVER_DIR%\lib\ext\*" ^
+org.apache.solr.util.SolrCLI delete -name !DELETE_NAME! -deleteConfig !DELETE_CONFIG! ^
+-solrUrl !SOLR_URL_SCHEME!://localhost:!DELETE_PORT!/solr
+
+goto done
+
+
:invalid_cmd_line
@echo.
IF "!SCRIPT_ERROR!"=="" (
Modified: lucene/dev/trunk/solr/core/src/java/org/apache/solr/util/SolrCLI.java
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/solr/core/src/java/org/apache/solr/util/SolrCLI.java?rev=1652345&r1=1652344&r2=1652345&view=diff
==============================================================================
--- lucene/dev/trunk/solr/core/src/java/org/apache/solr/util/SolrCLI.java (original)
+++ lucene/dev/trunk/solr/core/src/java/org/apache/solr/util/SolrCLI.java Fri Jan 16 06:36:15 2015
@@ -244,6 +244,8 @@ public class SolrCLI {
return new CreateCoreTool();
else if ("create".equals(toolType))
return new CreateTool();
+ else if ("delete".equals(toolType))
+ return new DeleteTool();
// If you add a built-in tool to this class, add it here to avoid
// classpath scanning
@@ -265,6 +267,7 @@ public class SolrCLI {
formatter.printHelp("create_collection", getToolOptions(new CreateCollectionTool()));
formatter.printHelp("create_core", getToolOptions(new CreateCoreTool()));
formatter.printHelp("create", getToolOptions(new CreateTool()));
+ formatter.printHelp("delete", getToolOptions(new DeleteTool()));
List<Class<Tool>> toolClasses = findToolClassesInPackage("org.apache.solr.util");
for (Class<Tool> next : toolClasses) {
@@ -1080,6 +1083,43 @@ public class SolrCLI {
.create("configsetsDir")
};
+ public static String getZkHost(CommandLine cli) throws Exception {
+ String zkHost = cli.getOptionValue("zkHost");
+ if (zkHost != null)
+ return zkHost;
+
+ // 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";
+ CloseableHttpClient 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) {
+ String zookeeper = (String) cloud.get("ZooKeeper");
+ if (zookeeper.endsWith("(embedded)")) {
+ zookeeper = zookeeper.substring(0, zookeeper.length() - "(embedded)".length());
+ }
+ zkHost = zookeeper;
+ }
+ } finally {
+ HttpClientUtil.close(httpClient);
+ }
+
+ return zkHost;
+ }
/**
* Supports create_collection command in the bin/solr script.
@@ -1103,43 +1143,12 @@ public class SolrCLI {
LogManager.getLogger("org.apache.zookeeper").setLevel(Level.ERROR);
LogManager.getLogger("org.apache.solr.common.cloud").setLevel(Level.WARN);
- String zkHost = cli.getOptionValue("zkHost");
+ String zkHost = getZkHost(cli);
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";
- CloseableHttpClient 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 {
- HttpClientUtil.close(httpClient);
- }
+ System.err.println("\nERROR: Solr at "+cli.getOptionValue("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;
}
int toolExitStatus = 0;
@@ -1502,4 +1511,220 @@ public class SolrCLI {
} // end CreateTool class
+ public static class DeleteTool implements Tool {
+
+ @Override
+ public String getName() {
+ return "delete";
+ }
+
+ @SuppressWarnings("static-access")
+ @Override
+ public Option[] getOptions() {
+ return new Option[]{
+ OptionBuilder
+ .withArgName("URL")
+ .hasArg()
+ .isRequired(false)
+ .withDescription("Base Solr URL, default is http://localhost:8983/solr")
+ .create("solrUrl"),
+ OptionBuilder
+ .withArgName("NAME")
+ .hasArg()
+ .isRequired(true)
+ .withDescription("Name of the core / collection to delete.")
+ .create("name"),
+ OptionBuilder
+ .withArgName("true|false")
+ .hasArg()
+ .isRequired(false)
+ .withDescription("Flag to indicate if the underlying configuration directory for a collection should also be deleted; default is true")
+ .create("deleteConfig"),
+ OptionBuilder
+ .isRequired(false)
+ .withDescription("Skip safety checks when deleting the configuration directory used by a collection")
+ .create("forceDeleteConfig"),
+ OptionBuilder
+ .withArgName("HOST")
+ .hasArg()
+ .isRequired(false)
+ .withDescription("Address of the Zookeeper ensemble; defaults to: "+ZK_HOST)
+ .create("zkHost")
+ };
+ }
+
+ @Override
+ 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 solrUrl = cli.getOptionValue("solrUrl", "http://localhost:8983/solr");
+ if (!solrUrl.endsWith("/"))
+ solrUrl += "/";
+
+ String systemInfoUrl = solrUrl+"admin/info/system";
+ CloseableHttpClient httpClient = getHttpClient();
+
+ int result = 0;
+ try {
+ Map<String,Object> systemInfo = getJson(httpClient, systemInfoUrl, 2);
+ if ("solrcloud".equals(systemInfo.get("mode"))) {
+ result = deleteCollection(cli);
+ } else {
+ result = deleteCore(cli, httpClient, solrUrl);
+ }
+ } finally {
+ closeHttpClient(httpClient);
+ }
+
+ return result;
+ }
+
+ protected int deleteCollection(CommandLine cli) throws Exception {
+
+ String zkHost = getZkHost(cli);
+
+ int toolExitStatus = 0;
+ CloudSolrClient cloudSolrServer = null;
+ try {
+ cloudSolrServer = new CloudSolrClient(zkHost);
+ System.out.println("Connecting to ZooKeeper at " + zkHost);
+ cloudSolrServer.connect();
+ toolExitStatus = deleteCollection(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 deleteCollection(CloudSolrClient cloudSolrClient, CommandLine cli) throws Exception {
+ Set<String> liveNodes = cloudSolrClient.getZkStateReader().getClusterState().getLiveNodes();
+ if (liveNodes.isEmpty())
+ throw new IllegalStateException("No live nodes found! Cannot delete a collection until " +
+ "there is at least 1 live node in the cluster.");
+ String firstLiveNode = liveNodes.iterator().next();
+ ZkStateReader zkStateReader = cloudSolrClient.getZkStateReader();
+ String baseUrl = zkStateReader.getBaseUrlForNodeName(firstLiveNode);
+
+ String collectionName = cli.getOptionValue("name");
+
+ if (!zkStateReader.getClusterState().hasCollection(collectionName)) {
+ System.err.println("\nERROR: Collection "+collectionName+" not found!");
+ System.err.println();
+ return 1;
+ }
+
+ String configName = zkStateReader.readConfigName(collectionName);
+ boolean deleteConfig = "true".equals(cli.getOptionValue("deleteConfig", "true"));
+ if (deleteConfig && configName != null) {
+ if (cli.hasOption("forceDeleteConfig")) {
+ log.warn("Skipping safety checks, configuration directory "+configName+" will be deleted with impunity.");
+ } else {
+ // need to scan all Collections to see if any are using the config
+ Set<String> collections = zkStateReader.getClusterState().getCollections();
+
+ // give a little note to the user if there are many collections in case it takes a while
+ if (collections.size() > 50)
+ log.info("Scanning " + collections.size() +
+ " to ensure no other collections are using config " + configName);
+
+ for (String next : collections) {
+ if (collectionName.equals(next))
+ continue; // don't check the collection we're deleting
+
+ if (configName.equals(zkStateReader.readConfigName(next))) {
+ deleteConfig = false;
+ log.warn("Configuration directory "+configName+" is also being used by "+next+
+ "; configuration will not be deleted from ZooKeeper. You can pass the -forceDeleteConfig flag to force delete.");
+ break;
+ }
+ }
+ }
+ }
+
+ String deleteCollectionUrl =
+ String.format(Locale.ROOT,
+ "%s/admin/collections?action=DELETE&name=%s",
+ baseUrl,
+ collectionName);
+
+ System.out.println("\nDeleting collection '"+collectionName+"' using command:\n"+deleteCollectionUrl+"\n");
+
+ Map<String,Object> json = null;
+ try {
+ json = getJson(deleteCollectionUrl);
+ } catch (SolrServerException sse) {
+ System.err.println("Failed to delete collection '"+collectionName+"' due to: "+sse.getMessage());
+ System.err.println();
+ return 1;
+ }
+
+ if (deleteConfig) {
+ String configZnode = "/configs/" + configName;
+ try {
+ zkStateReader.getZkClient().clean(configZnode);
+ } catch (Exception exc) {
+ System.err.println("\nERROR: Failed to delete configuration directory "+configZnode+" in ZooKeeper due to: "+
+ exc.getMessage()+"\nYou'll need to manually delete this znode using the zkcli script.");
+ }
+ }
+
+ if (json != null) {
+ CharArr arr = new CharArr();
+ new JSONWriter(arr, 2).write(json);
+ System.out.println(arr.toString());
+ System.out.println();
+ }
+
+ return 0;
+ }
+
+ protected int deleteCore(CommandLine cli, CloseableHttpClient httpClient, String solrUrl) throws Exception {
+
+ int status = 0;
+ String coreName = cli.getOptionValue("name");
+ String deleteCoreUrl =
+ String.format(Locale.ROOT,
+ "%sadmin/cores?action=UNLOAD&core=%s&deleteIndex=true&deleteDataDir=true&deleteInstanceDir=true",
+ solrUrl,
+ coreName);
+
+ System.out.println("\nDeleting core '"+coreName+"' using command:\n"+deleteCoreUrl+"\n");
+
+ Map<String,Object> json = null;
+ try {
+ json = getJson(deleteCoreUrl);
+ } catch (SolrServerException sse) {
+ System.err.println("Failed to delete core '"+coreName+"' due to: "+sse.getMessage());
+ System.err.println();
+ status = 1;
+ }
+
+ if (json != null) {
+ CharArr arr = new CharArr();
+ new JSONWriter(arr, 2).write(json);
+ System.out.println(arr.toString());
+ System.out.println();
+ }
+
+ return status;
+ }
+
+ } // end DeleteTool class
}