1 /** 2 * Licensed to the Apache Software Foundation (ASF) under one 3 * or more contributor license agreements. See the NOTICE file 4 * distributed with this work for additional information 5 * regarding copyright ownership. The ASF licenses this file 6 * to you under the Apache License, Version 2.0 (the 7 * "License"); you may not use this file except in compliance 8 * with the License. You may obtain a copy of the License at 9 * 10 * http://www.apache.org/licenses/LICENSE-2.0 11 * 12 * Unless required by applicable law or agreed to in writing, software 13 * distributed under the License is distributed on an "AS IS" BASIS, 14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 * See the License for the specific language governing permissions and 16 * limitations under the License. 17 */ 18 19 package org.apache.hadoop.yarn.server.resourcemanager.webapp; 20 21 import static org.apache.hadoop.yarn.util.StringHelper.join; 22 23 import java.util.Collection; 24 25 import org.apache.hadoop.yarn.server.resourcemanager.ResourceManager; 26 import org.apache.hadoop.yarn.server.resourcemanager.scheduler.fair.FairScheduler; 27 import org.apache.hadoop.yarn.server.resourcemanager.webapp.dao.FairSchedulerInfo; 28 import org.apache.hadoop.yarn.server.resourcemanager.webapp.dao.FairSchedulerLeafQueueInfo; 29 import org.apache.hadoop.yarn.server.resourcemanager.webapp.dao.FairSchedulerQueueInfo; 30 import org.apache.hadoop.yarn.server.webapp.WebPageUtils; 31 import org.apache.hadoop.yarn.webapp.ResponseInfo; 32 import org.apache.hadoop.yarn.webapp.SubView; 33 import org.apache.hadoop.yarn.webapp.hamlet.Hamlet; 34 import org.apache.hadoop.yarn.webapp.hamlet.Hamlet.DIV; 35 import org.apache.hadoop.yarn.webapp.hamlet.Hamlet.LI; 36 import org.apache.hadoop.yarn.webapp.hamlet.Hamlet.UL; 37 import org.apache.hadoop.yarn.webapp.view.HtmlBlock; 38 import org.apache.hadoop.yarn.webapp.view.InfoBlock; 39 40 import com.google.inject.Inject; 41 import com.google.inject.servlet.RequestScoped; 42 43 public class FairSchedulerPage extends RmView { 44 static final String _Q = ".ui-state-default.ui-corner-all"; 45 static final float Q_MAX_WIDTH = 0.8f; 46 static final float Q_STATS_POS = Q_MAX_WIDTH + 0.05f; 47 static final String Q_END = "left:101%"; 48 static final String Q_GIVEN = "left:0%;background:none;border:1px solid rgba(0,0,0,1)"; 49 static final String Q_INSTANTANEOUS_FS = "left:0%;background:none;border:1px dashed rgba(0,0,0,1)"; 50 static final String Q_OVER = "background:rgba(255, 140, 0, 0.8)"; 51 static final String Q_UNDER = "background:rgba(50, 205, 50, 0.8)"; 52 static final String STEADY_FAIR_SHARE = "Steady Fair Share"; 53 static final String INSTANTANEOUS_FAIR_SHARE = "Instantaneous Fair Share"; 54 @RequestScoped 55 static class FSQInfo { 56 FairSchedulerQueueInfo qinfo; 57 } 58 59 static class LeafQueueBlock extends HtmlBlock { 60 final FairSchedulerLeafQueueInfo qinfo; 61 LeafQueueBlock(ViewContext ctx, FSQInfo info)62 @Inject LeafQueueBlock(ViewContext ctx, FSQInfo info) { 63 super(ctx); 64 qinfo = (FairSchedulerLeafQueueInfo)info.qinfo; 65 } 66 67 @Override render(Block html)68 protected void render(Block html) { 69 ResponseInfo ri = info("\'" + qinfo.getQueueName() + "\' Queue Status"). 70 _("Used Resources:", qinfo.getUsedResources().toString()). 71 _("Num Active Applications:", qinfo.getNumActiveApplications()). 72 _("Num Pending Applications:", qinfo.getNumPendingApplications()). 73 _("Min Resources:", qinfo.getMinResources().toString()). 74 _("Max Resources:", qinfo.getMaxResources().toString()); 75 int maxApps = qinfo.getMaxApplications(); 76 if (maxApps < Integer.MAX_VALUE) { 77 ri._("Max Running Applications:", qinfo.getMaxApplications()); 78 } 79 ri._(STEADY_FAIR_SHARE + ":", qinfo.getSteadyFairShare().toString()); 80 ri._(INSTANTANEOUS_FAIR_SHARE + ":", qinfo.getFairShare().toString()); 81 html._(InfoBlock.class); 82 83 // clear the info contents so this queue's info doesn't accumulate into another queue's info 84 ri.clear(); 85 } 86 } 87 88 static class QueueBlock extends HtmlBlock { 89 final FSQInfo fsqinfo; 90 QueueBlock(FSQInfo info)91 @Inject QueueBlock(FSQInfo info) { 92 fsqinfo = info; 93 } 94 95 @Override render(Block html)96 public void render(Block html) { 97 Collection<FairSchedulerQueueInfo> subQueues = fsqinfo.qinfo.getChildQueues(); 98 UL<Hamlet> ul = html.ul("#pq"); 99 for (FairSchedulerQueueInfo info : subQueues) { 100 float capacity = info.getMaxResourcesFraction(); 101 float steadyFairShare = info.getSteadyFairShareMemoryFraction(); 102 float instantaneousFairShare = info.getFairShareMemoryFraction(); 103 float used = info.getUsedMemoryFraction(); 104 LI<UL<Hamlet>> li = ul. 105 li(). 106 a(_Q).$style(width(capacity * Q_MAX_WIDTH)). 107 $title(join(join(STEADY_FAIR_SHARE + ":", percent(steadyFairShare)), 108 join(" " + INSTANTANEOUS_FAIR_SHARE + ":", percent(instantaneousFairShare)))). 109 span().$style(join(Q_GIVEN, ";font-size:1px;", width(steadyFairShare / capacity))). 110 _('.')._(). 111 span().$style(join(Q_INSTANTANEOUS_FS, ";font-size:1px;", 112 width(instantaneousFairShare/capacity))). 113 _('.')._(). 114 span().$style(join(width(used/capacity), 115 ";font-size:1px;left:0%;", used > instantaneousFairShare ? Q_OVER : Q_UNDER)). 116 _('.')._(). 117 span(".q", info.getQueueName())._(). 118 span().$class("qstats").$style(left(Q_STATS_POS)). 119 _(join(percent(used), " used"))._(); 120 121 fsqinfo.qinfo = info; 122 if (info instanceof FairSchedulerLeafQueueInfo) { 123 li.ul("#lq").li()._(LeafQueueBlock.class)._()._(); 124 } else { 125 li._(QueueBlock.class); 126 } 127 li._(); 128 } 129 130 ul._(); 131 } 132 } 133 134 static class QueuesBlock extends HtmlBlock { 135 final FairScheduler fs; 136 final FSQInfo fsqinfo; 137 QueuesBlock(ResourceManager rm, FSQInfo info)138 @Inject QueuesBlock(ResourceManager rm, FSQInfo info) { 139 fs = (FairScheduler)rm.getResourceScheduler(); 140 fsqinfo = info; 141 } 142 143 @Override render(Block html)144 public void render(Block html) { 145 html._(MetricsOverviewTable.class); 146 UL<DIV<DIV<Hamlet>>> ul = html. 147 div("#cs-wrapper.ui-widget"). 148 div(".ui-widget-header.ui-corner-top"). 149 _("Application Queues")._(). 150 div("#cs.ui-widget-content.ui-corner-bottom"). 151 ul(); 152 if (fs == null) { 153 ul. 154 li(). 155 a(_Q).$style(width(Q_MAX_WIDTH)). 156 span().$style(Q_END)._("100% ")._(). 157 span(".q", "default")._()._(); 158 } else { 159 FairSchedulerInfo sinfo = new FairSchedulerInfo(fs); 160 fsqinfo.qinfo = sinfo.getRootQueueInfo(); 161 float used = fsqinfo.qinfo.getUsedMemoryFraction(); 162 163 ul. 164 li().$style("margin-bottom: 1em"). 165 span().$style("font-weight: bold")._("Legend:")._(). 166 span().$class("qlegend ui-corner-all").$style(Q_GIVEN). 167 $title("The steady fair shares consider all queues, " + 168 "both active (with running applications) and inactive."). 169 _(STEADY_FAIR_SHARE)._(). 170 span().$class("qlegend ui-corner-all").$style(Q_INSTANTANEOUS_FS). 171 $title("The instantaneous fair shares consider only active " + 172 "queues (with running applications)."). 173 _(INSTANTANEOUS_FAIR_SHARE)._(). 174 span().$class("qlegend ui-corner-all").$style(Q_UNDER). 175 _("Used")._(). 176 span().$class("qlegend ui-corner-all").$style(Q_OVER). 177 _("Used (over fair share)")._(). 178 span().$class("qlegend ui-corner-all ui-state-default"). 179 _("Max Capacity")._(). 180 _(). 181 li(). 182 a(_Q).$style(width(Q_MAX_WIDTH)). 183 span().$style(join(width(used), ";left:0%;", 184 used > 1 ? Q_OVER : Q_UNDER))._(".")._(). 185 span(".q", "root")._(). 186 span().$class("qstats").$style(left(Q_STATS_POS)). 187 _(join(percent(used), " used"))._(). 188 _(QueueBlock.class)._(); 189 } 190 ul._()._(). 191 script().$type("text/javascript"). 192 _("$('#cs').hide();")._()._(). 193 _(FairSchedulerAppsBlock.class); 194 } 195 } 196 postHead(Page.HTML<_> html)197 @Override protected void postHead(Page.HTML<_> html) { 198 html. 199 style().$type("text/css"). 200 _("#cs { padding: 0.5em 0 1em 0; margin-bottom: 1em; position: relative }", 201 "#cs ul { list-style: none }", 202 "#cs a { font-weight: normal; margin: 2px; position: relative }", 203 "#cs a span { font-weight: normal; font-size: 80% }", 204 "#cs-wrapper .ui-widget-header { padding: 0.2em 0.5em }", 205 ".qstats { font-weight: normal; font-size: 80%; position: absolute }", 206 ".qlegend { font-weight: normal; padding: 0 1em; margin: 1em }", 207 "table.info tr th {width: 50%}")._(). // to center info table 208 script("/static/jt/jquery.jstree.js"). 209 script().$type("text/javascript"). 210 _("$(function() {", 211 " $('#cs a span').addClass('ui-corner-all').css('position', 'absolute');", 212 " $('#cs').bind('loaded.jstree', function (e, data) {", 213 " var callback = { call:reopenQueryNodes }", 214 " data.inst.open_node('#pq', callback);", 215 " }).", 216 " jstree({", 217 " core: { animation: 188, html_titles: true },", 218 " plugins: ['themeroller', 'html_data', 'ui'],", 219 " themeroller: { item_open: 'ui-icon-minus',", 220 " item_clsd: 'ui-icon-plus', item_leaf: 'ui-icon-gear'", 221 " }", 222 " });", 223 " $('#cs').bind('select_node.jstree', function(e, data) {", 224 " var queues = $('.q', data.rslt.obj);", 225 " var q = '^' + queues.first().text();", 226 " q += queues.length == 1 ? '$' : '\\\\.';", 227 " $('#apps').dataTable().fnFilter(q, 4, true);", 228 " });", 229 " $('#cs').show();", 230 "});")._(). 231 _(SchedulerPageUtil.QueueBlockUtil.class); 232 } 233 content()234 @Override protected Class<? extends SubView> content() { 235 return QueuesBlock.class; 236 } 237 238 @Override initAppsTable()239 protected String initAppsTable() { 240 return WebPageUtils.appsTableInit(true); 241 } 242 percent(float f)243 static String percent(float f) { 244 return String.format("%.1f%%", f * 100); 245 } 246 width(float f)247 static String width(float f) { 248 return String.format("width:%.1f%%", f * 100); 249 } 250 left(float f)251 static String left(float f) { 252 return String.format("left:%.1f%%", f * 100); 253 } 254 } 255