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..723cbd68174 --- /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,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 org.apache.hadoop.conf.Configuration; +import org.apache.hadoop.yarn.conf.YarnConfiguration; +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.router.Router; +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; + +/** + * About block for the Router Web UI. + */ +public class AboutBlock extends HtmlBlock { + private final Router router; + + @Inject + AboutBlock(Router router, ViewContext ctx) { + super(ctx); + this.router = router; + } + + @Override + protected void render(Block html) { + Configuration conf = this.router.getConfig(); + String webAppAddress = WebAppUtils.getRouterWebAppURLWithScheme(conf); + ClusterMetricsInfo metrics = RouterWebServiceUtil.genericForward( + webAppAddress, null, ClusterMetricsInfo.class, HTTPMethods.GET, + RMWSConsts.RM_WEB_SERVICE_PATH + RMWSConsts.METRICS, null, null); + boolean isEnabled = conf.getBoolean( + YarnConfiguration.FEDERATION_ENABLED, + YarnConfiguration.DEFAULT_FEDERATION_ENABLED); + info("Federation status"). + __("Enabled", isEnabled). + __("Applications submitted", metrics.getAppsSubmitted()). + __("Applications pending", metrics.getAppsPending()). + __("Applications running", metrics.getAppsRunning()). + __("Applications failed", metrics.getAppsFailed()). + __("Applications killed", metrics.getAppsKilled()). + __("Applications completed", metrics.getAppsCompleted()). + __("Containers allocated", metrics.getContainersAllocated()). + __("Virtual codes available", metrics.getAvailableVirtualCores()). + __("Virtual codes allocated", metrics.getAllocatedVirtualCores()). + __("Virtual codes total", metrics.getTotalVirtualCores()). + __("Memory available", metrics.getAvailableMB()). + __("Memory allocated", metrics.getAllocatedMB()). + __("Memory total", metrics.getTotalMB()). + __("Nodes active", metrics.getActiveNodes()). + __("Nodes lost", metrics.getLostNodes()). + __("Nodes decommissioned", metrics.getDecommissionedNodes()). + __("Nodes decommissioning", metrics.getDecommissioningNodes()). + __("Nodes total", metrics.getTotalNodes()); + + 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 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..ddad460361a --- /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,132 @@ +/** + * 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.Map; +import java.util.Set; + +import org.apache.commons.lang.StringEscapeUtils; +import org.apache.hadoop.conf.Configuration; +import org.apache.hadoop.util.StringUtils; +import org.apache.hadoop.yarn.server.resourcemanager.webapp.RMWSConsts; +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.util.WebAppUtils; +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; + + @Inject + AppsBlock(Router router, ViewContext ctx) { + super(ctx); + this.router = router; + } + + @Override + protected void render(Block html) { + // Get the applications from the Resource Managers + Configuration conf = this.router.getConfig(); + String webAppAddress = WebAppUtils.getRouterWebAppURLWithScheme(conf); + AppsInfo apps = RouterWebServiceUtil.genericForward(webAppAddress, null, + AppsInfo.class, HTTPMethods.GET, + RMWSConsts.RM_WEB_SERVICE_PATH + RMWSConsts.APPS, null, null); + + 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(".starttime", "StartTime") + .th(".finishtime", "FinishTime") + .th(".state", "State") + .th(".finalstatus", "FinalStatus") + .th(".progress", "Progress") + .th(".ui", "Tracking UI") + .__().__().tbody(); + + // Render the applications + StringBuilder appsTableData = new StringBuilder("[\n"); + for (AppInfo app : apps.getApps()) { + try { + + String percent = String.format("%.1f", app.getProgress() * 100.0F); + // 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(app.getStartTime()).append("\",\"") + .append(app.getFinishTime()).append("\",\"").append(app.getState()) + .append("\",\"").append(app.getFinalStatus()).append("\",\"") + // Progress bar + .append("
").append("
") + .append("\",\"").append("History") + .append(""); + 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.__().__(); + } +} 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 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/FederationBlock.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-router/src/main/java/org/apache/hadoop/yarn/server/router/webapp/FederationBlock.java new file mode 100644 index 00000000000..e3171c4a607 --- /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/FederationBlock.java @@ -0,0 +1,129 @@ +/** + * 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.io.StringReader; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.List; +import java.util.Map; + +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.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; +import com.sun.jersey.api.json.JSONConfiguration; +import com.sun.jersey.api.json.JSONJAXBContext; +import com.sun.jersey.api.json.JSONUnmarshaller; + +class FederationBlock extends HtmlBlock { + + @Inject + FederationBlock(ViewContext ctx, Router router) { + super(ctx); + } + + @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 { + // Binding to the State Store + FederationStateStoreFacade facade = + FederationStateStoreFacade.getInstance(); + Map subClustersInfo = + facade.getSubClusters(true); + + // Sort the subclusters + List subclusters = new ArrayList<>(); + subclusters.addAll(subClustersInfo.values()); + Comparator cmp = + new Comparator() { + @Override + public int compare(SubClusterInfo o1, SubClusterInfo o2) { + return o1.getSubClusterId().compareTo(o2.getSubClusterId()); + } + }; + Collections.sort(subclusters, cmp); + + for (SubClusterInfo subcluster : subclusters) { + SubClusterId subClusterId = subcluster.getSubClusterId(); + String webAppAddress = subcluster.getClientRMServiceAddress(); + String capability = subcluster.getCapability(); + ClusterMetricsInfo subClusterInfo = getClusterMetricsInfo(capability); + + // Building row per subcluster + tbody.tr().td() + .a("//" + webAppAddress, subClusterId.toString()).__() + .td(Integer.toString(subClusterInfo.getAppsSubmitted())) + .td(Integer.toString(subClusterInfo.getAppsRunning())) + .td(Integer.toString(subClusterInfo.getAppsCompleted())) + .td(Integer.toString(subClusterInfo.getAppsFailed())) + .td(Integer.toString(subClusterInfo.getAppsKilled())) + .td(Integer.toString(subClusterInfo.getContainersAllocated())) + .td(Integer.toString(subClusterInfo.getActiveNodes())) + .__(); + } + } catch (YarnException e) { + LOG.error("Cannot render resource managers", e); + } + + tbody.__().__(); + } + + private static ClusterMetricsInfo getClusterMetricsInfo(String capability) { + ClusterMetricsInfo clusterMetrics = null; + try { + JSONJAXBContext jc = new JSONJAXBContext( + JSONConfiguration.mapped().rootUnwrapping(false).build(), + ClusterMetricsInfo.class); + JSONUnmarshaller unmarshaller = jc.createJSONUnmarshaller(); + clusterMetrics = unmarshaller.unmarshalFromJSON( + new StringReader(capability), + ClusterMetricsInfo.class); + } catch (Exception e) { + LOG.error("Cannot parse subcluster info", e); + } + return clusterMetrics; + } +} 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 new file mode 100644 index 00000000000..ddd2866e3b1 --- /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/FederationPage.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 FederationPage 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 content() { + return FederationBlock.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/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..bc569f70bbf --- /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,124 @@ +/** + * 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.Map; + +import org.apache.hadoop.conf.Configuration; +import org.apache.hadoop.util.StringUtils; +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.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.util.WebAppUtils; +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 static final long BYTES_IN_MB = 1024 * 1024; + + private final Router router; + + private Map nodesToSubcluster; + + @Inject + NodesBlock(Router router, ViewContext ctx) { + super(ctx); + this.router = router; + } + + @Override + protected void render(Block html) { + // Get the node info from the federation + Configuration conf = this.router.getConfig(); + String webAppAddress = WebAppUtils.getRouterWebAppURLWithScheme(conf); + NodesInfo nodes = RouterWebServiceUtil.genericForward( + webAppAddress, null, NodesInfo.class, HTTPMethods.GET, + RMWSConsts.RM_WEB_SERVICE_PATH + RMWSConsts.NODES, + null, null); + + 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.__().__(); + } +} 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 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/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..bf377d46427 --- /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(FederationPage.class); + } + + public void about() { + setTitle("About the Cluster"); + render(AboutPage.class); + } + + public void federation() { + setTitle("Federation"); + render(FederationPage.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 nav() { + return NavBlock.class; + } + + @Override + protected Class content() { + return AboutBlock.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/TestAppsPage.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-router/src/test/java/org/apache/hadoop/yarn/server/router/webapp/TestAppsPage.java new file mode 100644 index 00000000000..655f1c5f146 --- /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/TestAppsPage.java @@ -0,0 +1,36 @@ +/** + * 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.junit.Before; +import org.junit.Test; + +/** + * Test the Router applications page. + */ +public class TestAppsPage { + + @Before + public void setup() { + } + + @Test + public void test() { + } +} 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/TestNodesPage.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-router/src/test/java/org/apache/hadoop/yarn/server/router/webapp/TestNodesPage.java new file mode 100644 index 00000000000..53ddd1bb2e8 --- /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/TestNodesPage.java @@ -0,0 +1,36 @@ +/** + * 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.junit.Before; +import org.junit.Test; + +/** + * Test the Router nodes page. + */ +public class TestNodesPage { + + @Before + public void setup() { + } + + @Test + public void test() { + } +} 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/TestResourceManagersPage.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-router/src/test/java/org/apache/hadoop/yarn/server/router/webapp/TestResourceManagersPage.java new file mode 100644 index 00000000000..50f97219b98 --- /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/TestResourceManagersPage.java @@ -0,0 +1,36 @@ +/** + * 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.junit.Before; +import org.junit.Test; + +/** + * Test the Router federation page. + */ +public class TestResourceManagersPage { + + @Before + public void setup() { + } + + @Test + public void test() { + } +}