", totalNodes, activeNodes);
+
+ // Prepare HTML Table
+ tbody.tr().$id(subClusterIdText)
+ .td().$class("details-control").a(herfWebAppAddress, subClusterIdText).__()
+ .td(subcluster.getState().name())
+ .td(lastStartTime)
+ .td(lastHeartBeat)
+ .td(resources)
+ .td(nodes)
+ .__();
+
+ subclusterMap.put("subcluster", subClusterId.getId());
+ subclusterMap.put("capability", capability);
+ lists.add(subclusterMap);
+ }
+ } catch (Exception e) {
+ LOG.error("Cannot render Router Federation.", e);
+ }
+
+ // Init FederationBlockTableJs
+ initFederationSubClusterDetailTableJs(html, lists);
+
+ // Tips
+ tbody.__().__().div().p().$style("color:red")
+ .__("*The application counts are local per subcluster").__().__();
}
}
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/FederationPage.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-router/src/main/java/org/apache/hadoop/yarn/server/router/webapp/FederationPage.java
index 02ec1a2402..1a1ae40012 100644
--- a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-router/src/main/java/org/apache/hadoop/yarn/server/router/webapp/FederationPage.java
+++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-router/src/main/java/org/apache/hadoop/yarn/server/router/webapp/FederationPage.java
@@ -33,8 +33,9 @@ class FederationPage extends RouterView {
@Override
protected void preHead(Page.HTML<__> html) {
commonPreHead(html);
- setTitle("Federation");
+ setTitle("About The YARN Federation");
set(DATATABLES_ID, "rms");
+ set("ui.div.id", "div_id");
set(initID(DATATABLES, "rms"), rmsTableInit());
setTableStyles(html, "rms", ".healthStatus {width:10em}",
".healthReport {width:10em}");
@@ -46,12 +47,14 @@ class FederationPage extends RouterView {
}
private String rmsTableInit() {
- StringBuilder b = tableInit().append(", aoColumnDefs: [");
- b.append("{'bSearchable': false, 'aTargets': [ 7 ]}")
- .append(", {'sType': 'title-numeric', 'bSearchable': false, "
- + "'aTargets': [ 8, 9 ] }")
- .append(", {'sType': 'title-numeric', 'aTargets': [ 5 ]}")
+ StringBuilder builder = tableInit().append(", aoColumnDefs: [");
+ builder
+ .append("{'sName':'State', 'sType':'string', 'bSearchable':false, 'aTargets':[1]},")
+ .append("{'sName':'LastStartTime', 'sType':'string', 'bSearchable':false, 'aTargets':[2]},")
+ .append("{'sName':'lastHeartBeat', 'sType':'string', 'bSearchable':false, 'aTargets':[3]},")
+ .append("{'sName':'resource', 'sType':'string', 'bSearchable':false, 'aTargets':[4]},")
+ .append("{'sName':'nodes', 'sType':'string', 'bSearchable':false, 'aTargets':[5]}")
.append("]}");
- return b.toString();
+ return builder.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
index e8cd0de665..38df0e7886 100644
--- 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
@@ -34,7 +34,7 @@ public class RouterController extends Controller {
@Override
public void index() {
- setTitle("Router");
+ setTitle("About the YARN Router");
render(AboutPage.class);
}
@@ -44,7 +44,6 @@ public class RouterController extends Controller {
}
public void federation() {
- setTitle("Federation");
render(FederationPage.class);
}
diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-router/src/test/java/org/apache/hadoop/yarn/server/router/webapp/MockRouter.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-router/src/test/java/org/apache/hadoop/yarn/server/router/webapp/MockRouter.java
new file mode 100644
index 0000000000..e3790108c0
--- /dev/null
+++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-router/src/test/java/org/apache/hadoop/yarn/server/router/webapp/MockRouter.java
@@ -0,0 +1,99 @@
+/**
+ * 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.commons.io.FileUtils;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.hadoop.conf.Configuration;
+import org.apache.hadoop.util.Time;
+import org.apache.hadoop.yarn.conf.YarnConfiguration;
+import org.apache.hadoop.yarn.exceptions.YarnException;
+import org.apache.hadoop.yarn.server.federation.store.FederationStateStore;
+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.store.records.SubClusterRegisterRequest;
+import org.apache.hadoop.yarn.server.federation.store.records.SubClusterState;
+import org.apache.hadoop.yarn.server.federation.utils.FederationStateStoreFacade;
+import org.apache.hadoop.yarn.server.router.Router;
+
+import java.io.File;
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+public class MockRouter extends Router {
+
+ private FederationStateStoreFacade facade;
+
+ public MockRouter(Configuration configuration)
+ throws InterruptedException, YarnException, IOException {
+ this.setConfig(configuration);
+
+ boolean isEnabled = configuration.getBoolean(
+ YarnConfiguration.FEDERATION_ENABLED,
+ YarnConfiguration.DEFAULT_FEDERATION_ENABLED);
+
+ if (isEnabled) {
+ facade = FederationStateStoreFacade.getInstance();
+ initTestFederationSubCluster();
+ }
+ }
+
+ public void initTestFederationSubCluster()
+ throws IOException, InterruptedException, YarnException {
+
+ // Initialize subcluster information
+ String scAmRMAddress = "5.6.7.8:5";
+ String scClientRMAddress = "5.6.7.8:6";
+ String scRmAdminAddress = "5.6.7.8:7";
+ String scWebAppAddress = "127.0.0.1:8080";
+
+ // Initialize subcluster capability
+ String[] capabilityPathItems = new String[] {".", "target", "test-classes", "capability"};
+ String capabilityPath = StringUtils.join(capabilityPathItems, File.separator);
+ String capabilityJson =
+ FileUtils.readFileToString(new File(capabilityPath), StandardCharsets.UTF_8);
+
+ // capability json needs to remove asflicense
+ String regex = "\"___asflicense__.*\\n(.*,\\n){1,15}.*\\n.*";
+ Pattern p = Pattern.compile(regex);
+ Matcher m = p.matcher(capabilityJson);
+ capabilityJson = m.replaceAll("").trim();
+
+ // Initialize subcluster sc1
+ SubClusterInfo sc1 =
+ SubClusterInfo.newInstance(SubClusterId.newInstance("SC-1"),
+ scAmRMAddress, scClientRMAddress, scRmAdminAddress, scWebAppAddress,
+ SubClusterState.SC_RUNNING, Time.now(), capabilityJson);
+ Thread.sleep(5000);
+ sc1.setLastHeartBeat(Time.now());
+
+ // Initialize subcluster sc2
+ SubClusterInfo sc2 =
+ SubClusterInfo.newInstance(SubClusterId.newInstance("SC-2"),
+ scAmRMAddress, scClientRMAddress, scRmAdminAddress, scWebAppAddress,
+ SubClusterState.SC_RUNNING, Time.now(), capabilityJson);
+ Thread.sleep(5000);
+ sc2.setLastHeartBeat(Time.now());
+
+ FederationStateStore stateStore = facade.getStateStore();
+ stateStore.registerSubCluster(SubClusterRegisterRequest.newInstance(sc1));
+ stateStore.registerSubCluster(SubClusterRegisterRequest.newInstance(sc2));
+ }
+}
diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-router/src/test/java/org/apache/hadoop/yarn/server/router/webapp/TestFederationWebApp.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-router/src/test/java/org/apache/hadoop/yarn/server/router/webapp/TestFederationWebApp.java
new file mode 100644
index 0000000000..e0ea4ccdb3
--- /dev/null
+++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-router/src/test/java/org/apache/hadoop/yarn/server/router/webapp/TestFederationWebApp.java
@@ -0,0 +1,48 @@
+/**
+ * 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.conf.Configuration;
+import org.apache.hadoop.yarn.conf.YarnConfiguration;
+import org.apache.hadoop.yarn.exceptions.YarnException;
+import org.apache.hadoop.yarn.server.router.Router;
+import org.apache.hadoop.yarn.webapp.test.WebAppTests;
+import org.junit.Test;
+
+import java.io.IOException;
+
+public class TestFederationWebApp {
+
+ @Test
+ public void testFederationWebViewNotEnable()
+ throws InterruptedException, YarnException, IOException {
+ // Test Federation is not Enabled
+ Configuration config = new YarnConfiguration();
+ config.setBoolean(YarnConfiguration.FEDERATION_ENABLED, false);
+ WebAppTests.testPage(FederationPage.class, Router.class, new MockRouter(config));
+ }
+
+ @Test
+ public void testFederationWebViewEnable()
+ throws InterruptedException, YarnException, IOException {
+ // Test Federation Enabled
+ Configuration config = new YarnConfiguration();
+ config.setBoolean(YarnConfiguration.FEDERATION_ENABLED, true);
+ WebAppTests.testPage(FederationPage.class, Router.class, new MockRouter(config));
+ }
+}
diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-router/src/test/resources/capability b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-router/src/test/resources/capability
new file mode 100644
index 0000000000..e6cf4a1cd0
--- /dev/null
+++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-router/src/test/resources/capability
@@ -0,0 +1,21 @@
+{
+ "___asflicense__": [
+ "",
+ "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."
+ ],
+ "clusterMetrics":{"appsSubmitted":"0","appsCompleted":"0","appsPending":"0","appsRunning":"0","appsFailed":"0","appsKilled":"0","reservedMB":"0","availableMB":"4096","allocatedMB":"0","pendingMB":"0","reservedVirtualCores":"0","availableVirtualCores":"4","allocatedVirtualCores":"0","pendingVirtualCores":"0","containersAllocated":"0","containersReserved":"0","containersPending":"0","totalMB":"4096","totalVirtualCores":"4","utilizedMBPercent":"0","utilizedVirtualCoresPercent":"0","rmSchedulerBusyPercent":"-1","totalNodes":"1","lostNodes":"0","unhealthyNodes":"0","decommissioningNodes":"0","decommissionedNodes":"0","rebootedNodes":"0","activeNodes":"1","shutdownNodes":"0","containerAssignedPerSecond":"0","totalUsedResourcesAcrossPartition":{"memory":"0","vCores":"0","resourceInformations":{"resourceInformation":[{"attributes":null,"maximumAllocation":"9223372036854775807","minimumAllocation":"0","name":"memory-mb","resourceType":"COUNTABLE","units":"Mi","value":"0"},{"attributes":null,"maximumAllocation":"9223372036854775807","minimumAllocation":"0","name":"vcores","resourceType":"COUNTABLE","units":"","value":"0"}]}},"totalClusterResourcesAcrossPartition":{"memory":"4096","vCores":"4","resourceInformations":{"resourceInformation":[{"attributes":null,"maximumAllocation":"9223372036854775807","minimumAllocation":"0","name":"memory-mb","resourceType":"COUNTABLE","units":"Mi","value":"4096"},{"attributes":null,"maximumAllocation":"9223372036854775807","minimumAllocation":"0","name":"vcores","resourceType":"COUNTABLE","units":"","value":"4"}]}},"totalReservedResourcesAcrossPartition":{"memory":"0","vCores":"0","resourceInformations":{"resourceInformation":[{"attributes":null,"maximumAllocation":"9223372036854775807","minimumAllocation":"0","name":"memory-mb","resourceType":"COUNTABLE","units":"Mi","value":"0"},{"attributes":null,"maximumAllocation":"9223372036854775807","minimumAllocation":"0","name":"vcores","resourceType":"COUNTABLE","units":"","value":"0"}]}},"totalAllocatedContainersAcrossPartition":"0","crossPartitionMetricsAvailable":"true","rmEventQueueSize":"0","schedulerEventQueueSize":"0"}
+}
\ No newline at end of file