/*
* Copyright 2012 Netflix, Inc.
*
* Licensed 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 com.netflix.exhibitor.core.rest;
import com.netflix.exhibitor.core.activity.QueueGroups;
import com.netflix.exhibitor.core.automanage.ClusterStatusTask;
import com.netflix.exhibitor.core.automanage.RemoteInstanceRequest;
import com.netflix.exhibitor.core.config.InstanceConfig;
import com.netflix.exhibitor.core.config.IntConfigs;
import com.netflix.exhibitor.core.config.StringConfigs;
import com.netflix.exhibitor.core.controlpanel.ControlPanelTypes;
import com.netflix.exhibitor.core.entities.Result;
import com.netflix.exhibitor.core.entities.ServerStatus;
import com.netflix.exhibitor.core.state.FourLetterWord;
import com.netflix.exhibitor.core.state.InstanceStateTypes;
import com.netflix.exhibitor.core.state.KillRunningInstance;
import com.netflix.exhibitor.core.state.MonitorRunningInstance;
import com.netflix.exhibitor.core.state.ServerList;
import com.netflix.exhibitor.core.state.ServerSpec;
import com.netflix.exhibitor.core.state.StartInstance;
import org.codehaus.jackson.map.ObjectMapper;
import org.codehaus.jackson.node.ArrayNode;
import org.codehaus.jackson.node.JsonNodeFactory;
import org.codehaus.jackson.node.ObjectNode;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.GenericEntity;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.UriInfo;
import javax.ws.rs.ext.ContextResolver;
import java.net.URLEncoder;
import java.util.List;
import java.util.concurrent.Callable;
@SuppressWarnings("UnusedDeclaration")
@Path("exhibitor/v1/cluster")
public class ClusterResource
{
private final UIContext context;
public ClusterResource(@Context ContextResolver<UIContext> resolver)
{
context = resolver.getContext(UIContext.class);
}
@Path("status")
@GET
@Produces(MediaType.APPLICATION_JSON)
public Response getClusterStatus() throws Exception
{
InstanceConfig config = context.getExhibitor().getConfigManager().getConfig();
ServerList serverList = new ServerList(config.getString(StringConfigs.SERVERS_SPEC));
ClusterStatusTask task = new ClusterStatusTask(context.getExhibitor(), serverList.getSpecs());
List<ServerStatus> statuses = context.getExhibitor().getForkJoinPool().invoke(task);
GenericEntity<List<ServerStatus>> entity = new GenericEntity<List<ServerStatus>>(statuses){};
return Response.ok(entity).build();
}
@Path("state/{hostname}")
@GET
@Produces(MediaType.APPLICATION_JSON)
public String remoteGetStatus(@Context UriInfo uriInfo, @PathParam("hostname") String hostname) throws Exception
{
return makeRemoteRequest
(
"getStatus",
hostname,
true,
new Callable<String>()
{
@Override
public String call() throws Exception
{
return getStatus();
}
}
);
}
@Path("set/{type}/{value}/{hostname}")
@GET
@Produces(MediaType.APPLICATION_JSON)
public String remoteSetControlPanelSetting(@Context UriInfo uriInfo, @PathParam("hostname") String hostname, final @PathParam("type") String typeStr, final @PathParam("value") boolean newValue) throws Exception
{
return makeRemoteRequest
(
"setControlPanelSetting",
hostname,
true,
new Callable<String>()
{
@Override
public String call() throws Exception
{
return setControlPanelSetting(typeStr, newValue);
}
},
typeStr,
newValue
);
}
@Path("restart/{hostname}")
@GET
@Produces(MediaType.APPLICATION_JSON)
public String remoteStopStartZooKeeper(@Context UriInfo uriInfo, @PathParam("hostname") String hostname) throws Exception
{
return makeRemoteRequest
(
"stopStartZooKeeper",
hostname,
true,
new Callable<String>()
{
@Override
public String call() throws Exception
{
return stopStartZooKeeper();
}
}
);
}
@Path("start/{hostname}")
@GET
@Produces(MediaType.APPLICATION_JSON)
public String remoteStartZooKeeper(@Context UriInfo uriInfo, @PathParam("hostname") String hostname) throws Exception
{
return makeRemoteRequest
(
"startZooKeeper",
hostname,
true,
new Callable<String>()
{
@Override
public String call() throws Exception
{
return startZooKeeper();
}
}
);
}
@Path("stop/{hostname}")
@GET
@Produces(MediaType.APPLICATION_JSON)
public String remoteStopZooKeeper(@Context UriInfo uriInfo, @PathParam("hostname") String hostname) throws Exception
{
return makeRemoteRequest
(
"stopZooKeeper",
hostname,
true,
new Callable<String>()
{
@Override
public String call() throws Exception
{
return stopZooKeeper();
}
}
);
}
@Path("4ltr/{word}/{hostname}")
@GET
@Produces(MediaType.APPLICATION_JSON)
public String remoteGetFourLetterWord(@Context UriInfo uriInfo, @PathParam("hostname") String hostname, @PathParam("word") final String word) throws Exception
{
return makeRemoteRequest
(
"getFourLetterWord",
hostname,
false,
new Callable<String>()
{
@Override
public String call() throws Exception
{
return getFourLetterWord(word);
}
},
word
);
}
@Path("log/{hostname}")
@GET
@Produces(MediaType.APPLICATION_JSON)
public String remoteGetLog(@Context UriInfo uriInfo, @PathParam("hostname") String hostname) throws Exception
{
return makeRemoteRequest
(
"getLog",
hostname,
false,
new Callable<String>()
{
@Override
public String call() throws Exception
{
return getLog();
}
}
);
}
@Path("log")
@GET
@Produces(MediaType.APPLICATION_JSON)
public String getLog() throws Exception
{
String log = UIResource.getLog(context);
return JsonUtil.writeValueAsString(log);
}
@Path("4ltr/{word}")
@GET
@Produces(MediaType.APPLICATION_JSON)
public String getFourLetterWord(@PathParam("word") String word) throws Exception
{
InstanceConfig config = context.getExhibitor().getConfigManager().getConfig();
String value;
try
{
FourLetterWord.Word wordEnum = FourLetterWord.Word.valueOf(word.toUpperCase());
value = new FourLetterWord(wordEnum, config, context.getExhibitor().getConnectionTimeOutMs()).getResponse();
}
catch ( IllegalArgumentException e )
{
value = "* unknown *";
}
return JsonUtil.writeValueAsString(value);
}
@Path("restart")
@GET
@Produces(MediaType.APPLICATION_JSON)
public String stopStartZooKeeper() throws Exception
{
context.getExhibitor().getActivityQueue().add(QueueGroups.MAIN, new KillRunningInstance(context.getExhibitor(), true));
Result result = new Result("OK", true);
return JsonUtil.writeValueAsString(result);
}
@Path("stop")
@GET
@Produces(MediaType.APPLICATION_JSON)
public String stopZooKeeper() throws Exception
{
context.getExhibitor().getActivityQueue().add(QueueGroups.MAIN, new KillRunningInstance(context.getExhibitor(), false));
Result result = new Result("OK", true);
return JsonUtil.writeValueAsString(result);
}
@Path("start")
@GET
@Produces(MediaType.APPLICATION_JSON)
public String startZooKeeper() throws Exception
{
context.getExhibitor().getActivityQueue().add(QueueGroups.MAIN, new StartInstance(context.getExhibitor()));
Result result = new Result("OK", true);
return JsonUtil.writeValueAsString(result);
}
@Path("set/{type}/{value}")
@GET
@Produces(MediaType.APPLICATION_JSON)
public String setControlPanelSetting(@PathParam("type") String typeStr, @PathParam("value") boolean newValue) throws Exception
{
ControlPanelTypes type = null;
try
{
type = ControlPanelTypes.fuzzyFind(typeStr);
}
catch ( IllegalArgumentException ignore )
{
// ignore
}
Result result;
if ( type != null )
{
try
{
context.getExhibitor().getControlPanelValues().set(type, newValue);
result = new Result("OK", true);
}
catch ( Exception e )
{
result = new Result(e);
}
}
else
{
result = new Result("Not found", false);
}
return JsonUtil.writeValueAsString(result);
}
@Path("state")
@GET
@Produces(MediaType.APPLICATION_JSON)
public String getStatus() throws Exception
{
ObjectNode mainNode = JsonNodeFactory.instance.objectNode();
ObjectNode switchesNode = JsonNodeFactory.instance.objectNode();
for ( ControlPanelTypes type : ControlPanelTypes.values() )
{
switchesNode.put(UIResource.fixName(type), context.getExhibitor().getControlPanelValues().isSet(type));
}
mainNode.put("switches", switchesNode);
MonitorRunningInstance monitorRunningInstance = context.getExhibitor().getMonitorRunningInstance();
InstanceStateTypes state = monitorRunningInstance.getCurrentInstanceState();
mainNode.put("state", state.getCode());
mainNode.put("description", state.getDescription());
mainNode.put("isLeader", monitorRunningInstance.isCurrentlyLeader());
return JsonUtil.writeValueAsString(mainNode);
}
@Path("list")
@GET
@Produces(MediaType.APPLICATION_JSON)
public String getClusterAsJson() throws Exception
{
InstanceConfig config = context.getExhibitor().getConfigManager().getConfig();
ObjectNode node = JsonNodeFactory.instance.objectNode();
ArrayNode serversNode = JsonNodeFactory.instance.arrayNode();
ServerList serverList = new ServerList(config.getString(StringConfigs.SERVERS_SPEC));
for ( ServerSpec spec : serverList.getSpecs() )
{
serversNode.add(spec.getHostname());
}
node.put("servers", serversNode);
node.put("port", config.getInt(IntConfigs.CLIENT_PORT));
return JsonUtil.writeValueAsString(node);
}
@Path("list")
@GET
@Produces(MediaType.APPLICATION_FORM_URLENCODED)
public String getClusterAsExhibitor() throws Exception
{
InstanceConfig config = context.getExhibitor().getConfigManager().getConfig();
StringBuilder response = new StringBuilder();
ServerList serverList = new ServerList(config.getString(StringConfigs.SERVERS_SPEC));
response.append("count=").append(serverList.getSpecs().size());
int index = 0;
for ( ServerSpec spec : serverList.getSpecs() )
{
response.append("&server").append(index++).append("=").append(URLEncoder.encode(spec.getHostname(), "UTF-8"));
}
response.append("&port=").append(config.getInt(IntConfigs.CLIENT_PORT));
return response.toString();
}
private String makeRemoteRequest(String methodName, String hostname, boolean responseIsJson, Callable<String> proc, Object... values) throws Exception
{
String remoteResponse;
String errorMessage;
if ( hostname.equals("localhost") || hostname.equals(context.getExhibitor().getThisJVMHostname()) )
{
remoteResponse = proc.call();
errorMessage = "";
}
else
{
try
{
RemoteInstanceRequest request = new RemoteInstanceRequest(context.getExhibitor(), hostname);
RemoteInstanceRequest.Result result = request.makeRequest(context.getExhibitor().getRemoteInstanceRequestClient(), methodName, values);
remoteResponse = result.remoteResponse;
errorMessage = result.errorMessage;
}
catch ( Exception e )
{
remoteResponse = "{}";
errorMessage = e.getMessage();
if ( errorMessage == null )
{
errorMessage = "Unknown";
}
}
}
ObjectMapper mapper = new ObjectMapper();
ObjectNode node = JsonNodeFactory.instance.objectNode();
if ( responseIsJson )
{
node.put("response", mapper.readTree(mapper.getJsonFactory().createJsonParser(remoteResponse)));
}
else
{
node.put("response", remoteResponse);
}
node.put("errorMessage", errorMessage);
node.put("success", errorMessage.length() == 0);
return JsonUtil.writeValueAsString(node);
}
}