blob: a4eb79ce63a418f67de8541e9b3759a30ed5e650 [file] [log] [blame]
/*
* 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.handler.admin;
import java.io.IOException;
import java.lang.invoke.MethodHandles;
import java.nio.file.Path;
import java.util.Locale;
import java.util.Map;
import java.util.Optional;
import org.apache.commons.lang3.StringUtils;
import org.apache.solr.cloud.CloudDescriptor;
import org.apache.solr.cloud.ZkController;
import org.apache.solr.common.SolrException;
import org.apache.solr.common.SolrException.ErrorCode;
import org.apache.solr.common.params.CoreAdminParams;
import org.apache.solr.common.params.SolrParams;
import org.apache.solr.common.util.NamedList;
import org.apache.solr.common.util.SimpleOrderedMap;
import org.apache.solr.common.util.Utils;
import org.apache.solr.core.CoreContainer;
import org.apache.solr.core.CoreDescriptor;
import org.apache.solr.core.SolrCore;
import org.apache.solr.core.SolrInfoBean;
import org.apache.solr.core.snapshots.SolrSnapshotManager;
import org.apache.solr.core.snapshots.SolrSnapshotMetaDataManager;
import org.apache.solr.core.snapshots.SolrSnapshotMetaDataManager.SnapshotMetaData;
import org.apache.solr.handler.admin.CoreAdminHandler.CoreAdminOp;
import org.apache.solr.metrics.SolrMetricManager;
import org.apache.solr.search.SolrIndexSearcher;
import org.apache.solr.update.UpdateLog;
import org.apache.solr.util.NumberUtils;
import org.apache.solr.common.util.PropertiesUtil;
import org.apache.solr.util.RefCounted;
import org.apache.solr.util.TestInjection;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import static org.apache.solr.common.params.CommonParams.NAME;
import static org.apache.solr.common.params.CoreAdminParams.COLLECTION;
import static org.apache.solr.common.params.CoreAdminParams.CoreAdminAction.*;
import static org.apache.solr.common.params.CoreAdminParams.REPLICA;
import static org.apache.solr.common.params.CoreAdminParams.REPLICA_TYPE;
import static org.apache.solr.common.params.CoreAdminParams.SHARD;
import static org.apache.solr.handler.admin.CoreAdminHandler.COMPLETED;
import static org.apache.solr.handler.admin.CoreAdminHandler.CallInfo;
import static org.apache.solr.handler.admin.CoreAdminHandler.FAILED;
import static org.apache.solr.handler.admin.CoreAdminHandler.RESPONSE;
import static org.apache.solr.handler.admin.CoreAdminHandler.RESPONSE_MESSAGE;
import static org.apache.solr.handler.admin.CoreAdminHandler.RESPONSE_STATUS;
import static org.apache.solr.handler.admin.CoreAdminHandler.RUNNING;
import static org.apache.solr.handler.admin.CoreAdminHandler.buildCoreParams;
import static org.apache.solr.handler.admin.CoreAdminHandler.normalizePath;
enum CoreAdminOperation implements CoreAdminOp {
CREATE_OP(CREATE, it -> {
assert TestInjection.injectRandomDelayInCoreCreation();
SolrParams params = it.req.getParams();
log().info("core create command {}", params);
String coreName = params.required().get(CoreAdminParams.NAME);
Map<String, String> coreParams = buildCoreParams(params);
CoreContainer coreContainer = it.handler.coreContainer;
Path instancePath;
// TODO: Should we nuke setting odd instance paths? They break core discovery, generally
String instanceDir = it.req.getParams().get(CoreAdminParams.INSTANCE_DIR);
if (instanceDir == null)
instanceDir = it.req.getParams().get("property.instanceDir");
if (instanceDir != null) {
instanceDir = PropertiesUtil.substituteProperty(instanceDir, coreContainer.getContainerProperties());
instancePath = coreContainer.getCoreRootDirectory().resolve(instanceDir).normalize();
} else {
instancePath = coreContainer.getCoreRootDirectory().resolve(coreName);
}
boolean newCollection = params.getBool(CoreAdminParams.NEW_COLLECTION, false);
coreContainer.create(coreName, instancePath, coreParams, newCollection);
it.rsp.add("core", coreName);
}),
UNLOAD_OP(UNLOAD, it -> {
SolrParams params = it.req.getParams();
String cname = params.required().get(CoreAdminParams.CORE);
boolean deleteIndexDir = params.getBool(CoreAdminParams.DELETE_INDEX, false);
boolean deleteDataDir = params.getBool(CoreAdminParams.DELETE_DATA_DIR, false);
boolean deleteInstanceDir = params.getBool(CoreAdminParams.DELETE_INSTANCE_DIR, false);
boolean deleteMetricsHistory = params.getBool(CoreAdminParams.DELETE_METRICS_HISTORY, false);
CoreDescriptor cdescr = it.handler.coreContainer.getCoreDescriptor(cname);
it.handler.coreContainer.unload(cname, deleteIndexDir, deleteDataDir, deleteInstanceDir);
if (deleteMetricsHistory) {
MetricsHistoryHandler historyHandler = it.handler.coreContainer.getMetricsHistoryHandler();
if (historyHandler != null) {
CloudDescriptor cd = cdescr != null ? cdescr.getCloudDescriptor() : null;
String registry;
if (cd == null) {
registry = SolrMetricManager.getRegistryName(SolrInfoBean.Group.core, cname);
} else {
String replicaName = Utils.parseMetricsReplicaName(cd.getCollectionName(), cname);
registry = SolrMetricManager.getRegistryName(SolrInfoBean.Group.core,
cd.getCollectionName(),
cd.getShardId(),
replicaName);
}
historyHandler.checkSystemCollection();
historyHandler.removeHistory(registry);
}
}
assert TestInjection.injectNonExistentCoreExceptionAfterUnload(cname);
}),
RELOAD_OP(RELOAD, it -> {
SolrParams params = it.req.getParams();
String cname = params.required().get(CoreAdminParams.CORE);
it.handler.coreContainer.reload(cname);
}),
STATUS_OP(STATUS, new StatusOp()),
SWAP_OP(SWAP, it -> {
final SolrParams params = it.req.getParams();
final String cname = params.required().get(CoreAdminParams.CORE);
String other = params.required().get(CoreAdminParams.OTHER);
it.handler.coreContainer.swap(cname, other);
}),
RENAME_OP(RENAME, it -> {
SolrParams params = it.req.getParams();
String name = params.required().get(CoreAdminParams.OTHER);
String cname = params.required().get(CoreAdminParams.CORE);
if (cname.equals(name)) return;
it.handler.coreContainer.rename(cname, name);
}),
MERGEINDEXES_OP(MERGEINDEXES, new MergeIndexesOp()),
SPLIT_OP(SPLIT, new SplitOp()),
PREPRECOVERY_OP(PREPRECOVERY, new PrepRecoveryOp()),
REQUESTRECOVERY_OP(REQUESTRECOVERY, it -> {
final SolrParams params = it.req.getParams();
final String cname = params.required().get(CoreAdminParams.CORE);
log().info("It has been requested that we recover: core=" + cname);
try (SolrCore core = it.handler.coreContainer.getCore(cname)) {
if (core != null) {
// This can take a while, but doRecovery is already async so don't worry about it here
core.getUpdateHandler().getSolrCoreState().doRecovery(it.handler.coreContainer, core.getCoreDescriptor());
} else {
throw new SolrException(ErrorCode.BAD_REQUEST, "Unable to locate core " + cname);
}
}
}),
REQUESTSYNCSHARD_OP(REQUESTSYNCSHARD, new RequestSyncShardOp()),
REQUESTBUFFERUPDATES_OP(REQUESTBUFFERUPDATES, it -> {
SolrParams params = it.req.getParams();
String cname = params.required().get(CoreAdminParams.NAME);
log().info("Starting to buffer updates on core:" + cname);
try (SolrCore core = it.handler.coreContainer.getCore(cname)) {
if (core == null)
throw new SolrException(ErrorCode.BAD_REQUEST, "Core [" + cname + "] does not exist");
UpdateLog updateLog = core.getUpdateHandler().getUpdateLog();
if (updateLog.getState() != UpdateLog.State.ACTIVE) {
throw new SolrException(ErrorCode.SERVER_ERROR, "Core " + cname + " not in active state");
}
updateLog.bufferUpdates();
it.rsp.add("core", cname);
it.rsp.add("status", "BUFFERING");
} catch (Throwable e) {
if (e instanceof SolrException)
throw (SolrException) e;
else
throw new SolrException(ErrorCode.SERVER_ERROR, "Could not start buffering updates", e);
} finally {
if (it.req != null) it.req.close();
}
}),
REQUESTAPPLYUPDATES_OP(REQUESTAPPLYUPDATES, new RequestApplyUpdatesOp()),
REQUESTSTATUS_OP(REQUESTSTATUS, it -> {
SolrParams params = it.req.getParams();
String requestId = params.required().get(CoreAdminParams.REQUESTID);
log().info("Checking request status for : " + requestId);
if (it.handler.getRequestStatusMap(RUNNING).containsKey(requestId)) {
it.rsp.add(RESPONSE_STATUS, RUNNING);
} else if (it.handler.getRequestStatusMap(COMPLETED).containsKey(requestId)) {
it.rsp.add(RESPONSE_STATUS, COMPLETED);
it.rsp.add(RESPONSE, it.handler.getRequestStatusMap(COMPLETED).get(requestId).getRspObject());
} else if (it.handler.getRequestStatusMap(FAILED).containsKey(requestId)) {
it.rsp.add(RESPONSE_STATUS, FAILED);
it.rsp.add(RESPONSE, it.handler.getRequestStatusMap(FAILED).get(requestId).getRspObject());
} else {
it.rsp.add(RESPONSE_STATUS, "notfound");
it.rsp.add(RESPONSE_MESSAGE, "No task found in running, completed or failed tasks");
}
}),
OVERSEEROP_OP(OVERSEEROP, it -> {
ZkController zkController = it.handler.coreContainer.getZkController();
if (zkController != null) {
String op = it.req.getParams().get("op");
String electionNode = it.req.getParams().get("electionNode");
if (electionNode != null) {
zkController.rejoinOverseerElection(electionNode, "rejoinAtHead".equals(op));
} else {
log().info("electionNode is required param");
}
}
}),
REJOINLEADERELECTION_OP(REJOINLEADERELECTION, it -> {
ZkController zkController = it.handler.coreContainer.getZkController();
if (zkController != null) {
zkController.rejoinShardLeaderElection(it.req.getParams());
} else {
log().warn("zkController is null in CoreAdminHandler.handleRequestInternal:REJOINLEADERELECTION. No action taken.");
}
}),
INVOKE_OP(INVOKE, new InvokeOp()),
BACKUPCORE_OP(BACKUPCORE, new BackupCoreOp()),
RESTORECORE_OP(RESTORECORE, new RestoreCoreOp()),
CREATESNAPSHOT_OP(CREATESNAPSHOT, new CreateSnapshotOp()),
DELETESNAPSHOT_OP(DELETESNAPSHOT, new DeleteSnapshotOp()),
@SuppressWarnings({"unchecked"})
LISTSNAPSHOTS_OP(LISTSNAPSHOTS, it -> {
final SolrParams params = it.req.getParams();
String cname = params.required().get(CoreAdminParams.CORE);
CoreContainer cc = it.handler.getCoreContainer();
try ( SolrCore core = cc.getCore(cname) ) {
if (core == null) {
throw new SolrException(ErrorCode.BAD_REQUEST, "Unable to locate core " + cname);
}
SolrSnapshotMetaDataManager mgr = core.getSnapshotMetaDataManager();
@SuppressWarnings({"rawtypes"})
NamedList result = new NamedList();
for (String name : mgr.listSnapshots()) {
Optional<SnapshotMetaData> metadata = mgr.getSnapshotMetaData(name);
if ( metadata.isPresent() ) {
NamedList<String> props = new NamedList<>();
props.add(SolrSnapshotManager.GENERATION_NUM, String.valueOf(metadata.get().getGenerationNumber()));
props.add(SolrSnapshotManager.INDEX_DIR_PATH, metadata.get().getIndexDirPath());
result.add(name, props);
}
}
it.rsp.add(SolrSnapshotManager.SNAPSHOTS_INFO, result);
}
});
final CoreAdminParams.CoreAdminAction action;
final CoreAdminOp fun;
CoreAdminOperation(CoreAdminParams.CoreAdminAction action, CoreAdminOp fun) {
this.action = action;
this.fun = fun;
}
private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
static Logger log() {
return log;
}
/**
* Returns the core status for a particular core.
* @param cores - the enclosing core container
* @param cname - the core to return
* @param isIndexInfoNeeded - add what may be expensive index information. NOT returned if the core is not loaded
* @return - a named list of key/value pairs from the core.
* @throws IOException - LukeRequestHandler can throw an I/O exception
*/
@SuppressWarnings({"unchecked", "rawtypes"})
static NamedList<Object> getCoreStatus(CoreContainer cores, String cname, boolean isIndexInfoNeeded) throws IOException {
NamedList<Object> info = new SimpleOrderedMap<>();
if (cores.isCoreLoading(cname)) {
info.add(NAME, cname);
info.add("isLoaded", "false");
info.add("isLoading", "true");
} else {
if (!cores.isLoaded(cname)) { // Lazily-loaded core, fill in what we can.
// It would be a real mistake to load the cores just to get the status
CoreDescriptor desc = cores.getUnloadedCoreDescriptor(cname);
if (desc != null) {
info.add(NAME, desc.getName());
info.add("instanceDir", desc.getInstanceDir());
// None of the following are guaranteed to be present in a not-yet-loaded core.
String tmp = desc.getDataDir();
if (StringUtils.isNotBlank(tmp)) info.add("dataDir", tmp);
tmp = desc.getConfigName();
if (StringUtils.isNotBlank(tmp)) info.add("config", tmp);
tmp = desc.getSchemaName();
if (StringUtils.isNotBlank(tmp)) info.add("schema", tmp);
info.add("isLoaded", "false");
}
} else {
try (SolrCore core = cores.getCore(cname)) {
if (core != null) {
info.add(NAME, core.getName());
info.add("instanceDir", core.getInstancePath().toString());
info.add("dataDir", normalizePath(core.getDataDir()));
info.add("config", core.getConfigResource());
info.add("schema", core.getSchemaResource());
info.add("startTime", core.getStartTimeStamp());
info.add("uptime", core.getUptimeMs());
if (cores.isZooKeeperAware()) {
info.add("lastPublished", core.getCoreDescriptor().getCloudDescriptor().getLastPublished().toString().toLowerCase(Locale.ROOT));
info.add("configVersion", core.getSolrConfig().getZnodeVersion());
SimpleOrderedMap cloudInfo = new SimpleOrderedMap<>();
cloudInfo.add(COLLECTION, core.getCoreDescriptor().getCloudDescriptor().getCollectionName());
cloudInfo.add(SHARD, core.getCoreDescriptor().getCloudDescriptor().getShardId());
cloudInfo.add(REPLICA, core.getCoreDescriptor().getCloudDescriptor().getCoreNodeName());
cloudInfo.add(REPLICA_TYPE, core.getCoreDescriptor().getCloudDescriptor().getReplicaType().name());
info.add("cloud", cloudInfo);
}
if (isIndexInfoNeeded) {
RefCounted<SolrIndexSearcher> searcher = core.getSearcher();
try {
SimpleOrderedMap<Object> indexInfo = LukeRequestHandler.getIndexInfo(searcher.get().getIndexReader());
long size = core.getIndexSize();
indexInfo.add("sizeInBytes", size);
indexInfo.add("size", NumberUtils.readableSize(size));
info.add("index", indexInfo);
} finally {
searcher.decref();
}
}
}
}
}
}
return info;
}
@Override
public void execute(CallInfo it) throws Exception {
try {
fun.execute(it);
} catch (SolrException | InterruptedException e) {
// No need to re-wrap; throw as-is.
throw e;
} catch (Exception e) {
throw new SolrException(ErrorCode.SERVER_ERROR, "Error handling '" + action.name() + "' action", e);
}
}
}