Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
/*
* 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.client.api.endpoint;

import io.swagger.v3.oas.annotations.Operation;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;
import org.apache.solr.client.api.model.ListClusterNodesResponse;

/** V2 API definition for listing the live nodes in the SolrCloud cluster. */
@Path("/cluster/nodes")
public interface ListClusterNodesApi {

@GET
@Operation(
summary = "List the live nodes in this Solr cluster.",
tags = {"cluster"})
ListClusterNodesResponse listClusterNodes();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
/*
* 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.client.api.model;

import com.fasterxml.jackson.annotation.JsonProperty;
import io.swagger.v3.oas.annotations.media.Schema;
import java.util.Set;

/** Response for the v2 "list cluster nodes" API */
public class ListClusterNodesResponse extends SolrJerseyResponse {

@Schema(description = "The live nodes in the cluster.")
@JsonProperty("nodes")
public Set<String> nodes;
}
13 changes: 6 additions & 7 deletions solr/core/src/java/org/apache/solr/cli/CLIUtils.java
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@
import java.nio.file.Path;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Optional;
Expand All @@ -41,15 +40,14 @@
import org.apache.solr.client.solrj.impl.CloudSolrClient;
import org.apache.solr.client.solrj.impl.SolrZkClientTimeout;
import org.apache.solr.client.solrj.jetty.HttpJettySolrClient;
import org.apache.solr.client.solrj.request.CollectionAdminRequest;
import org.apache.solr.client.solrj.request.CollectionsApi;
import org.apache.solr.client.solrj.request.CoresApi;
import org.apache.solr.client.solrj.request.SystemInfoRequest;
import org.apache.solr.client.solrj.response.SystemInfoResponse;
import org.apache.solr.common.SolrException;
import org.apache.solr.common.cloud.SolrZkClient;
import org.apache.solr.common.cloud.ZkStateReader;
import org.apache.solr.common.util.EnvUtils;
import org.apache.solr.common.util.NamedList;

/** Utility class that holds various helper methods for the CLI. */
public final class CLIUtils {
Expand Down Expand Up @@ -312,10 +310,11 @@ public static boolean safeCheckCollectionExists(
String solrUrl, String collection, String credentials) {
boolean exists = false;
try (var solrClient = getSolrClient(solrUrl, credentials)) {
NamedList<Object> existsCheckResult = solrClient.request(new CollectionAdminRequest.List());
@SuppressWarnings("unchecked")
List<String> collections = (List<String>) existsCheckResult.get("collections");
exists = collections != null && collections.contains(collection);
var response = new CollectionsApi.ListCollections().process(solrClient);
exists =
response != null
&& response.collections != null
&& response.collections.contains(collection);
} catch (Exception exc) {
// just ignore it since we're only interested in a positive result here
}
Expand Down
51 changes: 23 additions & 28 deletions solr/core/src/java/org/apache/solr/cli/CreateTool.java
Original file line number Diff line number Diff line change
Expand Up @@ -32,18 +32,14 @@
import org.apache.solr.client.solrj.SolrServerException;
import org.apache.solr.client.solrj.impl.CloudSolrClient;
import org.apache.solr.client.solrj.jetty.HttpJettySolrClient;
import org.apache.solr.client.solrj.request.CollectionAdminRequest;
import org.apache.solr.client.solrj.request.CoreAdminRequest;
import org.apache.solr.client.solrj.request.CollectionsApi;
import org.apache.solr.client.solrj.request.CoresApi;
import org.apache.solr.client.solrj.request.SystemInfoRequest;
import org.apache.solr.client.solrj.response.CoreAdminResponse;
import org.apache.solr.client.solrj.request.json.JacksonContentWriter;
import org.apache.solr.client.solrj.response.SystemInfoResponse;
import org.apache.solr.client.solrj.response.json.JsonMapResponseParser;
import org.apache.solr.cloud.ZkConfigSetService;
import org.apache.solr.common.cloud.ZkStateReader;
import org.apache.solr.common.util.NamedList;
import org.apache.solr.core.ConfigSetService;
import org.noggit.CharArr;
import org.noggit.JSONWriter;

/** Supports create command in the bin/solr script. */
public class CreateTool extends ToolBase {
Expand Down Expand Up @@ -184,14 +180,13 @@ protected void createCore(CommandLine cli, SolrClient solrClient) throws Excepti
+ coreInstanceDir.toAbsolutePath());
}

echoIfVerbose("\nCreating new core '" + coreName + "' using CoreAdminRequest");
echoIfVerbose("\nCreating new core '" + coreName + "' using V2 Cores API");

try {
CoreAdminResponse res = CoreAdminRequest.createCore(coreName, coreName, solrClient);
if (isVerbose()) {
echo(res.jsonStr());
echo("\n");
}
var req = new CoresApi.CreateCore();
req.setName(coreName);
req.setInstanceDir(coreName);
req.process(solrClient);
echo(String.format(Locale.ROOT, "\nCreated new core '%s'", coreName));

} catch (Exception e) {
Expand Down Expand Up @@ -280,31 +275,31 @@ protected void createCollection(CloudSolrClient cloudSolrClient, CommandLine cli
throw new IllegalStateException(
"\nCollection '"
+ collectionName
+ "' already exists!\nChecked collection existence using CollectionAdminRequest");
+ "' already exists!\nChecked collection existence using V2 Collections API");
}

// doesn't seem to exist ... try to create
echoIfVerbose(
"\nCreating new collection '" + collectionName + "' using CollectionAdminRequest");
echoIfVerbose("\nCreating new collection '" + collectionName + "' using V2 Collections API");

NamedList<Object> response;
try {
var req =
CollectionAdminRequest.createCollection(
collectionName, confName, numShards, replicationFactor);
req.setResponseParser(new JsonMapResponseParser());
response = cloudSolrClient.request(req);
var req = new CollectionsApi.CreateCollection();
req.setName(collectionName);
req.setConfig(confName);
req.setNumShards(numShards);
req.setReplicationFactor(replicationFactor);
var response = req.process(cloudSolrClient);
if (isVerbose() && response != null) {
echo(
JacksonContentWriter.DEFAULT_MAPPER
.writerWithDefaultPrettyPrinter()
.writeValueAsString(response));
echo("\n");
}
} catch (SolrServerException sse) {
throw new Exception(
"Failed to create collection '" + collectionName + "' due to: " + sse.getMessage());
}

if (isVerbose()) {
// pretty-print the response to stdout
CharArr arr = new CharArr();
new JSONWriter(arr, 2).write(response.asMap(10));
echo(arr.toString());
}
String endMessage =
String.format(
Locale.ROOT,
Expand Down
59 changes: 26 additions & 33 deletions solr/core/src/java/org/apache/solr/cli/DeleteTool.java
Original file line number Diff line number Diff line change
Expand Up @@ -29,13 +29,10 @@
import org.apache.solr.client.solrj.SolrServerException;
import org.apache.solr.client.solrj.impl.CloudSolrClient;
import org.apache.solr.client.solrj.jetty.HttpJettySolrClient;
import org.apache.solr.client.solrj.request.CollectionAdminRequest;
import org.apache.solr.client.solrj.request.CoreAdminRequest;
import org.apache.solr.client.solrj.response.json.JsonMapResponseParser;
import org.apache.solr.client.solrj.request.CollectionsApi;
import org.apache.solr.client.solrj.request.CoresApi;
import org.apache.solr.client.solrj.request.json.JacksonContentWriter;
import org.apache.solr.common.cloud.ZkStateReader;
import org.apache.solr.common.util.NamedList;
import org.noggit.CharArr;
import org.noggit.JSONWriter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

Expand Down Expand Up @@ -176,13 +173,18 @@ protected void deleteCollection(CloudSolrClient cloudSolrClient, CommandLine cli
}
}

echoIfVerbose("\nDeleting collection '" + collectionName + "' using CollectionAdminRequest");
echoIfVerbose("\nDeleting collection '" + collectionName + "' using V2 Collections API");

NamedList<Object> response;
try {
var req = CollectionAdminRequest.deleteCollection(collectionName);
req.setResponseParser(new JsonMapResponseParser());
response = cloudSolrClient.request(req);
var req = new CollectionsApi.DeleteCollection(collectionName);
var response = req.process(cloudSolrClient);
if (isVerbose() && response != null) {
echo(
JacksonContentWriter.DEFAULT_MAPPER
.writerWithDefaultPrettyPrinter()
.writeValueAsString(response));
echo("\n");
}
} catch (SolrServerException sse) {
throw new Exception(
"Failed to delete collection '" + collectionName + "' due to: " + sse.getMessage());
Expand All @@ -202,38 +204,29 @@ protected void deleteCollection(CloudSolrClient cloudSolrClient, CommandLine cli
}
}

if (isVerbose() && response != null) {
// pretty-print the response to stdout
CharArr arr = new CharArr();
new JSONWriter(arr, 2).write(response.asMap(10));
echo(arr.toString());
echo("\n");
}

echo(String.format(Locale.ROOT, "\nDeleted collection '%s'", collectionName));
}

protected void deleteCore(CommandLine cli, SolrClient solrClient) throws Exception {
String coreName = cli.getOptionValue(COLLECTION_NAME_OPTION);

echo("\nDeleting core '" + coreName + "' using CoreAdminRequest\n");
echo("\nDeleting core '" + coreName + "' using V2 Cores API\n");

NamedList<Object> response;
try {
CoreAdminRequest.Unload unloadRequest = new CoreAdminRequest.Unload(true);
unloadRequest.setDeleteIndex(true);
unloadRequest.setDeleteDataDir(true);
unloadRequest.setDeleteInstanceDir(true);
unloadRequest.setCoreName(coreName);
unloadRequest.setResponseParser(new JsonMapResponseParser());
response = solrClient.request(unloadRequest);
var req = new CoresApi.UnloadCore(coreName);
req.setDeleteIndex(true);
req.setDeleteDataDir(true);
req.setDeleteInstanceDir(true);
var response = req.process(solrClient);
if (isVerbose() && response != null) {
echo(
JacksonContentWriter.DEFAULT_MAPPER
.writerWithDefaultPrettyPrinter()
.writeValueAsString(response));
echo("\n");
}
} catch (SolrServerException sse) {
throw new Exception("Failed to delete core '" + coreName + "' due to: " + sse.getMessage());
}

if (response != null) {
echoIfVerbose((String) response.get("response"));
echoIfVerbose("\n");
}
}
}
26 changes: 13 additions & 13 deletions solr/core/src/java/org/apache/solr/cli/StatusTool.java
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@

import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Optional;
Expand All @@ -31,10 +30,10 @@
import org.apache.commons.cli.Options;
import org.apache.solr.cli.SolrProcessManager.SolrProcess;
import org.apache.solr.client.solrj.SolrClient;
import org.apache.solr.client.solrj.request.CollectionAdminRequest;
import org.apache.solr.client.solrj.request.ClusterApi;
import org.apache.solr.client.solrj.request.CollectionsApi;
import org.apache.solr.client.solrj.request.SystemInfoRequest;
import org.apache.solr.client.solrj.response.SystemInfoResponse;
import org.apache.solr.common.util.NamedList;
import org.apache.solr.common.util.URLUtil;
import org.noggit.CharArr;
import org.noggit.JSONWriter;
Expand Down Expand Up @@ -319,23 +318,24 @@ public static Map<String, Object> reportStatus(SolrClient solrClient) throws Exc
}

/**
* Calls the CLUSTERSTATUS endpoint in Solr to get basic status information about the SolrCloud
* cluster.
* Calls V2 API endpoints to get basic status information about the SolrCloud cluster.
*
* <p>Uses GET /cluster/nodes for live node count and GET /collections for collection count.
*/
@SuppressWarnings("unchecked")
private static Map<String, String> getCloudStatus(SolrClient solrClient, String zkHost)
throws Exception {
Map<String, String> cloudStatus = new LinkedHashMap<>();
cloudStatus.put("ZooKeeper", (zkHost != null) ? zkHost : "?");

// TODO add booleans to request just what we want; not everything
NamedList<Object> json = solrClient.request(new CollectionAdminRequest.ClusterStatus());

List<String> liveNodes = (List<String>) json._get(List.of("cluster", "live_nodes"), null);
cloudStatus.put("liveNodes", String.valueOf(liveNodes.size()));
var nodesResponse = new ClusterApi.ListClusterNodes().process(solrClient);
var liveNodes = nodesResponse != null ? nodesResponse.nodes : null;
cloudStatus.put("liveNodes", String.valueOf(liveNodes != null ? liveNodes.size() : 0));

// TODO get this as a metric from the metrics API instead, or something else.
var collections = (Map<String, Object>) json._get(List.of("cluster", "collections"), null);
var collectionsResponse = new CollectionsApi.ListCollections().process(solrClient);
var collections =
collectionsResponse != null && collectionsResponse.collections != null
? collectionsResponse.collections
: java.util.Collections.emptyList();
cloudStatus.put("collections", String.valueOf(collections.size()));

return cloudStatus;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,7 @@
import org.apache.solr.handler.admin.api.ForceLeader;
import org.apache.solr.handler.admin.api.InstallShardData;
import org.apache.solr.handler.admin.api.ListAliases;
import org.apache.solr.handler.admin.api.ListClusterNodes;
import org.apache.solr.handler.admin.api.ListCollectionBackups;
import org.apache.solr.handler.admin.api.ListCollectionSnapshots;
import org.apache.solr.handler.admin.api.ListCollections;
Expand Down Expand Up @@ -1370,6 +1371,7 @@ public Collection<Class<? extends JerseyResource>> getJerseyResources() {
DeleteNode.class,
ListAliases.class,
AliasProperty.class,
ListClusterNodes.class,
ListCollectionSnapshots.class,
CreateCollectionSnapshot.class,
DeleteCollectionSnapshot.class,
Expand Down
Loading
Loading