| /* |
| * 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; |
| |
| import java.util.HashMap; |
| import java.util.Map; |
| |
| import org.apache.solr.api.Command; |
| import org.apache.solr.api.EndPoint; |
| import org.apache.solr.api.PayloadObj; |
| import org.apache.solr.client.solrj.request.beans.ClusterPropInfo; |
| import org.apache.solr.client.solrj.request.beans.CreateConfigInfo; |
| import org.apache.solr.cloud.OverseerConfigSetMessageHandler; |
| import org.apache.solr.common.SolrException; |
| import org.apache.solr.common.annotation.JsonProperty; |
| import org.apache.solr.common.cloud.ClusterProperties; |
| import org.apache.solr.common.params.CommonParams; |
| import org.apache.solr.common.params.ConfigSetParams; |
| import org.apache.solr.common.params.DefaultSolrParams; |
| import org.apache.solr.common.params.ModifiableSolrParams; |
| import org.apache.solr.common.util.ReflectMapWriter; |
| import org.apache.solr.common.util.Utils; |
| import org.apache.solr.core.CoreContainer; |
| import org.apache.solr.handler.admin.CollectionsHandler; |
| import org.apache.solr.handler.admin.ConfigSetsHandler; |
| import org.apache.solr.request.SolrQueryRequest; |
| import org.apache.solr.response.SolrQueryResponse; |
| |
| import static org.apache.solr.client.solrj.SolrRequest.METHOD.DELETE; |
| 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.client.solrj.SolrRequest.METHOD.PUT; |
| import static org.apache.solr.cloud.api.collections.OverseerCollectionMessageHandler.REQUESTID; |
| import static org.apache.solr.common.params.CollectionParams.CollectionAction.ADDROLE; |
| import static org.apache.solr.common.params.CollectionParams.CollectionAction.CLUSTERPROP; |
| import static org.apache.solr.common.params.CollectionParams.CollectionAction.OVERSEERSTATUS; |
| import static org.apache.solr.common.params.CollectionParams.CollectionAction.REMOVEROLE; |
| import static org.apache.solr.security.PermissionNameProvider.Name.COLL_EDIT_PERM; |
| import static org.apache.solr.security.PermissionNameProvider.Name.COLL_READ_PERM; |
| import static org.apache.solr.security.PermissionNameProvider.Name.CONFIG_EDIT_PERM; |
| import static org.apache.solr.security.PermissionNameProvider.Name.CONFIG_READ_PERM; |
| |
| /** All V2 APIs that have a prefix of /api/cluster/ |
| * |
| */ |
| public class ClusterAPI { |
| private final CollectionsHandler collectionsHandler; |
| private final ConfigSetsHandler configSetsHandler; |
| |
| public final Commands commands = new Commands(); |
| public final ConfigSetCommands configSetCommands = new ConfigSetCommands(); |
| |
| public ClusterAPI(CollectionsHandler ch, ConfigSetsHandler configSetsHandler) { |
| this.collectionsHandler = ch; |
| this.configSetsHandler = configSetsHandler; |
| } |
| |
| @EndPoint(method = GET, |
| path = "/cluster/aliases", |
| permission = COLL_READ_PERM) |
| public void aliases(SolrQueryRequest req, SolrQueryResponse rsp) throws Exception { |
| CollectionsHandler.CollectionOperation.LISTALIASES_OP.execute(req, rsp, getCoreContainer().getCollectionsHandler()); |
| } |
| |
| @EndPoint(method = GET, |
| path = "/cluster/overseer", |
| permission = COLL_READ_PERM) |
| public void getOverseerStatus(SolrQueryRequest req, SolrQueryResponse rsp) throws Exception { |
| getCoreContainer().getCollectionsHandler().handleRequestBody(wrapParams(req, "action", OVERSEERSTATUS.toString()), rsp); |
| } |
| |
| @EndPoint(method = GET, |
| path = "/cluster", |
| permission = COLL_READ_PERM) |
| public void getCluster(SolrQueryRequest req, SolrQueryResponse rsp) throws Exception { |
| CollectionsHandler.CollectionOperation.LIST_OP.execute(req, rsp, getCoreContainer().getCollectionsHandler()); |
| } |
| |
| @EndPoint(method = DELETE, |
| path = "/cluster/command-status/{id}", |
| permission = COLL_EDIT_PERM) |
| public void deleteCommandStatus(SolrQueryRequest req, SolrQueryResponse rsp) throws Exception { |
| wrapParams(req, REQUESTID, req.getPathTemplateValues().get("id")); |
| CollectionsHandler.CollectionOperation.DELETESTATUS_OP.execute(req, rsp, collectionsHandler); |
| } |
| |
| @EndPoint(method = DELETE, |
| path = "/cluster/configs/{name}", |
| permission = CONFIG_EDIT_PERM |
| ) |
| public void deleteConfigSet(SolrQueryRequest req, SolrQueryResponse rsp) throws Exception { |
| req = wrapParams(req, |
| "action", ConfigSetParams.ConfigSetAction.DELETE.toString(), |
| CommonParams.NAME, req.getPathTemplateValues().get("name")); |
| configSetsHandler.handleRequestBody(req, rsp); |
| } |
| |
| @EndPoint(method = GET, |
| path = "/cluster/configs", |
| permission = CONFIG_READ_PERM) |
| public void listConfigSet(SolrQueryRequest req, SolrQueryResponse rsp) throws Exception { |
| ConfigSetsHandler.ConfigSetOperation.LIST_OP.call(req, rsp, configSetsHandler); |
| } |
| |
| @EndPoint(method = POST, |
| path = "/cluster/configs", |
| permission = CONFIG_EDIT_PERM |
| ) |
| public class ConfigSetCommands { |
| |
| @Command(name = "create") |
| @SuppressWarnings("unchecked") |
| public void create(PayloadObj<CreateConfigInfo> obj) throws Exception { |
| Map<String, Object> mapVals = obj.get().toMap(new HashMap<>()); |
| Map<String,Object> customProps = (Map<String, Object>) mapVals.remove("properties"); |
| if(customProps!= null) { |
| customProps.forEach((k, o) -> mapVals.put(OverseerConfigSetMessageHandler.PROPERTY_PREFIX+"."+ k, o)); |
| } |
| mapVals.put("action", ConfigSetParams.ConfigSetAction.CREATE.toString()); |
| configSetsHandler.handleRequestBody(wrapParams(obj.getRequest(), mapVals), obj.getResponse()); |
| } |
| |
| } |
| |
| @EndPoint(method = PUT, |
| path = "/cluster/configs/{name}", |
| permission = CONFIG_EDIT_PERM |
| ) |
| public void uploadConfigSet(SolrQueryRequest req, SolrQueryResponse rsp) throws Exception { |
| req = wrapParams(req, |
| "action", ConfigSetParams.ConfigSetAction.UPLOAD.toString(), |
| CommonParams.NAME, req.getPathTemplateValues().get("name"), |
| ConfigSetParams.OVERWRITE, true, |
| ConfigSetParams.CLEANUP, false); |
| configSetsHandler.handleRequestBody(req, rsp); |
| } |
| |
| @EndPoint(method = PUT, |
| path = "/cluster/configs/{name}/*", |
| permission = CONFIG_EDIT_PERM |
| ) |
| public void insertIntoConfigSet(SolrQueryRequest req, SolrQueryResponse rsp) throws Exception { |
| String path = req.getPathTemplateValues().get("*"); |
| if (path == null || path.isEmpty()) { |
| throw new SolrException(SolrException.ErrorCode.BAD_REQUEST, "In order to insert a file in a configSet, a filePath must be provided in the url after the name of the configSet."); |
| } |
| req = wrapParams(req, |
| "action", ConfigSetParams.ConfigSetAction.UPLOAD.toString(), |
| CommonParams.NAME, req.getPathTemplateValues().get("name"), |
| ConfigSetParams.FILE_PATH, path, |
| ConfigSetParams.OVERWRITE, true, |
| ConfigSetParams.CLEANUP, false); |
| configSetsHandler.handleRequestBody(req, rsp); |
| } |
| |
| @SuppressWarnings({"rawtypes"}) |
| public static SolrQueryRequest wrapParams(SolrQueryRequest req, Object... def) { |
| Map m = Utils.makeMap(def); |
| return wrapParams(req, m); |
| } |
| |
| @SuppressWarnings({"unchecked", "rawtypes"}) |
| public static SolrQueryRequest wrapParams(SolrQueryRequest req, Map m) { |
| ModifiableSolrParams solrParams = new ModifiableSolrParams(); |
| m.forEach((k, v) -> { |
| if(v == null) return; |
| solrParams.add(k.toString(), String.valueOf(v)); |
| }); |
| DefaultSolrParams dsp = new DefaultSolrParams(req.getParams(),solrParams); |
| req.setParams(dsp); |
| return req; |
| } |
| |
| @EndPoint(method = GET, |
| path = "/cluster/command-status", |
| permission = COLL_READ_PERM) |
| public void getCommandStatus(SolrQueryRequest req, SolrQueryResponse rsp) throws Exception { |
| CollectionsHandler.CollectionOperation.REQUESTSTATUS_OP.execute(req, rsp, collectionsHandler); |
| } |
| |
| @EndPoint(method = GET, |
| path = "/cluster/nodes", |
| permission = COLL_READ_PERM) |
| public void getNodes(SolrQueryRequest req, SolrQueryResponse rsp) { |
| rsp.add("nodes", getCoreContainer().getZkController().getClusterState().getLiveNodes()); |
| } |
| |
| private CoreContainer getCoreContainer() { |
| return collectionsHandler.getCoreContainer(); |
| } |
| |
| @EndPoint(method = POST, |
| path = "/cluster", |
| permission = COLL_EDIT_PERM) |
| public class Commands { |
| @Command(name = "add-role") |
| @SuppressWarnings({"rawtypes", "unchecked"}) |
| public void addRole(PayloadObj<RoleInfo> obj) throws Exception { |
| RoleInfo info = obj.get(); |
| Map m = info.toMap(new HashMap<>()); |
| m.put("action", ADDROLE.toString()); |
| collectionsHandler.handleRequestBody(wrapParams(obj.getRequest(), m), obj.getResponse()); |
| } |
| |
| @Command(name = "remove-role") |
| @SuppressWarnings({"rawtypes", "unchecked"}) |
| public void removeRole(PayloadObj<RoleInfo> obj) throws Exception { |
| RoleInfo info = obj.get(); |
| Map m = info.toMap(new HashMap<>()); |
| m.put("action", REMOVEROLE.toString()); |
| collectionsHandler.handleRequestBody(wrapParams(obj.getRequest(), m), obj.getResponse()); |
| } |
| |
| @Command(name = "set-obj-property") |
| @SuppressWarnings({"rawtypes", "unchecked"}) |
| public void setObjProperty(PayloadObj<ClusterPropInfo> obj) { |
| //Not using the object directly here because the API differentiate between {name:null} and {} |
| Map m = obj.getDataMap(); |
| ClusterProperties clusterProperties = new ClusterProperties(getCoreContainer().getZkController().getZkClient()); |
| try { |
| clusterProperties.setClusterProperties(m); |
| } catch (Exception e) { |
| throw new SolrException(SolrException.ErrorCode.SERVER_ERROR, "Error in API", e); |
| } |
| } |
| |
| @Command(name = "set-property") |
| public void setProperty(PayloadObj<Map<String,String>> obj) throws Exception { |
| Map<String,Object> m = obj.getDataMap(); |
| m.put("action", CLUSTERPROP.toString()); |
| collectionsHandler.handleRequestBody(wrapParams(obj.getRequest(),m ), obj.getResponse()); |
| } |
| |
| } |
| |
| public static class RoleInfo implements ReflectMapWriter { |
| @JsonProperty(required = true) |
| public String node; |
| @JsonProperty(required = true) |
| public String role; |
| |
| } |
| |
| |
| } |