MetricsOverviewTable.java
/**
* 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
* <p>
* http://www.apache.org/licenses/LICENSE-2.0
* <p>
* 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.router.webapp;
import com.google.inject.Inject;
import org.apache.commons.lang3.StringUtils;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.yarn.conf.YarnConfiguration;
import org.apache.hadoop.yarn.exceptions.YarnException;
import org.apache.hadoop.yarn.server.federation.store.records.SubClusterInfo;
import org.apache.hadoop.yarn.server.resourcemanager.webapp.RMWSConsts;
import org.apache.hadoop.yarn.server.resourcemanager.webapp.dao.ClusterMetricsInfo;
import org.apache.hadoop.yarn.server.resourcemanager.webapp.dao.SchedulerOverviewInfo;
import org.apache.hadoop.yarn.server.router.Router;
import org.apache.hadoop.yarn.server.router.webapp.dao.RouterClusterMetrics;
import org.apache.hadoop.yarn.server.router.webapp.dao.RouterSchedulerMetrics;
import org.apache.hadoop.yarn.webapp.hamlet2.Hamlet;
import org.apache.hadoop.yarn.webapp.util.WebAppUtils;
import javax.ws.rs.client.Client;
import java.io.IOException;
import java.util.Collection;
import java.util.List;
public class MetricsOverviewTable extends RouterBlock {
private final Router router;
@Inject
MetricsOverviewTable(Router router, ViewContext ctx) {
super(router, ctx);
this.router = router;
}
@Override
protected void render(Block html) {
// Initialize page styles
html.style(".metrics {margin-bottom:5px}");
// get routerClusterMetrics Info
ClusterMetricsInfo routerClusterMetricsInfo = getRouterClusterMetricsInfo();
RouterClusterMetrics routerClusterMetrics = new RouterClusterMetrics(routerClusterMetricsInfo);
// metrics div
Hamlet.DIV<Hamlet> div = html.div().$class("metrics");
try {
initFederationClusterAppsMetrics(div, routerClusterMetrics);
initFederationClusterNodesMetrics(div, routerClusterMetrics);
List<SubClusterInfo> subClusters = getSubClusterInfoList();
initFederationClusterSchedulersMetrics(div, routerClusterMetrics, subClusters);
} catch (Exception e) {
LOG.error("MetricsOverviewTable init error.", e);
}
div.__();
}
protected void render(Block html, String subClusterId) {
// Initialize page styles
html.style(".metrics {margin-bottom:5px}");
// get subClusterId ClusterMetrics Info
ClusterMetricsInfo clusterMetricsInfo =
getClusterMetricsInfoBySubClusterId(subClusterId);
RouterClusterMetrics routerClusterMetrics =
new RouterClusterMetrics(clusterMetricsInfo, subClusterId);
// metrics div
Hamlet.DIV<Hamlet> div = html.div().$class("metrics");
try {
initFederationClusterAppsMetrics(div, routerClusterMetrics);
initFederationClusterNodesMetrics(div, routerClusterMetrics);
Collection<SubClusterInfo> subClusters = getSubClusterInfoList(subClusterId);
initFederationClusterSchedulersMetrics(div, routerClusterMetrics, subClusters);
} catch (Exception e) {
LOG.error("MetricsOverviewTable init error.", e);
}
div.__();
}
/**
* Init Federation Cluster Apps Metrics.
* Contains App information, resource usage information.
*
* @param div data display div.
* @param metrics data metric information.
*/
private void initFederationClusterAppsMetrics(Hamlet.DIV<Hamlet> div,
RouterClusterMetrics metrics) {
div.h3(metrics.getWebPageTitlePrefix() + " Cluster Metrics").
table("#metricsoverview").
thead().$class("ui-widget-header").
// Initialize table header information
tr().
th().$class("ui-state-default").__("Apps Submitted").__().
th().$class("ui-state-default").__("Apps Pending").__().
th().$class("ui-state-default").__("Apps Running").__().
th().$class("ui-state-default").__("Apps Completed").__().
th().$class("ui-state-default").__("Containers Running").__().
th().$class("ui-state-default").__("Used Resources").__().
th().$class("ui-state-default").__("Total Resources").__().
th().$class("ui-state-default").__("Reserved Resources").__().
th().$class("ui-state-default").__("Physical Mem Used %").__().
th().$class("ui-state-default").__("Physical VCores Used %").__().
__().
__().
// Initialize table data information
tbody().$class("ui-widget-content").
tr().
td(metrics.getAppsSubmitted()).
td(metrics.getAppsPending()).
td(String.valueOf(metrics.getAppsRunning())).
td(metrics.getAppsCompleted()).
td(metrics.getAllocatedContainers()).
td(metrics.getUsedResources()).
td(metrics.getTotalResources()).
td(metrics.getReservedResources()).
td(metrics.getUtilizedMBPercent()).
td(metrics.getUtilizedVirtualCoresPercent()).
__().
__().__();
}
/**
* Init Federation Cluster Nodes Metrics.
*
* @param div data display div.
* @param metrics data metric information.
*/
private void initFederationClusterNodesMetrics(Hamlet.DIV<Hamlet> div,
RouterClusterMetrics metrics) {
div.h3(metrics.getWebPageTitlePrefix() + " Cluster Nodes Metrics").
table("#nodemetricsoverview").
thead().$class("ui-widget-header").
// Initialize table header information
tr().
th().$class("ui-state-default").__("Active Nodes").__().
th().$class("ui-state-default").__("Decommissioning Nodes").__().
th().$class("ui-state-default").__("Decommissioned Nodes").__().
th().$class("ui-state-default").__("Lost Nodes").__().
th().$class("ui-state-default").__("Unhealthy Nodes").__().
th().$class("ui-state-default").__("Rebooted Nodes").__().
th().$class("ui-state-default").__("Shutdown Nodes").__().
__().
__().
// Initialize table data information
tbody().$class("ui-widget-content").
tr().
td().a(url("nodes"), String.valueOf(metrics.getActiveNodes())).__().
td().a(url("nodes/router/?node.state=decommissioning"),
String.valueOf(metrics.getDecommissioningNodes())).__().
td().a(url("nodes/router/?node.state=decommissioned"),
String.valueOf(metrics.getDecommissionedNodes())).__().
td().a(url("nodes/router/?node.state=lost"),
String.valueOf(metrics.getLostNodes())).__().
td().a(url("nodes/router/?node.state=unhealthy"),
String.valueOf(metrics.getUnhealthyNodes())).__().
td().a(url("nodes/router/?node.state=rebooted"),
String.valueOf(metrics.getRebootedNodes())).__().
td().a(url("nodes/router/?node.state=shutdown"),
String.valueOf(metrics.getShutdownNodes())).__().
__().
__().__();
}
/**
* Init Federation Cluster SchedulersMetrics.
*
* @param div data display div.
* @param metrics data metric information.
* @param subclusters active subcluster List.
* @throws YarnException yarn error.
* @throws IOException io error.
* @throws InterruptedException interrupt error.
*/
private void initFederationClusterSchedulersMetrics(Hamlet.DIV<Hamlet> div,
RouterClusterMetrics metrics, Collection<SubClusterInfo> subclusters)
throws YarnException, IOException, InterruptedException {
Hamlet.TBODY<Hamlet.TABLE<Hamlet.DIV<Hamlet>>> fsMetricsScheduleTr =
div.h3(metrics.getWebPageTitlePrefix() + " Scheduler Metrics").
table("#schedulermetricsoverview").
thead().$class("ui-widget-header").
tr().
th().$class("ui-state-default").__("SubCluster").__().
th().$class("ui-state-default").__("Scheduler Type").__().
th().$class("ui-state-default").__("Scheduling Resource Type").__().
th().$class("ui-state-default").__("Minimum Allocation").__().
th().$class("ui-state-default").__("Maximum Allocation").__().
th().$class("ui-state-default").__("Maximum Cluster Application Priority").__().
th().$class("ui-state-default").__("Scheduler Busy %").__().
th().$class("ui-state-default").__("RM Dispatcher EventQueue Size").__().
th().$class("ui-state-default")
.__("Scheduler Dispatcher EventQueue Size").__().
__().
__().
tbody().$class("ui-widget-content");
boolean isEnabled = isYarnFederationEnabled();
// If Federation mode is not enabled or there is currently no SubCluster available,
// each column in the list should be displayed as N/A
if (!isEnabled) {
initLocalClusterOverViewTable(fsMetricsScheduleTr);
} else if (subclusters != null && !subclusters.isEmpty()) {
initSubClusterOverViewTable(metrics, fsMetricsScheduleTr, subclusters);
} else {
showRouterSchedulerMetricsData(UNAVAILABLE, fsMetricsScheduleTr);
}
fsMetricsScheduleTr.__().__();
}
/**
* We display Scheduler information for local cluster.
*
* @param fsMetricsScheduleTr MetricsScheduleTr.
*/
private void initLocalClusterOverViewTable(
Hamlet.TBODY<Hamlet.TABLE<Hamlet.DIV<Hamlet>>> fsMetricsScheduleTr) {
// configuration
Configuration config = this.router.getConfig();
Client client = RouterWebServiceUtil.createJerseyClient(config);
String webAppAddress = WebAppUtils.getRMWebAppURLWithScheme(config);
// Get the name of the local cluster.
String localClusterName = config.get(YarnConfiguration.RM_CLUSTER_ID, UNAVAILABLE);
SchedulerOverviewInfo schedulerOverviewInfo =
getSchedulerOverviewInfo(webAppAddress, config, client);
if (schedulerOverviewInfo != null) {
RouterSchedulerMetrics rsMetrics =
new RouterSchedulerMetrics(localClusterName, schedulerOverviewInfo);
// Basic information
showRouterSchedulerMetricsData(rsMetrics, fsMetricsScheduleTr);
} else {
showRouterSchedulerMetricsData(localClusterName, fsMetricsScheduleTr);
}
}
/**
* We display Scheduler information for multiple subClusters.
*
* @param metrics RouterClusterMetrics.
* @param fsMetricsScheduleTr MetricsScheduleTr.
* @param subClusters subCluster list.
*/
private void initSubClusterOverViewTable(RouterClusterMetrics metrics,
Hamlet.TBODY<Hamlet.TABLE<Hamlet.DIV<Hamlet>>> fsMetricsScheduleTr,
Collection<SubClusterInfo> subClusters) {
// configuration
Configuration config = this.router.getConfig();
Client client = RouterWebServiceUtil.createJerseyClient(config);
// Traverse all SubClusters to get cluster information.
for (SubClusterInfo subcluster : subClusters) {
// We need to make sure subCluster is not null
if (subcluster != null && subcluster.getSubClusterId() != null) {
// Call the RM interface to obtain schedule information
String webAppAddress = WebAppUtils.getHttpSchemePrefix(config) +
subcluster.getRMWebServiceAddress();
SchedulerOverviewInfo schedulerOverviewInfo =
getSchedulerOverviewInfo(webAppAddress, config, client);
// If schedulerOverviewInfo is not null,
// We will display information from rsMetrics, otherwise we will not display information.
if (schedulerOverviewInfo != null) {
RouterSchedulerMetrics rsMetrics =
new RouterSchedulerMetrics(subcluster, metrics, schedulerOverviewInfo);
// Basic information
showRouterSchedulerMetricsData(rsMetrics, fsMetricsScheduleTr);
}
}
}
client.close();
}
/**
* Get SchedulerOverview information based on webAppAddress.
*
* @param webAppAddress webAppAddress.
* @param config configuration.
* @param client jersey Client.
* @return SchedulerOverviewInfo.
*/
private SchedulerOverviewInfo getSchedulerOverviewInfo(String webAppAddress,
Configuration config, Client client) {
try {
SchedulerOverviewInfo schedulerOverviewInfo = RouterWebServiceUtil
.genericForward(webAppAddress, null, SchedulerOverviewInfo.class, HTTPMethods.GET,
RMWSConsts.RM_WEB_SERVICE_PATH + RMWSConsts.SCHEDULER_OVERVIEW, null, null,
config, client);
return schedulerOverviewInfo;
} catch (Exception e) {
LOG.error("get SchedulerOverviewInfo from webAppAddress = {} error.",
webAppAddress, e);
return null;
}
}
/**
* Show RouterSchedulerMetricsData.
*
* @param rsMetrics routerSchedulerMetrics.
* @param fsMetricsScheduleTr MetricsScheduleTr.
*/
private void showRouterSchedulerMetricsData(RouterSchedulerMetrics rsMetrics,
Hamlet.TBODY<Hamlet.TABLE<Hamlet.DIV<Hamlet>>> fsMetricsScheduleTr) {
// Basic information
fsMetricsScheduleTr.tr().
td(rsMetrics.getSubCluster()).
td(rsMetrics.getSchedulerType()).
td(rsMetrics.getSchedulingResourceType()).
td(rsMetrics.getMinimumAllocation()).
td(rsMetrics.getMaximumAllocation()).
td(rsMetrics.getApplicationPriority()).
td(rsMetrics.getSchedulerBusy()).
td(rsMetrics.getRmDispatcherEventQueueSize()).
td(rsMetrics.getSchedulerDispatcherEventQueueSize()).
__();
}
/**
* Show RouterSchedulerMetricsData.
*
* @param subClusterId subClusterId.
* @param fsMetricsScheduleTr MetricsScheduleTr.
*/
private void showRouterSchedulerMetricsData(String subClusterId,
Hamlet.TBODY<Hamlet.TABLE<Hamlet.DIV<Hamlet>>> fsMetricsScheduleTr) {
String subCluster = StringUtils.isNotBlank(subClusterId) ? subClusterId : UNAVAILABLE;
fsMetricsScheduleTr.tr().
td(subCluster).
td(UNAVAILABLE).
td(UNAVAILABLE).
td(UNAVAILABLE).
td(UNAVAILABLE).
td(UNAVAILABLE).
td(UNAVAILABLE).
td(UNAVAILABLE).
td(UNAVAILABLE)
.__();
}
}