/* * Copyright © 2015-2016 Cask Data, 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 co.cask.cdap.gateway.handlers; import co.cask.cdap.app.store.ServiceStore; import co.cask.cdap.common.conf.Constants; import co.cask.cdap.common.twill.MasterServiceManager; import co.cask.cdap.gateway.handlers.util.AbstractAppFabricHttpHandler; import co.cask.cdap.proto.SystemServiceMeta; import co.cask.http.HttpResponder; import com.google.common.collect.Lists; import com.google.gson.JsonObject; import com.google.inject.Inject; import org.jboss.netty.handler.codec.http.HttpRequest; import org.jboss.netty.handler.codec.http.HttpResponseStatus; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.SortedSet; import java.util.TreeSet; /** * Monitor Handler returns the status of different discoverable services */ public class AbstractMonitorHandler extends AbstractAppFabricHttpHandler { private static final Logger LOG = LoggerFactory.getLogger(AbstractMonitorHandler.class); private final Map<String, MasterServiceManager> serviceManagementMap; private static final String STATUSOK = Constants.Monitor.STATUS_OK; private static final String STATUSNOTOK = Constants.Monitor.STATUS_NOTOK; private static final String NOTAPPLICABLE = "NA"; private final ServiceStore serviceStore; @Inject public AbstractMonitorHandler(Map<String, MasterServiceManager> serviceMap, ServiceStore serviceStore) throws Exception { this.serviceManagementMap = serviceMap; this.serviceStore = serviceStore; } public void getServiceLiveInfo(HttpRequest request, HttpResponder responder, String serviceName) throws Exception { if (!serviceManagementMap.containsKey(serviceName)) { responder.sendString(HttpResponseStatus.NOT_FOUND, String.format("Invalid service name %s", serviceName)); return; } MasterServiceManager serviceManager = serviceManagementMap.get(serviceName); if (serviceManager.isServiceEnabled()) { responder.sendJson(HttpResponseStatus.OK, serviceManager.getLiveInfo()); } else { responder.sendString(HttpResponseStatus.FORBIDDEN, String.format("Service %s is not enabled", serviceName)); } } public void getServiceInstance(HttpRequest request, HttpResponder responder, String serviceName) throws Exception { JsonObject reply = new JsonObject(); if (!serviceManagementMap.containsKey(serviceName)) { responder.sendString(HttpResponseStatus.NOT_FOUND, String.format("Invalid service name %s", serviceName)); return; } MasterServiceManager serviceManager = serviceManagementMap.get(serviceName); if (serviceManager.isServiceEnabled()) { int actualInstance = serviceManagementMap.get(serviceName).getInstances(); reply.addProperty("provisioned", actualInstance); reply.addProperty("requested", getSystemServiceInstanceCount(serviceName)); responder.sendJson(HttpResponseStatus.OK, reply); } else { responder.sendString(HttpResponseStatus.FORBIDDEN, String.format("Service %s is not enabled", serviceName)); } } public void setServiceInstance(HttpRequest request, HttpResponder responder, String serviceName) { try { if (!serviceManagementMap.containsKey(serviceName)) { responder.sendString(HttpResponseStatus.NOT_FOUND, "Invalid Service Name"); return; } MasterServiceManager serviceManager = serviceManagementMap.get(serviceName); int instances = getInstances(request); if (!serviceManager.isServiceEnabled()) { responder.sendString(HttpResponseStatus.FORBIDDEN, String.format("Service %s is not enabled", serviceName)); return; } int currentInstances = getSystemServiceInstanceCount(serviceName); if (instances < serviceManager.getMinInstances() || instances > serviceManager.getMaxInstances()) { String response = String.format("Instance count should be between [%s,%s]", serviceManager.getMinInstances(), serviceManager.getMaxInstances()); responder.sendString(HttpResponseStatus.BAD_REQUEST, response); return; } else if (instances == currentInstances) { responder.sendStatus(HttpResponseStatus.OK); return; } serviceStore.setServiceInstance(serviceName, instances); if (serviceManager.setInstances(instances)) { responder.sendStatus(HttpResponseStatus.OK); } else { responder.sendString(HttpResponseStatus.BAD_REQUEST, "Operation did not succeed"); } } catch (Exception e) { responder.sendString(HttpResponseStatus.INTERNAL_SERVER_ERROR, String.format("Error updating instances for service: %s", serviceName)); } } public void getBootStatus(HttpRequest request, HttpResponder responder) { Map<String, String> result = new HashMap<>(); for (String service : serviceManagementMap.keySet()) { MasterServiceManager masterServiceManager = serviceManagementMap.get(service); if (masterServiceManager.isServiceEnabled() && masterServiceManager.canCheckStatus()) { String status = masterServiceManager.isServiceAvailable() ? STATUSOK : STATUSNOTOK; result.put(service, status); } } responder.sendJson(HttpResponseStatus.OK, result); } public void monitor(HttpRequest request, HttpResponder responder, String serviceName) { if (!serviceManagementMap.containsKey(serviceName)) { responder.sendString(HttpResponseStatus.NOT_FOUND, String.format("Invalid service name %s", serviceName)); return; } MasterServiceManager masterServiceManager = serviceManagementMap.get(serviceName); if (!masterServiceManager.isServiceEnabled()) { responder.sendString(HttpResponseStatus.FORBIDDEN, String.format("Service %s is not enabled", serviceName)); return; } if (masterServiceManager.canCheckStatus() && masterServiceManager.isServiceAvailable()) { JsonObject json = new JsonObject(); json.addProperty("status", STATUSOK); responder.sendJson(HttpResponseStatus.OK, json); } else if (masterServiceManager.canCheckStatus()) { JsonObject json = new JsonObject(); json.addProperty("status", STATUSNOTOK); responder.sendJson(HttpResponseStatus.OK, json); } else { responder.sendString(HttpResponseStatus.BAD_REQUEST, "Operation not valid for this service"); } } public void getServiceSpec(HttpRequest request, HttpResponder responder) throws Exception { List<SystemServiceMeta> response = Lists.newArrayList(); SortedSet<String> services = new TreeSet<>(serviceManagementMap.keySet()); List<String> serviceList = new ArrayList<>(services); for (String service : serviceList) { MasterServiceManager serviceManager = serviceManagementMap.get(service); if (serviceManager.isServiceEnabled()) { String logs = serviceManager.isLogAvailable() ? Constants.Monitor.STATUS_OK : Constants.Monitor.STATUS_NOTOK; String canCheck = serviceManager.canCheckStatus() ? ( serviceManager.isServiceAvailable() ? STATUSOK : STATUSNOTOK) : NOTAPPLICABLE; //TODO: Add metric name for Event Rate monitoring response.add(new SystemServiceMeta(service, serviceManager.getDescription(), canCheck, logs, serviceManager.getMinInstances(), serviceManager.getMaxInstances(), getSystemServiceInstanceCount(service), serviceManager.getInstances())); } } responder.sendJson(HttpResponseStatus.OK, response); } private int getSystemServiceInstanceCount(String serviceName) throws Exception { Integer count = serviceStore.getServiceInstance(serviceName); //In standalone mode, this count will be null. And thus we just return the actual instance count. if (count == null) { return serviceManagementMap.get(serviceName).getInstances(); } else { return count; } } public void restartAllServiceInstances(HttpRequest request, HttpResponder responder, String serviceName) { restartInstances(responder, serviceName, -1, true); } public void restartServiceInstance(HttpRequest request, HttpResponder responder, String serviceName, int instanceId) { restartInstances(responder, serviceName, instanceId, false); } private void restartInstances(HttpResponder responder, String serviceName, int instanceId, boolean restartAll) { long startTimeMs = System.currentTimeMillis(); boolean isSuccess = true; if (!serviceManagementMap.containsKey(serviceName)) { responder.sendString(HttpResponseStatus.NOT_FOUND, String.format("Invalid service name %s", serviceName)); return; } MasterServiceManager masterServiceManager = serviceManagementMap.get(serviceName); try { if (!masterServiceManager.isServiceEnabled()) { String message = String.format("Failed to restart instance for %s because the service is not enabled.", serviceName); LOG.debug(message); isSuccess = false; responder.sendString(HttpResponseStatus.FORBIDDEN, message); return; } if (restartAll) { masterServiceManager.restartAllInstances(); } else { if (instanceId < 0 || instanceId >= masterServiceManager.getInstances()) { throw new IllegalArgumentException(); } masterServiceManager.restartInstances(instanceId); } responder.sendStatus(HttpResponseStatus.OK); } catch (IllegalStateException ise) { String message = String.format("Failed to restart instance for %s because the service may not be ready yet", serviceName); LOG.debug(message, ise); isSuccess = false; responder.sendString(HttpResponseStatus.SERVICE_UNAVAILABLE, message); } catch (IllegalArgumentException iex) { String message = String.format("Failed to restart instance %d for service: %s because invalid instance id", instanceId, serviceName); LOG.debug(message, iex); isSuccess = false; responder.sendString(HttpResponseStatus.BAD_REQUEST, message); } catch (Exception ex) { LOG.warn(String.format("Exception when trying to restart instances for service %s", serviceName), ex); isSuccess = false; responder.sendString(HttpResponseStatus.INTERNAL_SERVER_ERROR, String.format("Error restarting instance %d for service: %s", instanceId, serviceName)); } finally { long endTimeMs = System.currentTimeMillis(); if (restartAll) { serviceStore.setRestartAllInstancesRequest(serviceName, startTimeMs, endTimeMs, isSuccess); } else { serviceStore.setRestartInstanceRequest(serviceName, startTimeMs, endTimeMs, isSuccess, instanceId); } } } public void getLatestRestartServiceInstanceStatus(HttpRequest request, HttpResponder responder, String serviceName) { try { responder.sendJson(HttpResponseStatus.OK, serviceStore.getLatestRestartInstancesRequest(serviceName)); } catch (IllegalStateException ex) { responder.sendString(HttpResponseStatus.NOT_FOUND, String.format("No restart instances request found or %s", serviceName)); } } }