blob: 00508b807523f083dbfd9ff9b514f1890ef8dd38 [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.hadoop.yarn.server.resourcemanager.webapp;
import static org.apache.hadoop.yarn.util.StringHelper.join;
import static org.apache.hadoop.yarn.webapp.YarnWebParams.APPLICATION_ID;
import static org.apache.hadoop.yarn.webapp.view.JQueryUI._EVEN;
import static org.apache.hadoop.yarn.webapp.view.JQueryUI._INFO_WRAP;
import static org.apache.hadoop.yarn.webapp.view.JQueryUI._ODD;
import static org.apache.hadoop.yarn.webapp.view.JQueryUI._TH;
import java.util.Collection;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.security.UserGroupInformation;
import org.apache.hadoop.util.StringUtils;
import org.apache.hadoop.yarn.api.records.ApplicationAccessType;
import org.apache.hadoop.yarn.api.records.ApplicationId;
import org.apache.hadoop.yarn.api.records.FinalApplicationStatus;
import org.apache.hadoop.yarn.api.records.QueueACL;
import org.apache.hadoop.yarn.api.records.Resource;
import org.apache.hadoop.yarn.api.records.ResourceRequest;
import org.apache.hadoop.yarn.api.records.YarnApplicationState;
import org.apache.hadoop.yarn.conf.YarnConfiguration;
import org.apache.hadoop.yarn.server.resourcemanager.RMContext;
import org.apache.hadoop.yarn.server.resourcemanager.ResourceManager;
import org.apache.hadoop.yarn.server.resourcemanager.rmapp.RMApp;
import org.apache.hadoop.yarn.server.resourcemanager.rmapp.RMAppMetrics;
import org.apache.hadoop.yarn.server.resourcemanager.rmapp.attempt.RMAppAttempt;
import org.apache.hadoop.yarn.server.resourcemanager.rmapp.attempt.RMAppAttemptMetrics;
import org.apache.hadoop.yarn.server.resourcemanager.webapp.dao.AppAttemptInfo;
import org.apache.hadoop.yarn.server.resourcemanager.webapp.dao.AppInfo;
import org.apache.hadoop.yarn.util.Apps;
import org.apache.hadoop.yarn.util.Times;
import org.apache.hadoop.yarn.util.resource.Resources;
import org.apache.hadoop.yarn.webapp.hamlet.Hamlet;
import org.apache.hadoop.yarn.webapp.hamlet.Hamlet.DIV;
import org.apache.hadoop.yarn.webapp.hamlet.Hamlet.TABLE;
import org.apache.hadoop.yarn.webapp.hamlet.Hamlet.TBODY;
import org.apache.hadoop.yarn.webapp.util.WebAppUtils;
import org.apache.hadoop.yarn.webapp.view.HtmlBlock;
import org.apache.hadoop.yarn.webapp.view.InfoBlock;
import com.google.inject.Inject;
public class AppBlock extends HtmlBlock {
private final Configuration conf;
private final ResourceManager rm;
private final boolean rmWebAppUIActions;
@Inject
AppBlock(ResourceManager rm, ViewContext ctx, Configuration conf) {
super(ctx);
this.conf = conf;
this.rm = rm;
this.rmWebAppUIActions =
conf.getBoolean(YarnConfiguration.RM_WEBAPP_UI_ACTIONS_ENABLED,
YarnConfiguration.DEFAULT_RM_WEBAPP_UI_ACTIONS_ENABLED);
}
@Override
protected void render(Block html) {
String aid = $(APPLICATION_ID);
if (aid.isEmpty()) {
puts("Bad request: requires application ID");
return;
}
ApplicationId appID = null;
try {
appID = Apps.toAppID(aid);
} catch (Exception e) {
puts("Invalid Application ID: " + aid);
return;
}
RMContext context = this.rm.getRMContext();
RMApp rmApp = context.getRMApps().get(appID);
if (rmApp == null) {
puts("Application not found: "+ aid);
return;
}
AppInfo app =
new AppInfo(rm, rmApp, true, WebAppUtils.getHttpSchemePrefix(conf));
// Check for the authorization.
String remoteUser = request().getRemoteUser();
UserGroupInformation callerUGI = null;
if (remoteUser != null) {
callerUGI = UserGroupInformation.createRemoteUser(remoteUser);
}
if (callerUGI != null
&& !(this.rm.getApplicationACLsManager().checkAccess(callerUGI,
ApplicationAccessType.VIEW_APP, app.getUser(), appID) || this.rm
.getQueueACLsManager().checkAccess(callerUGI,
QueueACL.ADMINISTER_QUEUE, app.getQueue()))) {
puts("You (User " + remoteUser
+ ") are not authorized to view application " + appID);
return;
}
setTitle(join("Application ", aid));
if (rmWebAppUIActions) {
// Application Kill
html.div()
.button()
.$onclick("confirmAction()").b("Kill Application")._()
._();
StringBuilder script = new StringBuilder();
script.append("function confirmAction() {")
.append(" b = confirm(\"Are you sure?\");")
.append(" if (b == true) {")
.append(" $.ajax({")
.append(" type: 'PUT',")
.append(" url: '/ws/v1/cluster/apps/").append(aid).append("/state',")
.append(" contentType: 'application/json',")
.append(" data: '{\"state\":\"KILLED\"}',")
.append(" dataType: 'json'")
.append(" }).done(function(data){")
.append(" setTimeout(function(){")
.append(" location.href = '/cluster/app/").append(aid).append("';")
.append(" }, 1000);")
.append(" }).fail(function(data){")
.append(" console.log(data);")
.append(" });")
.append(" }")
.append("}");
html.script().$type("text/javascript")._(script.toString())._();
}
RMAppMetrics appMerics = rmApp.getRMAppMetrics();
// Get attempt metrics and fields, it is possible currentAttempt of RMApp is
// null. In that case, we will assume resource preempted and number of Non
// AM container preempted on that attempt is 0
RMAppAttemptMetrics attemptMetrics;
if (null == rmApp.getCurrentAppAttempt()) {
attemptMetrics = null;
} else {
attemptMetrics = rmApp.getCurrentAppAttempt().getRMAppAttemptMetrics();
}
Resource attemptResourcePreempted =
attemptMetrics == null ? Resources.none() : attemptMetrics
.getResourcePreempted();
int attemptNumNonAMContainerPreempted =
attemptMetrics == null ? 0 : attemptMetrics
.getNumNonAMContainersPreempted();
info("Application Overview")
._("User:", app.getUser())
._("Name:", app.getName())
._("Application Type:", app.getApplicationType())
._("Application Tags:", app.getApplicationTags())
._("YarnApplicationState:", clarifyAppState(app.getState()))
._("FinalStatus Reported by AM:",
clairfyAppFinalStatus(app.getFinalStatus()))
._("Started:", Times.format(app.getStartTime()))
._("Elapsed:",
StringUtils.formatTime(Times.elapsed(app.getStartTime(),
app.getFinishTime())))
._("Tracking URL:",
!app.isTrackingUrlReady() ? "#" : app.getTrackingUrlPretty(),
app.getTrackingUI())
._("Diagnostics:", app.getNote());
DIV<Hamlet> pdiv = html.
_(InfoBlock.class).
div(_INFO_WRAP);
info("Application Overview").clear();
info("Application Metrics")
._("Total Resource Preempted:",
appMerics.getResourcePreempted())
._("Total Number of Non-AM Containers Preempted:",
String.valueOf(appMerics.getNumNonAMContainersPreempted()))
._("Total Number of AM Containers Preempted:",
String.valueOf(appMerics.getNumAMContainersPreempted()))
._("Resource Preempted from Current Attempt:",
attemptResourcePreempted)
._("Number of Non-AM Containers Preempted from Current Attempt:",
attemptNumNonAMContainerPreempted)
._("Aggregate Resource Allocation:",
String.format("%d MB-seconds, %d vcore-seconds",
appMerics.getMemorySeconds(), appMerics.getVcoreSeconds()));
pdiv._();
Collection<RMAppAttempt> attempts = rmApp.getAppAttempts().values();
String amString =
attempts.size() == 1 ? "ApplicationMaster" : "ApplicationMasters";
DIV<Hamlet> div = html.
_(InfoBlock.class).
div(_INFO_WRAP);
// MRAppMasters Table
TABLE<DIV<Hamlet>> table = div.table("#app");
table.
tr().
th(amString).
_().
tr().
th(_TH, "Attempt Number").
th(_TH, "Start Time").
th(_TH, "Node").
th(_TH, "Logs").
_();
boolean odd = false;
for (RMAppAttempt attempt : attempts) {
AppAttemptInfo attemptInfo = new AppAttemptInfo(attempt, app.getUser());
table.tr((odd = !odd) ? _ODD : _EVEN).
td(String.valueOf(attemptInfo.getAttemptId())).
td(Times.format(attemptInfo.getStartTime())).
td().a(".nodelink", url("//",
attemptInfo.getNodeHttpAddress()),
attemptInfo.getNodeHttpAddress())._().
td().a(".logslink", url(attemptInfo.getLogsLink()), "logs")._().
_();
}
table._();
div._();
createContainerLocalityTable(html, attemptMetrics);
createResourceRequestsTable(html, app);
}
private void createContainerLocalityTable(Block html,
RMAppAttemptMetrics attemptMetrics) {
if (attemptMetrics == null) {
return;
}
DIV<Hamlet> div = html.div(_INFO_WRAP);
TABLE<DIV<Hamlet>> table =
div.h3(
"Total Allocated Containers: "
+ attemptMetrics.getTotalAllocatedContainers()).h3("Each table cell"
+ " represents the number of NodeLocal/RackLocal/OffSwitch containers"
+ " satisfied by NodeLocal/RackLocal/OffSwitch resource requests.").table(
"#containerLocality");
table.
tr().
th(_TH, "").
th(_TH, "Node Local Request").
th(_TH, "Rack Local Request").
th(_TH, "Off Switch Request").
_();
String[] containersType =
{ "Num Node Local Containers (satisfied by)", "Num Rack Local Containers (satisfied by)",
"Num Off Switch Containers (satisfied by)" };
boolean odd = false;
for (int i = 0; i < attemptMetrics.getLocalityStatistics().length; i++) {
table.tr((odd = !odd) ? _ODD : _EVEN).td(containersType[i])
.td(String.valueOf(attemptMetrics.getLocalityStatistics()[i][0]))
.td(i == 0 ? "" : String.valueOf(attemptMetrics.getLocalityStatistics()[i][1]))
.td(i <= 1 ? "" : String.valueOf(attemptMetrics.getLocalityStatistics()[i][2]))._();
}
table._();
div._();
}
private void createResourceRequestsTable(Block html, AppInfo app) {
TBODY<TABLE<Hamlet>> tbody =
html.table("#ResourceRequests").thead().tr()
.th(".priority", "Priority")
.th(".resourceName", "Resource Name")
.th(".totalResource", "Capability")
.th(".numContainers", "Num Containers")
.th(".relaxLocality", "Relax Locality")
.th(".nodeLabelExpression", "Node Label Expression")._()._().tbody();
Resource totalResource = Resource.newInstance(0, 0);
if (app.getResourceRequests() != null) {
for (ResourceRequest request : app.getResourceRequests()) {
if (request.getNumContainers() == 0) {
continue;
}
tbody.tr()
.td(String.valueOf(request.getPriority()))
.td(request.getResourceName())
.td(String.valueOf(request.getCapability()))
.td(String.valueOf(request.getNumContainers()))
.td(String.valueOf(request.getRelaxLocality()))
.td(request.getNodeLabelExpression() == null ? "N/A" : request
.getNodeLabelExpression())._();
if (request.getResourceName().equals(ResourceRequest.ANY)) {
Resources.addTo(totalResource,
Resources.multiply(request.getCapability(),
request.getNumContainers()));
}
}
}
html.div().$class("totalResourceRequests")
.h3("Total Outstanding Resource Requests: " + totalResource)._();
tbody._()._();
}
private String clarifyAppState(YarnApplicationState state) {
String ret = state.toString();
switch (state) {
case NEW:
return ret + ": waiting for application to be initialized";
case NEW_SAVING:
return ret + ": waiting for application to be persisted in state-store.";
case SUBMITTED:
return ret + ": waiting for application to be accepted by scheduler.";
case ACCEPTED:
return ret + ": waiting for AM container to be allocated, launched and"
+ " register with RM.";
case RUNNING:
return ret + ": AM has registered with RM and started running.";
default:
return ret;
}
}
private String clairfyAppFinalStatus(FinalApplicationStatus status) {
if (status == FinalApplicationStatus.UNDEFINED) {
return "Application has not completed yet.";
}
return status.toString();
}
}