diff --git a/hadoop-common-project/hadoop-common/pom.xml b/hadoop-common-project/hadoop-common/pom.xml
index 0cd54799abb..cbd5f436e96 100644
--- a/hadoop-common-project/hadoop-common/pom.xml
+++ b/hadoop-common-project/hadoop-common/pom.xml
@@ -815,14 +815,14 @@
org.apache.hadoop.io.compress.zstd.ZStandardDecompressor
org.apache.hadoop.io.compress.lz4.Lz4Compressor
org.apache.hadoop.io.compress.lz4.Lz4Decompressor
- org.apache.hadoop.io.erasurecode.ErasureCodeNative
+
${project.build.directory}/native/javah
diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-router/src/main/java/org/apache/hadoop/yarn/server/router/Router.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-router/src/main/java/org/apache/hadoop/yarn/server/router/Router.java
index 121e5344fdb..4d9c4282ace 100644
--- a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-router/src/main/java/org/apache/hadoop/yarn/server/router/Router.java
+++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-router/src/main/java/org/apache/hadoop/yarn/server/router/Router.java
@@ -29,6 +29,7 @@
import org.apache.hadoop.yarn.YarnUncaughtExceptionHandler;
import org.apache.hadoop.yarn.conf.YarnConfiguration;
import org.apache.hadoop.yarn.exceptions.YarnRuntimeException;
+import org.apache.hadoop.yarn.server.federation.utils.FederationStateStoreFacade;
import org.apache.hadoop.yarn.server.resourcemanager.webapp.RMWebAppUtil;
import org.apache.hadoop.yarn.server.router.clientrm.RouterClientRMService;
import org.apache.hadoop.yarn.server.router.rmadmin.RouterRMAdminService;
@@ -142,6 +143,15 @@ public WebApp getWebapp() {
return this.webApp;
}
+ /**
+ * Get the federation facade to the State Store.
+ * @return Federation facade to the State Store.
+ */
+ @Private
+ public FederationStateStoreFacade getFederationFacade() {
+ return FederationStateStoreFacade.getInstance();
+ }
+
@VisibleForTesting
public void startWepApp() {
diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-router/src/main/java/org/apache/hadoop/yarn/server/router/webapp/AboutBlock.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-router/src/main/java/org/apache/hadoop/yarn/server/router/webapp/AboutBlock.java
new file mode 100644
index 00000000000..6f4c685cdf3
--- /dev/null
+++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-router/src/main/java/org/apache/hadoop/yarn/server/router/webapp/AboutBlock.java
@@ -0,0 +1,43 @@
+/**
+* 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.router.webapp;
+
+import org.apache.hadoop.yarn.server.router.Router;
+import org.apache.hadoop.yarn.webapp.view.HtmlBlock;
+import org.apache.hadoop.yarn.webapp.view.InfoBlock;
+
+import com.google.inject.Inject;
+
+/**
+ * About block for the Router Web UI.
+ */
+public class AboutBlock extends HtmlBlock {
+ final Router router;
+
+ @Inject
+ AboutBlock(Router router, ViewContext ctx) {
+ super(ctx);
+ this.router = router;
+ }
+
+ @Override
+ protected void render(Block html) {
+ html.__(InfoBlock.class);
+ }
+}
diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-router/src/main/java/org/apache/hadoop/yarn/server/router/webapp/AboutPage.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-router/src/main/java/org/apache/hadoop/yarn/server/router/webapp/AboutPage.java
new file mode 100644
index 00000000000..3c9f00dbfc6
--- /dev/null
+++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-router/src/main/java/org/apache/hadoop/yarn/server/router/webapp/AboutPage.java
@@ -0,0 +1,37 @@
+/**
+* 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.router.webapp;
+
+import org.apache.hadoop.yarn.webapp.SubView;
+
+/**
+ * About page for the Router Web UI.
+ */
+public class AboutPage extends RouterView {
+
+ @Override
+ protected void preHead(Page.HTML<__> html) {
+ commonPreHead(html);
+ }
+
+ @Override
+ protected Class extends SubView> content() {
+ return AboutBlock.class;
+ }
+}
diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-router/src/main/java/org/apache/hadoop/yarn/server/router/webapp/AppsBlock.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-router/src/main/java/org/apache/hadoop/yarn/server/router/webapp/AppsBlock.java
new file mode 100644
index 00000000000..eb1aef6be73
--- /dev/null
+++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-router/src/main/java/org/apache/hadoop/yarn/server/router/webapp/AppsBlock.java
@@ -0,0 +1,194 @@
+/**
+ * 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.router.webapp;
+
+import static org.apache.hadoop.yarn.util.StringHelper.join;
+import static org.apache.hadoop.yarn.webapp.view.JQueryUI.C_PROGRESSBAR;
+import static org.apache.hadoop.yarn.webapp.view.JQueryUI.C_PROGRESSBAR_VALUE;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+import java.util.TreeSet;
+
+import org.apache.commons.lang.StringEscapeUtils;
+import org.apache.hadoop.util.StringUtils;
+import org.apache.hadoop.yarn.exceptions.YarnException;
+import org.apache.hadoop.yarn.server.federation.store.records.SubClusterId;
+import org.apache.hadoop.yarn.server.federation.store.records.SubClusterInfo;
+import org.apache.hadoop.yarn.server.resourcemanager.webapp.dao.AppInfo;
+import org.apache.hadoop.yarn.server.resourcemanager.webapp.dao.AppsInfo;
+import org.apache.hadoop.yarn.server.router.Router;
+import org.apache.hadoop.yarn.webapp.hamlet2.Hamlet;
+import org.apache.hadoop.yarn.webapp.hamlet2.Hamlet.TABLE;
+import org.apache.hadoop.yarn.webapp.hamlet2.Hamlet.TBODY;
+import org.apache.hadoop.yarn.webapp.view.HtmlBlock;
+
+import com.google.inject.Inject;
+
+/**
+ * Applications block for the Router Web UI.
+ */
+public class AppsBlock extends HtmlBlock {
+ private final Router router;
+
+ /** Application id -> SubClusters. */
+ private Map> appsToSubcluster;
+ /** Applications in the system. */
+ private AppsInfo apps;
+
+ @Inject
+ AppsBlock(Router router, ViewContext ctx) {
+ super(ctx);
+ this.router = router;
+ }
+
+ @Override
+ protected void render(Block html) {
+ // Get the applications from the Resource Managers
+ initAppsInfo();
+
+ setTitle("Applications");
+
+ TBODY> tbody =
+ html.table("#apps").thead().tr().th(".id", "ID").th(".user", "User")
+ .th(".name", "Name").th(".type", "Application Type")
+ .th(".queue", "Queue").th(".reservationid", "ReservationId")
+ .th(".starttime", "StartTime").th(".finishtime", "FinishTime")
+ .th(".state", "State").th(".finalstatus", "FinalStatus")
+ .th(".progress", "Progress").th(".ui", "Tracking UI")
+ .th(".subcluster", "Subcluster").__().__().tbody();
+
+ // Render the applications
+ StringBuilder appsTableData = new StringBuilder("[\n");
+ for (AppInfo app : apps.getApps()) {
+ try {
+
+ String percent = String.format("%.1f", app.getProgress() * 100.0F);
+ String subclusters = "N/A";
+ if (appsToSubcluster.containsKey(app.getAppId())) {
+ subclusters =
+ StringUtils.join(",", appsToSubcluster.get(app.getAppId()));
+ }
+
+ // AppID numerical value parsed by parseHadoopID in yarn.dt.plugins.js
+ appsTableData.append("[\"").append(app.getAppId()).append("\",\"")
+ .append(StringEscapeUtils
+ .escapeJavaScript(StringEscapeUtils.escapeHtml(app.getUser())))
+ .append("\",\"")
+ .append(StringEscapeUtils
+ .escapeJavaScript(StringEscapeUtils.escapeHtml(app.getName())))
+ .append("\",\"")
+ .append(StringEscapeUtils.escapeJavaScript(
+ StringEscapeUtils.escapeHtml(app.getApplicationType())))
+ .append("\",\"")
+ .append(StringEscapeUtils
+ .escapeJavaScript(StringEscapeUtils.escapeHtml(app.getQueue())))
+ .append("\",\"").append("(best effort)") // TODO
+ .append("\",\"").append(app.getStartTime()).append("\",\"")
+ .append(app.getFinishTime()).append("\",\"").append(app.getState())
+ .append("\",\"").append(app.getFinalStatus()).append("\",\"")
+ // Progress bar
+ .append("
")
+ .append("\",\"").append("History")
+ .append("");
+ appsTableData.append("\",\"").append(subclusters);
+ appsTableData.append("\"],\n");
+
+ } catch (Exception e) {
+ LOG.info(
+ "Cannot add application " + app.getAppId() + ": " + e.getMessage());
+ }
+ }
+ if (appsTableData.charAt(appsTableData.length() - 2) == ',') {
+ appsTableData.delete(appsTableData.length() - 2,
+ appsTableData.length() - 1);
+ }
+ appsTableData.append("]");
+ html.script().$type("text/javascript")
+ .__("var appsTableData=" + appsTableData).__();
+
+ tbody.__().__();
+ }
+
+ /**
+ * Get the applications from the Resource Managers.
+ *
+ * @return Information of all the applications in the federation.
+ */
+ private void initAppsInfo() {
+ // Application to SubClusters
+ appsToSubcluster = new HashMap<>();
+
+ // Get the applications from the Resource Managers
+ apps = new AppsInfo();
+ try {
+ // Check each SubCluster
+ Map infos =
+ router.getFederationFacade().getSubClusters(true);
+ for (SubClusterInfo info : infos.values()) {
+ try {
+ // Get the applications for this SubCluster
+ String rmWebAddress = info.getRMWebServiceAddress();
+ AppsInfo rmApps = RouterWebServiceUtil.invokeRMWebService(
+ rmWebAddress, "apps", AppsInfo.class);
+ if (rmApps != null) {
+ for (AppInfo rmApp : rmApps.getApps()) {
+ apps.add(rmApp);
+ }
+
+ // Get link to the SubCluster
+ StringBuilder sb = new StringBuilder();
+ sb.append("");
+ sb.append(info.getSubClusterId());
+ sb.append("");
+ String subclusterLink = sb.toString();
+
+ // Generate mapping to SubCluster
+ for (AppInfo rmApp : rmApps.getApps()) {
+ String appId = rmApp.getAppId();
+ if (!appsToSubcluster.containsKey(appId)) {
+ appsToSubcluster.put(appId, new TreeSet<>());
+ }
+ appsToSubcluster.get(appId).add(subclusterLink);
+ }
+ }
+ } catch (Exception e) {
+ LOG.error("Cannot get the subcluster information for {}: {}",
+ info.getRMWebServiceAddress(), e.getMessage());
+ }
+ }
+ // Aggregate the applications per SubCluster
+ // TODO
+ //apps.mergeAppsInfo();
+ } catch (YarnException e) {
+ LOG.error("Cannot get the subcluster information", e);
+ }
+ }
+}
diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-router/src/main/java/org/apache/hadoop/yarn/server/router/webapp/AppsPage.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-router/src/main/java/org/apache/hadoop/yarn/server/router/webapp/AppsPage.java
new file mode 100644
index 00000000000..12d0b5b4ede
--- /dev/null
+++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-router/src/main/java/org/apache/hadoop/yarn/server/router/webapp/AppsPage.java
@@ -0,0 +1,77 @@
+/**
+ * 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.router.webapp;
+
+import static org.apache.hadoop.yarn.util.StringHelper.sjoin;
+import static org.apache.hadoop.yarn.webapp.YarnWebParams.APP_STATE;
+import static org.apache.hadoop.yarn.webapp.view.JQueryUI.DATATABLES;
+import static org.apache.hadoop.yarn.webapp.view.JQueryUI.DATATABLES_ID;
+import static org.apache.hadoop.yarn.webapp.view.JQueryUI.initID;
+import static org.apache.hadoop.yarn.webapp.view.JQueryUI.tableInit;
+
+import org.apache.hadoop.yarn.webapp.SubView;
+
+class AppsPage extends RouterView {
+
+ @Override
+ protected void preHead(Page.HTML<__> html) {
+ commonPreHead(html);
+ set(DATATABLES_ID, "apps");
+ set(initID(DATATABLES, "apps"), appsTableInit());
+ setTableStyles(html, "apps", ".queue {width:6em}", ".ui {width:8em}");
+
+ // Set the correct title.
+ String reqState = $(APP_STATE);
+ reqState = (reqState == null || reqState.isEmpty() ? "All" : reqState);
+ setTitle(sjoin(reqState, "Applications"));
+ }
+
+ private String appsTableInit() {
+ // id, user, name, queue, starttime, finishtime, state, status, progress, ui
+ return tableInit()
+ .append(", 'aaData': appsTableData")
+ .append(", bDeferRender: true")
+ .append(", bProcessing: true")
+
+ .append("\n, aoColumnDefs: ")
+ .append(getAppsTableColumnDefs())
+
+ // Sort by id upon page load
+ .append(", aaSorting: [[0, 'desc']]}").toString();
+ }
+
+ protected String getAppsTableColumnDefs() {
+ StringBuilder sb = new StringBuilder();
+ return sb
+ .append("[\n")
+ .append("{'sType':'string', 'aTargets': [0]")
+ .append(", 'mRender': parseHadoopID }")
+
+ .append("\n, {'sType':'numeric', 'aTargets': [6, 7]")
+ .append(", 'mRender': renderHadoopDate }")
+
+ .append("\n, {'sType':'numeric', bSearchable:false, 'aTargets': [10]")
+ .append(", 'mRender': parseHadoopProgress }]").toString();
+ }
+
+ @Override
+ protected Class extends SubView> content() {
+ return AppsBlock.class;
+ }
+}
\ No newline at end of file
diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-router/src/main/java/org/apache/hadoop/yarn/server/router/webapp/NavBlock.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-router/src/main/java/org/apache/hadoop/yarn/server/router/webapp/NavBlock.java
new file mode 100644
index 00000000000..c33170cbcbf
--- /dev/null
+++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-router/src/main/java/org/apache/hadoop/yarn/server/router/webapp/NavBlock.java
@@ -0,0 +1,46 @@
+/**
+* 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.router.webapp;
+
+import org.apache.hadoop.yarn.webapp.view.HtmlBlock;
+
+/**
+ * Navigation block for the Router Web UI.
+ */
+public class NavBlock extends HtmlBlock {
+
+ @Override
+ public void render(Block html) {
+ html.
+ div("#nav").
+ h3("Cluster").
+ ul().
+ li().a(url("cluster"), "About").__().
+ li().a(url(""), "Federation").__().
+ li().a(url("nodes"), "Nodes").__().
+ li().a(url("apps"), "Applications").__().
+ __().
+ h3("Tools").
+ ul().
+ li().a("/conf", "Configuration").__().
+ li().a("/logs", "Local logs").__().
+ li().a("/stacks", "Server stacks").__().
+ li().a("/jmx?qry=Hadoop:*", "Server metrics").__().__().__();
+ }
+}
diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-router/src/main/java/org/apache/hadoop/yarn/server/router/webapp/NodesBlock.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-router/src/main/java/org/apache/hadoop/yarn/server/router/webapp/NodesBlock.java
new file mode 100644
index 00000000000..4eb1004cb1a
--- /dev/null
+++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-router/src/main/java/org/apache/hadoop/yarn/server/router/webapp/NodesBlock.java
@@ -0,0 +1,148 @@
+/**
+ * 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.router.webapp;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.hadoop.util.StringUtils;
+import org.apache.hadoop.yarn.server.federation.store.records.SubClusterId;
+import org.apache.hadoop.yarn.server.federation.store.records.SubClusterInfo;
+import org.apache.hadoop.yarn.server.federation.utils.FederationStateStoreFacade;
+import org.apache.hadoop.yarn.server.resourcemanager.webapp.dao.NodeInfo;
+import org.apache.hadoop.yarn.server.resourcemanager.webapp.dao.NodesInfo;
+import org.apache.hadoop.yarn.server.router.Router;
+import org.apache.hadoop.yarn.util.Times;
+import org.apache.hadoop.yarn.webapp.hamlet2.Hamlet;
+import org.apache.hadoop.yarn.webapp.hamlet2.Hamlet.TABLE;
+import org.apache.hadoop.yarn.webapp.hamlet2.Hamlet.TBODY;
+import org.apache.hadoop.yarn.webapp.hamlet2.Hamlet.TR;
+import org.apache.hadoop.yarn.webapp.view.HtmlBlock;
+
+import com.google.inject.Inject;
+
+/**
+ * Nodes block for the Router Web UI.
+ */
+public class NodesBlock extends HtmlBlock {
+ private final Router router;
+ private static final long BYTES_IN_MB = 1024 * 1024;
+
+ private Map nodesToSubcluster;
+ private NodesInfo nodes;
+
+ @Inject
+ NodesBlock(Router router, ViewContext ctx) {
+ super(ctx);
+ this.router = router;
+ }
+
+ @Override
+ protected void render(Block html) {
+ // Get the node info from the federation
+ initNodesInfo();
+
+ setTitle("Nodes");
+
+ TBODY> tbody = html.table("#nodes").thead().tr()
+ .th(".subcluster", "Subcluster").th(".nodelabels", "Node Labels")
+ .th(".rack", "Rack").th(".state", "Node State")
+ .th(".nodeaddress", "Node Address")
+ .th(".nodehttpaddress", "Node HTTP Address")
+ .th(".lastHealthUpdate", "Last health-update")
+ .th(".healthReport", "Health-report").th(".containers", "Containers")
+ .th(".mem", "Mem Used").th(".mem", "Mem Avail")
+ .th(".vcores", "VCores Used").th(".vcores", "VCores Avail")
+ .th(".nodeManagerVersion", "Version").__().__().tbody();
+
+ // Add nodes to the web UI
+ for (NodeInfo info : nodes.getNodes()) {
+ String nodeId = info.getNodeId();
+ int usedMemory = (int) info.getUsedMemory();
+ int availableMemory = (int) info.getAvailableMemory();
+ TR>> row = tbody.tr();
+ if (nodesToSubcluster.containsKey(nodeId)) {
+ SubClusterInfo subcluster = nodesToSubcluster.get(info.getNodeId());
+ row.td().a("//" + subcluster.getRMWebServiceAddress(),
+ subcluster.getSubClusterId().toString()).__();
+ } else {
+ row.td("N/A");
+ }
+ row.td(StringUtils.join(",", info.getNodeLabels())).td(info.getRack())
+ .td(info.getState()).td(info.getNodeId());
+ // TODO
+ boolean isInactive = false;
+ if (isInactive) {
+ row.td().__("N/A").__();
+ } else {
+ String httpAddress = info.getNodeHTTPAddress();
+ row.td().a("//" + httpAddress, httpAddress).__();
+ }
+ row.td().br().$title(String.valueOf(info.getLastHealthUpdate())).__()
+ .__(Times.format(info.getLastHealthUpdate())).__()
+ .td(info.getHealthReport())
+ .td(String.valueOf(info.getNumContainers())).td().br()
+ .$title(String.valueOf(usedMemory)).__()
+ .__(StringUtils.byteDesc(usedMemory * BYTES_IN_MB)).__().td().br()
+ .$title(String.valueOf(availableMemory)).__()
+ .__(StringUtils.byteDesc(availableMemory * BYTES_IN_MB)).__()
+ .td(String.valueOf(info.getUsedVirtualCores()))
+ .td(String.valueOf(info.getAvailableVirtualCores()))
+ .td(info.getVersion()).__();
+ }
+ tbody.__().__();
+ }
+
+ /**
+ * Get the node information from the federation
+ */
+ private void initNodesInfo() {
+ // Node -> SubCluster
+ nodesToSubcluster = new HashMap();
+
+ // Get the nodes from the Resource Managers
+ nodes = new NodesInfo();
+ try {
+ FederationStateStoreFacade facade = router.getFederationFacade();
+ Map infos = facade.getSubClusters(true);
+ for (SubClusterInfo info : infos.values()) {
+ String webAddr = info.getRMWebServiceAddress();
+ try {
+ NodesInfo rmNodes = RouterWebServiceUtil.invokeRMWebService(
+ webAddr, "nodes", NodesInfo.class);
+ if (rmNodes != null) {
+ for (NodeInfo rmNode : rmNodes.getNodes()) {
+ nodes.add(rmNode);
+ }
+
+ // Assign the node to its SubCluster
+ for (NodeInfo rmNode : rmNodes.getNodes()) {
+ nodesToSubcluster.put(rmNode.getNodeId(), info);
+ }
+ }
+ } catch (Exception e) {
+ LOG.error("Cannot get the SubCluster information for {}: {}",
+ webAddr, e.getMessage());
+ }
+ }
+ } catch (Exception e) {
+ LOG.error("Cannot get the SubCluster information", e);
+ }
+ }
+}
diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-router/src/main/java/org/apache/hadoop/yarn/server/router/webapp/NodesPage.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-router/src/main/java/org/apache/hadoop/yarn/server/router/webapp/NodesPage.java
new file mode 100644
index 00000000000..0351cbac3a3
--- /dev/null
+++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-router/src/main/java/org/apache/hadoop/yarn/server/router/webapp/NodesPage.java
@@ -0,0 +1,60 @@
+/**
+ * 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.router.webapp;
+
+import static org.apache.hadoop.yarn.webapp.YarnWebParams.NODE_STATE;
+import static org.apache.hadoop.yarn.webapp.view.JQueryUI.DATATABLES;
+import static org.apache.hadoop.yarn.webapp.view.JQueryUI.DATATABLES_ID;
+import static org.apache.hadoop.yarn.webapp.view.JQueryUI.initID;
+import static org.apache.hadoop.yarn.webapp.view.JQueryUI.tableInit;
+
+import org.apache.hadoop.yarn.webapp.SubView;
+
+class NodesPage extends RouterView {
+
+ @Override
+ protected void preHead(Page.HTML<__> html) {
+ commonPreHead(html);
+ String type = $(NODE_STATE);
+ String title = "Nodes of the cluster";
+ if (type != null && !type.isEmpty()) {
+ title = title + " (" + type + ")";
+ }
+ setTitle(title);
+ set(DATATABLES_ID, "nodes");
+ set(initID(DATATABLES, "nodes"), nodesTableInit());
+ setTableStyles(html, "nodes", ".healthStatus {width:10em}",
+ ".healthReport {width:10em}");
+ }
+
+ @Override
+ protected Class extends SubView> content() {
+ return NodesBlock.class;
+ }
+
+ private String nodesTableInit() {
+ StringBuilder b = tableInit().append(", aoColumnDefs: [");
+ b.append("{'bSearchable': false, 'aTargets': [ 7 ]}");
+ b.append(", {'sType': 'title-numeric', 'bSearchable': false, "
+ + "'aTargets': [ 2, 3, 4, 5, 6 ] }");
+ b.append(", {'sType': 'title-numeric', 'aTargets': [ 5 ]}");
+ b.append("]}");
+ return b.toString();
+ }
+}
\ No newline at end of file
diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-router/src/main/java/org/apache/hadoop/yarn/server/router/webapp/ResourceManagersBlock.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-router/src/main/java/org/apache/hadoop/yarn/server/router/webapp/ResourceManagersBlock.java
new file mode 100644
index 00000000000..2aade7f82d1
--- /dev/null
+++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-router/src/main/java/org/apache/hadoop/yarn/server/router/webapp/ResourceManagersBlock.java
@@ -0,0 +1,111 @@
+/**
+ * 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.router.webapp;
+
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+import java.util.Map;
+
+import javax.xml.bind.JAXBException;
+
+import org.apache.hadoop.yarn.exceptions.YarnException;
+import org.apache.hadoop.yarn.server.federation.store.records.SubClusterId;
+import org.apache.hadoop.yarn.server.federation.store.records.SubClusterInfo;
+import org.apache.hadoop.yarn.server.federation.utils.FederationStateStoreFacade;
+import org.apache.hadoop.yarn.server.resourcemanager.webapp.dao.ClusterMetricsInfo;
+import org.apache.hadoop.yarn.server.router.Router;
+import org.apache.hadoop.yarn.server.router.webapp.dao.SubClusterState;
+import org.apache.hadoop.yarn.server.router.webapp.dao.SubClustersMetricsInfo;
+import org.apache.hadoop.yarn.webapp.hamlet2.Hamlet;
+import org.apache.hadoop.yarn.webapp.hamlet2.Hamlet.TABLE;
+import org.apache.hadoop.yarn.webapp.hamlet2.Hamlet.TBODY;
+import org.apache.hadoop.yarn.webapp.view.HtmlBlock;
+
+import com.google.inject.Inject;
+
+class ResourceManagersBlock extends HtmlBlock {
+ private Router router;
+
+ @Inject
+ ResourceManagersBlock(ViewContext ctx, Router router) {
+ super(ctx);
+ this.router = router;
+ }
+
+ @Override
+ public void render(Block html) {
+ setTitle("Federation");
+
+ // Table header
+ TBODY> tbody = html.table("#rms")
+ .thead()
+ .tr()
+ .th(".id", "Subcluster")
+ .th(".submitted", "Apps Submitted")
+ .th(".running", "Apps Running")
+ .th(".completed", "Apps Completed")
+ .th(".failed", "Apps Failed")
+ .th(".killed", "Apps Killed")
+ .th(".containers", "Containers Running")
+ .th(".nodes", "Active Nodes")
+ .th(".maintenance", "Maintenance Nodes")
+ .__().__().tbody();
+
+ try {
+ FederationStateStoreFacade facade = router.getFederationFacade();
+ Map subClustersInfo =
+ facade.getSubClusters(true);
+
+ SubClustersMetricsInfo fsi = new SubClustersMetricsInfo(subClustersInfo);
+ List subclusters = fsi.getSubClusterMetrics();
+ Comparator super SubClusterState> cmp =
+ new Comparator() {
+ @Override
+ public int compare(SubClusterState o1, SubClusterState o2) {
+ return o1.getSubClusterId().compareTo(o2.getSubClusterId());
+ }
+ };
+ Collections.sort(subclusters, cmp);
+
+ for (SubClusterState subClusterState : subclusters) {
+ String webAppAddress = subClusterState.getWebAppAddress();
+ ClusterMetricsInfo scm = subClusterState.getClusterMetrics();
+
+ // Building row per subcluster
+ tbody.tr().td()
+ .a("//" + webAppAddress, subClusterState.getSubClusterId()).__()
+ .td(Integer.toString(scm.getAppsSubmitted()))
+ .td(Integer.toString(scm.getAppsRunning()))
+ .td(Integer.toString(scm.getAppsCompleted()))
+ .td(Integer.toString(scm.getAppsFailed()))
+ .td(Integer.toString(scm.getAppsKilled()))
+ .td(Integer.toString(scm.getContainersAllocated()))
+ .td(Integer.toString(scm.getActiveNodes()))
+ .__();
+ }
+ } catch (YarnException e) {
+ LOG.error("Cannot render resource managers", e);
+ } catch (JAXBException e) {
+ LOG.error("Cannot parse subcluster info", e);
+ }
+
+ tbody.__().__();
+ }
+}
diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-router/src/main/java/org/apache/hadoop/yarn/server/router/webapp/ResourceManagersPage.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-router/src/main/java/org/apache/hadoop/yarn/server/router/webapp/ResourceManagersPage.java
new file mode 100644
index 00000000000..65ad7463164
--- /dev/null
+++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-router/src/main/java/org/apache/hadoop/yarn/server/router/webapp/ResourceManagersPage.java
@@ -0,0 +1,57 @@
+/**
+* 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.router.webapp;
+
+import static org.apache.hadoop.yarn.webapp.view.JQueryUI.DATATABLES;
+import static org.apache.hadoop.yarn.webapp.view.JQueryUI.DATATABLES_ID;
+import static org.apache.hadoop.yarn.webapp.view.JQueryUI.initID;
+import static org.apache.hadoop.yarn.webapp.view.JQueryUI.tableInit;
+
+import org.apache.hadoop.yarn.webapp.SubView;
+
+/**
+ * Renders a block for the applications with metrics information.
+ */
+class ResourceManagersPage extends RouterView {
+
+ @Override
+ protected void preHead(Page.HTML<__> html) {
+ commonPreHead(html);
+ setTitle("Federation");
+ set(DATATABLES_ID, "rms");
+ set(initID(DATATABLES, "rms"), rmsTableInit());
+ setTableStyles(html, "rms", ".healthStatus {width:10em}",
+ ".healthReport {width:10em}");
+ }
+
+ @Override
+ protected Class extends SubView> content() {
+ return ResourceManagersBlock.class;
+ }
+
+ private String rmsTableInit() {
+ StringBuilder b = tableInit().append(", aoColumnDefs: [");
+ b.append("{'bSearchable': false, 'aTargets': [ 7 ]}");
+ b.append(", {'sType': 'title-numeric', 'bSearchable': false, "
+ + "'aTargets': [ 8, 9 ] }");
+ b.append(", {'sType': 'title-numeric', 'aTargets': [ 5 ]}");
+ b.append("]}");
+ return b.toString();
+ }
+}
diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-router/src/main/java/org/apache/hadoop/yarn/server/router/webapp/RouterController.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-router/src/main/java/org/apache/hadoop/yarn/server/router/webapp/RouterController.java
new file mode 100644
index 00000000000..819f0a23ffb
--- /dev/null
+++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-router/src/main/java/org/apache/hadoop/yarn/server/router/webapp/RouterController.java
@@ -0,0 +1,60 @@
+/**
+* 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.router.webapp;
+
+import org.apache.hadoop.yarn.webapp.Controller;
+
+import com.google.inject.Inject;
+
+/**
+ * Controller for the Router Web UI.
+ */
+public class RouterController extends Controller {
+
+ @Inject
+ RouterController(RequestContext ctx) {
+ super(ctx);
+ }
+
+ @Override
+ public void index() {
+ setTitle("Router");
+ render(ResourceManagersPage.class);
+ }
+
+ public void about() {
+ setTitle("About the Cluster");
+ render(AboutPage.class);
+ }
+
+ public void federation() {
+ setTitle("Federation");
+ render(ResourceManagersPage.class);
+ }
+
+ public void apps() {
+ setTitle("Applications");
+ render(AppsPage.class);
+ }
+
+ public void nodes() {
+ setTitle("Nodes");
+ render(NodesPage.class);
+ }
+}
diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-router/src/main/java/org/apache/hadoop/yarn/server/router/webapp/RouterView.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-router/src/main/java/org/apache/hadoop/yarn/server/router/webapp/RouterView.java
new file mode 100644
index 00000000000..b377f7dc1dd
--- /dev/null
+++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-router/src/main/java/org/apache/hadoop/yarn/server/router/webapp/RouterView.java
@@ -0,0 +1,52 @@
+/**
+* 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.router.webapp;
+
+import org.apache.hadoop.yarn.webapp.SubView;
+import org.apache.hadoop.yarn.webapp.view.TwoColumnLayout;
+
+import static org.apache.hadoop.yarn.webapp.view.JQueryUI.*;
+
+/**
+ * View for the Router Web UI.
+ */
+public class RouterView extends TwoColumnLayout {
+
+ @Override
+ protected void preHead(Page.HTML<__> html) {
+ commonPreHead(html);
+
+ setTitle("Router");
+ }
+
+ protected void commonPreHead(Page.HTML<__> html) {
+ set(ACCORDION_ID, "nav");
+ set(initID(ACCORDION, "nav"), "{autoHeight:false, active:0}");
+ }
+
+ @Override
+ protected Class extends SubView> nav() {
+ return NavBlock.class;
+ }
+
+ @Override
+ protected Class extends SubView> content() {
+ return AboutBlock.class;
+ }
+}
diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-router/src/main/java/org/apache/hadoop/yarn/server/router/webapp/RouterWebServiceUtil.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-router/src/main/java/org/apache/hadoop/yarn/server/router/webapp/RouterWebServiceUtil.java
index 18618eeac1b..0f5e03857ec 100644
--- a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-router/src/main/java/org/apache/hadoop/yarn/server/router/webapp/RouterWebServiceUtil.java
+++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-router/src/main/java/org/apache/hadoop/yarn/server/router/webapp/RouterWebServiceUtil.java
@@ -37,6 +37,7 @@
import org.apache.hadoop.yarn.webapp.BadRequestException;
import org.apache.hadoop.yarn.webapp.ForbiddenException;
import org.apache.hadoop.yarn.webapp.NotFoundException;
+import org.apache.hadoop.yarn.webapp.util.WebAppUtils;
import com.sun.jersey.api.ConflictException;
import com.sun.jersey.api.client.Client;
@@ -224,4 +225,49 @@ public static void retrieveException(ClientResponse response) {
}
+ /**
+ * Performs an invocation of the the remote RMWebService. It does not throw in
+ * case of any communication failure.
+ *
+ * @param webApp Address of the web resource.
+ * @param path Path of the web resource.
+ * @param returnType Class of the return.
+ * @return Result of the invocation.
+ */
+ public static T invokeRMWebServiceNoThrow(
+ String webApp, String path, final Class returnType) {
+ try {
+ return invokeRMWebService(webApp, path, returnType);
+ } catch (Exception e) {
+ LOG.error("Cannot invoke RM web request", e);
+ return null;
+ }
+ }
+
+ /**
+ * Performs an invocation to the remote RM Web Service.
+ *
+ * @param webApp Address of the web resource.
+ * @param path Path of the web resource.
+ * @param returnType Class of the return.
+ * @return Result of the invocation.
+ */
+ public static T invokeRMWebService(
+ String webAddr, String path, final Class returnType) {
+ Client client = Client.create();
+ T obj = null;
+
+ // It is needed for the new HTTP_POLICY because we enable 2 listeners
+ WebResource webResource =
+ client.resource(WebAppUtils.HTTP_PREFIX + webAddr);
+ ClientResponse response = webResource
+ .path("ws/v1/cluster")
+ .path(path)
+ .accept(MediaType.APPLICATION_XML)
+ .get(ClientResponse.class);
+ if (response.getStatus() == Response.Status.OK.getStatusCode()) {
+ obj = response.getEntity(returnType);
+ }
+ return obj;
+ }
}
\ No newline at end of file
diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-router/src/main/java/org/apache/hadoop/yarn/server/router/webapp/dao/SubClusterMetricsInfo.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-router/src/main/java/org/apache/hadoop/yarn/server/router/webapp/dao/SubClusterMetricsInfo.java
new file mode 100644
index 00000000000..66c261a7877
--- /dev/null
+++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-router/src/main/java/org/apache/hadoop/yarn/server/router/webapp/dao/SubClusterMetricsInfo.java
@@ -0,0 +1,46 @@
+/**
+ * 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.router.webapp.dao;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.xml.bind.annotation.XmlAccessType;
+import javax.xml.bind.annotation.XmlAccessorType;
+import javax.xml.bind.annotation.XmlRootElement;
+
+import org.apache.hadoop.yarn.server.resourcemanager.webapp.dao.ClusterMetricsInfo;
+
+/**
+ * Metrics for each subcluster for the Router Web UI.
+ */
+@XmlRootElement(name = "federationSubClusterMetrics")
+@XmlAccessorType(XmlAccessType.FIELD)
+public class SubClusterMetricsInfo {
+
+ protected List subClusterMetrics =
+ new ArrayList<>();
+
+ public SubClusterMetricsInfo() {
+ // JAXB needs this
+ }
+
+ public void add(ClusterMetricsInfo info) {
+ subClusterMetrics.add(info);
+ }
+}
\ No newline at end of file
diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-router/src/main/java/org/apache/hadoop/yarn/server/router/webapp/dao/SubClusterState.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-router/src/main/java/org/apache/hadoop/yarn/server/router/webapp/dao/SubClusterState.java
new file mode 100644
index 00000000000..32cb9d9c7e0
--- /dev/null
+++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-router/src/main/java/org/apache/hadoop/yarn/server/router/webapp/dao/SubClusterState.java
@@ -0,0 +1,87 @@
+/**
+ * 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.router.webapp.dao;
+
+import java.io.StringReader;
+
+import javax.xml.bind.JAXBException;
+import javax.xml.bind.annotation.XmlAccessType;
+import javax.xml.bind.annotation.XmlAccessorType;
+import javax.xml.bind.annotation.XmlRootElement;
+
+import org.apache.hadoop.yarn.server.federation.store.records.SubClusterId;
+import org.apache.hadoop.yarn.server.federation.store.records.SubClusterInfo;
+import org.apache.hadoop.yarn.server.resourcemanager.webapp.dao.ClusterMetricsInfo;
+
+import com.sun.jersey.api.json.JSONConfiguration;
+import com.sun.jersey.api.json.JSONJAXBContext;
+import com.sun.jersey.api.json.JSONUnmarshaller;
+
+/**
+ * State for each cluster for the Router Web UI.
+ */
+@XmlRootElement(name = "subClusterState")
+@XmlAccessorType(XmlAccessType.FIELD)
+public class SubClusterState {
+
+ private SubClusterId subClusterId;
+ private String clientRMAddress;
+ private String state;
+ private ClusterMetricsInfo clusterMetrics;
+ private String webAppAddress;
+
+ public SubClusterState() {
+ // JAXB needs this
+ }
+
+ public SubClusterState(SubClusterInfo subClusterInfo) throws JAXBException {
+ this.subClusterId = subClusterInfo.getSubClusterId();
+ this.clientRMAddress = subClusterInfo.getClientRMServiceAddress();
+ this.state = subClusterInfo.getState().toString();
+ this.webAppAddress = subClusterInfo.getRMWebServiceAddress();
+
+ // Parse metrics
+ JSONJAXBContext jc = new JSONJAXBContext(
+ JSONConfiguration.mapped().rootUnwrapping(false).build(),
+ ClusterMetricsInfo.class);
+ JSONUnmarshaller unmarshaller = jc.createJSONUnmarshaller();
+ this.clusterMetrics = unmarshaller.unmarshalFromJSON(
+ new StringReader(subClusterInfo.getCapability()),
+ ClusterMetricsInfo.class);
+ }
+
+ public String getSubClusterId() {
+ return subClusterId.toString();
+ }
+
+ public String getClientRMAddress() {
+ return clientRMAddress;
+ }
+
+ public String getSubClusterState() {
+ return state;
+ }
+
+ public ClusterMetricsInfo getClusterMetrics() {
+ return clusterMetrics;
+ }
+
+ public String getWebAppAddress() {
+ return webAppAddress;
+ }
+}
diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-router/src/main/java/org/apache/hadoop/yarn/server/router/webapp/dao/SubClustersMetricsInfo.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-router/src/main/java/org/apache/hadoop/yarn/server/router/webapp/dao/SubClustersMetricsInfo.java
new file mode 100644
index 00000000000..bb8e4d99799
--- /dev/null
+++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-router/src/main/java/org/apache/hadoop/yarn/server/router/webapp/dao/SubClustersMetricsInfo.java
@@ -0,0 +1,62 @@
+/**
+ * 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.router.webapp.dao;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+import javax.xml.bind.JAXBException;
+import javax.xml.bind.annotation.XmlAccessType;
+import javax.xml.bind.annotation.XmlAccessorType;
+import javax.xml.bind.annotation.XmlRootElement;
+
+import org.apache.hadoop.yarn.server.federation.store.records.SubClusterId;
+import org.apache.hadoop.yarn.server.federation.store.records.SubClusterInfo;
+
+/**
+ * Metrics of multiple subclusters for the Router Web UI.
+ */
+@XmlRootElement(name = "subClustersMetricsInfo")
+@XmlAccessorType(XmlAccessType.FIELD)
+public class SubClustersMetricsInfo {
+
+ protected List subClusterMetrics;
+
+ public SubClustersMetricsInfo() {
+ // JAXB needs this
+ }
+
+ public SubClustersMetricsInfo(List subClusterMetrics) {
+ this.subClusterMetrics = subClusterMetrics;
+ }
+
+ public SubClustersMetricsInfo(
+ Map federationState) throws JAXBException {
+
+ subClusterMetrics = new ArrayList<>();
+ for (SubClusterInfo subClusterInfo : federationState.values()) {
+ subClusterMetrics.add(new SubClusterState(subClusterInfo));
+ }
+ }
+
+ public List getSubClusterMetrics() {
+ return subClusterMetrics;
+ }
+
+}