/*
* 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.hadoop.hive.llap.daemon.services.impl;
import java.io.IOException;
import java.io.PrintWriter;
import java.lang.management.ManagementFactory;
import java.lang.management.RuntimeMXBean;
import javax.management.MalformedObjectNameException;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.hive.common.classification.InterfaceAudience;
import org.apache.hadoop.hive.conf.HiveConf;
import org.apache.hadoop.hive.conf.HiveConf.ConfVars;
import org.apache.hadoop.hive.llap.registry.ServiceInstance;
import org.apache.hadoop.hive.llap.registry.impl.LlapRegistryService;
import org.apache.hadoop.hive.llap.registry.impl.LlapZookeeperRegistryImpl;
import org.apache.hadoop.security.UserGroupInformation;
import org.apache.hadoop.service.AbstractService;
import org.apache.hadoop.service.CompositeService;
import org.apache.hadoop.yarn.api.records.Resource;
import org.apache.hive.common.util.HiveVersionInfo;
import org.apache.hive.http.HttpServer;
import org.codehaus.jackson.JsonFactory;
import org.codehaus.jackson.JsonGenerator;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class LlapWebServices extends AbstractService {
private static final Logger LOG = LoggerFactory.getLogger(LlapWebServices.class);
// this is what allows the UI to do cross-domain reads of the contents
// only apply to idempotent GET ops (all others need crumbs)
static final String ACCESS_CONTROL_ALLOW_METHODS = "Access-Control-Allow-Methods";
static final String ACCESS_CONTROL_ALLOW_ORIGIN = "Access-Control-Allow-Origin";
static final String REGISTRY_ATTRIBUTE="llap.registry";
static final String PARENT_ATTRIBUTE="llap.parent";
private int port;
private HttpServer http;
private boolean useSSL = false;
private boolean useSPNEGO = false;
private final CompositeService parent;
private final LlapRegistryService registry;
public LlapWebServices(int port, CompositeService parent, LlapRegistryService registry) {
super("LlapWebServices");
this.port = port;
this.registry = registry;
this.parent = parent;
}
@Override
public void serviceInit(Configuration conf) {
this.useSSL = HiveConf.getBoolVar(conf, ConfVars.LLAP_DAEMON_WEB_SSL);
this.useSPNEGO = HiveConf.getBoolVar(conf, ConfVars.LLAP_WEB_AUTO_AUTH);
String bindAddress = "0.0.0.0";
HttpServer.Builder builder =
new HttpServer.Builder("llap").setPort(this.port).setHost(bindAddress);
builder.setConf(new HiveConf(conf, HiveConf.class));
if (UserGroupInformation.isSecurityEnabled()) {
LOG.info("LLAP UI useSSL=" + this.useSSL + ", auto-auth/SPNEGO="
+ this.useSPNEGO + ", port=" + this.port);
builder.setUseSSL(this.useSSL);
if (this.useSPNEGO) {
builder.setUseSPNEGO(true); // this setups auth filtering in build()
builder.setSPNEGOPrincipal(HiveConf.getVar(conf, ConfVars.LLAP_WEBUI_SPNEGO_PRINCIPAL,
HiveConf.getVar(conf, ConfVars.LLAP_KERBEROS_PRINCIPAL)));
builder.setSPNEGOKeytab(HiveConf.getVar(conf, ConfVars.LLAP_WEBUI_SPNEGO_KEYTAB_FILE,
HiveConf.getVar(conf, ConfVars.LLAP_KERBEROS_KEYTAB_FILE)));
}
}
builder.setContextAttribute(REGISTRY_ATTRIBUTE, registry);
builder.setContextAttribute(PARENT_ATTRIBUTE, parent);
try {
this.http = builder.build();
this.http.addServlet("status", "/status", LlapStatusServlet.class);
this.http.addServlet("peers", "/peers", LlapPeerRegistryServlet.class);
this.http.addServlet("iomem", "/iomem", LlapIoMemoryServlet.class);
} catch (IOException e) {
LOG.warn("LLAP web service failed to come up", e);
}
}
@InterfaceAudience.Private
public int getPort() {
return this.http.getPort();
}
@Override
public void serviceStart() throws Exception {
if (this.http != null) {
this.http.start();
}
}
@Override
public void serviceStop() throws Exception {
if (this.http != null) {
this.http.stop();
}
}
public static class LlapStatusServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
/**
* Possible values: UNKNOWN, NOTINITED, INITED, STARTED, STOPPED
*/
private static final String STATUS_ATTRIBUTE = "status";
private static final String UPTIME_ATTRIBUTE = "uptime";
private static final String BUILD_ATTRIBUTE = "build";
private static final String UNKNOWN_STATE = "UNKNOWN";
protected transient JsonFactory jsonFactory;
protected RuntimeMXBean runtimeBean;
@Override
public void init() throws ServletException {
jsonFactory = new JsonFactory();
runtimeBean = ManagementFactory.getRuntimeMXBean();
}
@Override
public void doGet(HttpServletRequest request, HttpServletResponse response) {
JsonGenerator jg = null;
PrintWriter writer = null;
final ServletContext context = getServletContext();
final Object parent = context.getAttribute(PARENT_ATTRIBUTE);
final long uptime = runtimeBean.getUptime();
try {
try {
writer = response.getWriter();
response.setContentType("application/json; charset=utf8");
response.setHeader(ACCESS_CONTROL_ALLOW_METHODS, "GET");
response.setHeader(ACCESS_CONTROL_ALLOW_ORIGIN, "*");
jg = jsonFactory.createJsonGenerator(writer);
jg.disable(JsonGenerator.Feature.AUTO_CLOSE_TARGET);
jg.useDefaultPrettyPrinter();
jg.writeStartObject();
if (parent != null && parent instanceof CompositeService) {
jg.writeStringField(STATUS_ATTRIBUTE, ((CompositeService) parent).getServiceState()
.toString());
} else {
jg.writeStringField(STATUS_ATTRIBUTE, UNKNOWN_STATE);
}
jg.writeNumberField(UPTIME_ATTRIBUTE, uptime);
jg.writeStringField(BUILD_ATTRIBUTE, HiveVersionInfo.getBuildVersion());
jg.writeEndObject();
} finally {
if (jg != null) {
jg.close();
}
if (writer != null) {
writer.close();
}
}
} catch (IOException e) {
LOG.error("Caught an exception while processing /status request", e);
response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
}
}
}
public static class LlapPeerRegistryServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
protected transient JsonFactory jsonFactory;
@Override
public void init() throws ServletException {
jsonFactory = new JsonFactory();
}
@Override
public void doGet(HttpServletRequest request, HttpServletResponse response) {
JsonGenerator jg = null;
PrintWriter writer = null;
final ServletContext context = getServletContext();
final LlapRegistryService registry = (LlapRegistryService)context.getAttribute(REGISTRY_ATTRIBUTE);
try {
// admin check
if (!HttpServer.isInstrumentationAccessAllowed(context, request, response)) {
return;
}
try {
writer = response.getWriter();
response.setContentType("application/json; charset=utf8");
response.setHeader(ACCESS_CONTROL_ALLOW_METHODS, "GET");
response.setHeader(ACCESS_CONTROL_ALLOW_ORIGIN, "*");
jg = jsonFactory.createJsonGenerator(writer);
jg.disable(JsonGenerator.Feature.AUTO_CLOSE_TARGET);
jg.useDefaultPrettyPrinter();
jg.writeStartObject();
if (registry.isDynamic()) {
jg.writeBooleanField("dynamic", true);
}
jg.writeStringField("identity", registry.getWorkerIdentity());
jg.writeArrayFieldStart("peers");
for (ServiceInstance s : registry.getInstances().getAllInstancesOrdered(false)) {
jg.writeStartObject();
jg.writeStringField("identity", s.getWorkerIdentity());
jg.writeStringField("host", s.getHost());
jg.writeNumberField("management-port", s.getManagementPort());
jg.writeNumberField("rpc-port", s.getRpcPort());
jg.writeNumberField("shuffle-port", s.getShufflePort());
Resource r = s.getResource();
if (r != null) {
jg.writeObjectFieldStart("resource");
jg.writeNumberField("vcores", r.getVirtualCores());
jg.writeNumberField("memory", r.getMemory());
jg.writeEndObject();
}
jg.writeStringField("host", s.getHost());
jg.writeEndObject();
}
jg.writeEndArray();
jg.writeEndObject();
} finally {
if (jg != null) {
jg.close();
}
if (writer != null) {
writer.close();
}
}
} catch (IOException e) {
LOG.error("Caught an exception while processing /status request", e);
response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
}
}
}
}