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.ArrayList; 24 25 import org.apache.hadoop.util.StringUtils; 26 import org.apache.hadoop.yarn.server.resourcemanager.ResourceManager; 27 import org.apache.hadoop.yarn.server.resourcemanager.scheduler.capacity.CSQueue; 28 import org.apache.hadoop.yarn.server.resourcemanager.scheduler.capacity.CapacityScheduler; 29 import org.apache.hadoop.yarn.server.resourcemanager.scheduler.capacity.UserInfo; 30 import org.apache.hadoop.yarn.server.resourcemanager.webapp.dao.CapacitySchedulerInfo; 31 import org.apache.hadoop.yarn.server.resourcemanager.webapp.dao.CapacitySchedulerLeafQueueInfo; 32 import org.apache.hadoop.yarn.server.resourcemanager.webapp.dao.CapacitySchedulerQueueInfo; 33 import org.apache.hadoop.yarn.server.resourcemanager.webapp.dao.ResourceInfo; 34 import org.apache.hadoop.yarn.server.webapp.AppsBlock; 35 import org.apache.hadoop.yarn.webapp.ResponseInfo; 36 import org.apache.hadoop.yarn.webapp.SubView; 37 import org.apache.hadoop.yarn.webapp.hamlet.Hamlet; 38 import org.apache.hadoop.yarn.webapp.hamlet.Hamlet.DIV; 39 import org.apache.hadoop.yarn.webapp.hamlet.Hamlet.LI; 40 import org.apache.hadoop.yarn.webapp.hamlet.Hamlet.TABLE; 41 import org.apache.hadoop.yarn.webapp.hamlet.Hamlet.TBODY; 42 import org.apache.hadoop.yarn.webapp.hamlet.Hamlet.UL; 43 import org.apache.hadoop.yarn.webapp.view.HtmlBlock; 44 import org.apache.hadoop.yarn.webapp.view.InfoBlock; 45 46 import com.google.inject.Inject; 47 import com.google.inject.servlet.RequestScoped; 48 49 class CapacitySchedulerPage extends RmView { 50 static final String _Q = ".ui-state-default.ui-corner-all"; 51 static final float Q_MAX_WIDTH = 0.8f; 52 static final float Q_STATS_POS = Q_MAX_WIDTH + 0.05f; 53 static final String Q_END = "left:101%"; 54 static final String Q_GIVEN = "left:0%;background:none;border:1px dashed rgba(0,0,0,0.25)"; 55 static final String Q_OVER = "background:rgba(255, 140, 0, 0.8)"; 56 static final String Q_UNDER = "background:rgba(50, 205, 50, 0.8)"; 57 58 @RequestScoped 59 static class CSQInfo { 60 CapacitySchedulerInfo csinfo; 61 CapacitySchedulerQueueInfo qinfo; 62 } 63 64 static class LeafQueueInfoBlock extends HtmlBlock { 65 final CapacitySchedulerLeafQueueInfo lqinfo; 66 LeafQueueInfoBlock(ViewContext ctx, CSQInfo info)67 @Inject LeafQueueInfoBlock(ViewContext ctx, CSQInfo info) { 68 super(ctx); 69 lqinfo = (CapacitySchedulerLeafQueueInfo) info.qinfo; 70 } 71 72 @Override render(Block html)73 protected void render(Block html) { 74 75 ResponseInfo ri = info("\'" + lqinfo.getQueuePath().substring(5) + "\' Queue Status"). 76 _("Queue State:", lqinfo.getQueueState()). 77 _("Used Capacity:", percent(lqinfo.getUsedCapacity() / 100)). 78 _("Absolute Used Capacity:", percent(lqinfo.getAbsoluteUsedCapacity() / 100)). 79 _("Absolute Capacity:", percent(lqinfo.getAbsoluteCapacity() / 100)). 80 _("Absolute Max Capacity:", percent(lqinfo.getAbsoluteMaxCapacity() / 100)). 81 _("Used Resources:", lqinfo.getResourcesUsed().toString()). 82 _("Num Schedulable Applications:", Integer.toString(lqinfo.getNumActiveApplications())). 83 _("Num Non-Schedulable Applications:", Integer.toString(lqinfo.getNumPendingApplications())). 84 _("Num Containers:", Integer.toString(lqinfo.getNumContainers())). 85 _("Max Applications:", Integer.toString(lqinfo.getMaxApplications())). 86 _("Max Applications Per User:", Integer.toString(lqinfo.getMaxApplicationsPerUser())). 87 _("Max Application Master Resources:", lqinfo.getAMResourceLimit().toString()). 88 _("Used Application Master Resources:", lqinfo.getUsedAMResource().toString()). 89 _("Max Application Master Resources Per User:", lqinfo.getUserAMResourceLimit().toString()). 90 _("Configured Capacity:", percent(lqinfo.getCapacity() / 100)). 91 _("Configured Max Capacity:", percent(lqinfo.getMaxCapacity() / 100)). 92 _("Configured Minimum User Limit Percent:", Integer.toString(lqinfo.getUserLimit()) + "%"). 93 _("Configured User Limit Factor:", String.format("%.1f", lqinfo.getUserLimitFactor())). 94 _("Accessible Node Labels:", StringUtils.join(",", lqinfo.getNodeLabels())). 95 _("Preemption:", lqinfo.getPreemptionDisabled() ? "disabled" : "enabled"); 96 97 html._(InfoBlock.class); 98 99 // clear the info contents so this queue's info doesn't accumulate into another queue's info 100 ri.clear(); 101 } 102 } 103 104 static class QueueUsersInfoBlock extends HtmlBlock { 105 final CapacitySchedulerLeafQueueInfo lqinfo; 106 107 @Inject QueueUsersInfoBlock(ViewContext ctx, CSQInfo info)108 QueueUsersInfoBlock(ViewContext ctx, CSQInfo info) { 109 super(ctx); 110 lqinfo = (CapacitySchedulerLeafQueueInfo) info.qinfo; 111 } 112 113 @Override render(Block html)114 protected void render(Block html) { 115 TBODY<TABLE<Hamlet>> tbody = 116 html.table("#userinfo").thead().$class("ui-widget-header").tr().th() 117 .$class("ui-state-default")._("User Name")._().th() 118 .$class("ui-state-default")._("Max Resource")._().th() 119 .$class("ui-state-default")._("Used Resource")._().th() 120 .$class("ui-state-default")._("Max AM Resource")._().th() 121 .$class("ui-state-default")._("Used AM Resource")._().th() 122 .$class("ui-state-default")._("Schedulable Apps")._().th() 123 .$class("ui-state-default")._("Non-Schedulable Apps")._()._()._() 124 .tbody(); 125 126 ArrayList<UserInfo> users = lqinfo.getUsers().getUsersList(); 127 for (UserInfo userInfo : users) { 128 tbody.tr().td(userInfo.getUsername()) 129 .td(userInfo.getUserResourceLimit().toString()) 130 .td(userInfo.getResourcesUsed().toString()) 131 .td(lqinfo.getUserAMResourceLimit().toString()) 132 .td(userInfo.getAMResourcesUsed().toString()) 133 .td(Integer.toString(userInfo.getNumActiveApplications())) 134 .td(Integer.toString(userInfo.getNumPendingApplications()))._(); 135 } 136 137 html.div().$class("usersinfo").h5("Active Users Info")._(); 138 tbody._()._(); 139 } 140 } 141 142 public static class QueueBlock extends HtmlBlock { 143 final CSQInfo csqinfo; 144 QueueBlock(CSQInfo info)145 @Inject QueueBlock(CSQInfo info) { 146 csqinfo = info; 147 } 148 149 @Override render(Block html)150 public void render(Block html) { 151 ArrayList<CapacitySchedulerQueueInfo> subQueues = 152 (csqinfo.qinfo == null) ? csqinfo.csinfo.getQueues().getQueueInfoList() 153 : csqinfo.qinfo.getQueues().getQueueInfoList(); 154 UL<Hamlet> ul = html.ul("#pq"); 155 for (CapacitySchedulerQueueInfo info : subQueues) { 156 float used = info.getUsedCapacity() / 100; 157 float absCap = info.getAbsoluteCapacity() / 100; 158 float absMaxCap = info.getAbsoluteMaxCapacity() / 100; 159 float absUsedCap = info.getAbsoluteUsedCapacity() / 100; 160 LI<UL<Hamlet>> li = ul. 161 li(). 162 a(_Q).$style(width(absMaxCap * Q_MAX_WIDTH)). 163 $title(join("Absolute Capacity:", percent(absCap))). 164 span().$style(join(Q_GIVEN, ";font-size:1px;", width(absCap/absMaxCap))). 165 _('.')._(). 166 span().$style(join(width(absUsedCap/absMaxCap), 167 ";font-size:1px;left:0%;", absUsedCap > absCap ? Q_OVER : Q_UNDER)). 168 _('.')._(). 169 span(".q", info.getQueuePath().substring(5))._(). 170 span().$class("qstats").$style(left(Q_STATS_POS)). 171 _(join(percent(used), " used"))._(); 172 173 csqinfo.qinfo = info; 174 if (info.getQueues() == null) { 175 li.ul("#lq").li()._(LeafQueueInfoBlock.class)._()._(); 176 li.ul("#lq").li()._(QueueUsersInfoBlock.class)._()._(); 177 } else { 178 li._(QueueBlock.class); 179 } 180 li._(); 181 } 182 183 ul._(); 184 } 185 } 186 187 static class QueuesBlock extends HtmlBlock { 188 final CapacityScheduler cs; 189 final CSQInfo csqinfo; 190 QueuesBlock(ResourceManager rm, CSQInfo info)191 @Inject QueuesBlock(ResourceManager rm, CSQInfo info) { 192 cs = (CapacityScheduler) rm.getResourceScheduler(); 193 csqinfo = info; 194 } 195 196 @Override render(Block html)197 public void render(Block html) { 198 html._(MetricsOverviewTable.class); 199 UL<DIV<DIV<Hamlet>>> ul = html. 200 div("#cs-wrapper.ui-widget"). 201 div(".ui-widget-header.ui-corner-top"). 202 _("Application Queues")._(). 203 div("#cs.ui-widget-content.ui-corner-bottom"). 204 ul(); 205 if (cs == null) { 206 ul. 207 li(). 208 a(_Q).$style(width(Q_MAX_WIDTH)). 209 span().$style(Q_END)._("100% ")._(). 210 span(".q", "default")._()._(); 211 } else { 212 CSQueue root = cs.getRootQueue(); 213 CapacitySchedulerInfo sinfo = new CapacitySchedulerInfo(root); 214 csqinfo.csinfo = sinfo; 215 csqinfo.qinfo = null; 216 217 float used = sinfo.getUsedCapacity() / 100; 218 ul. 219 li().$style("margin-bottom: 1em"). 220 span().$style("font-weight: bold")._("Legend:")._(). 221 span().$class("qlegend ui-corner-all").$style(Q_GIVEN). 222 _("Capacity")._(). 223 span().$class("qlegend ui-corner-all").$style(Q_UNDER). 224 _("Used")._(). 225 span().$class("qlegend ui-corner-all").$style(Q_OVER). 226 _("Used (over capacity)")._(). 227 span().$class("qlegend ui-corner-all ui-state-default"). 228 _("Max Capacity")._(). 229 _(). 230 li(). 231 a(_Q).$style(width(Q_MAX_WIDTH)). 232 span().$style(join(width(used), ";left:0%;", 233 used > 1 ? Q_OVER : Q_UNDER))._(".")._(). 234 span(".q", "root")._(). 235 span().$class("qstats").$style(left(Q_STATS_POS)). 236 _(join(percent(used), " used"))._(). 237 _(QueueBlock.class)._(); 238 } 239 ul._()._(). 240 script().$type("text/javascript"). 241 _("$('#cs').hide();")._()._(). 242 _(RMAppsBlock.class); 243 } 244 } 245 postHead(Page.HTML<_> html)246 @Override protected void postHead(Page.HTML<_> html) { 247 html. 248 style().$type("text/css"). 249 _("#cs { padding: 0.5em 0 1em 0; margin-bottom: 1em; position: relative }", 250 "#cs ul { list-style: none }", 251 "#cs a { font-weight: normal; margin: 2px; position: relative }", 252 "#cs a span { font-weight: normal; font-size: 80% }", 253 "#cs-wrapper .ui-widget-header { padding: 0.2em 0.5em }", 254 ".qstats { font-weight: normal; font-size: 80%; position: absolute }", 255 ".qlegend { font-weight: normal; padding: 0 1em; margin: 1em }", 256 "table.info tr th {width: 50%}")._(). // to center info table 257 script("/static/jt/jquery.jstree.js"). 258 script().$type("text/javascript"). 259 _("$(function() {", 260 " $('#cs a span').addClass('ui-corner-all').css('position', 'absolute');", 261 " $('#cs').bind('loaded.jstree', function (e, data) {", 262 " var callback = { call:reopenQueryNodes }", 263 " data.inst.open_node('#pq', callback);", 264 " }).", 265 " jstree({", 266 " core: { animation: 188, html_titles: true },", 267 " plugins: ['themeroller', 'html_data', 'ui'],", 268 " themeroller: { item_open: 'ui-icon-minus',", 269 " item_clsd: 'ui-icon-plus', item_leaf: 'ui-icon-gear'", 270 " }", 271 " });", 272 " $('#cs').bind('select_node.jstree', function(e, data) {", 273 " var q = $('.q', data.rslt.obj).first().text();", 274 " if (q == 'root') q = '';", 275 " else q = '^' + q.substr(q.lastIndexOf('.') + 1) + '$';", 276 " $('#apps').dataTable().fnFilter(q, 4, true);", 277 " });", 278 " $('#cs').show();", 279 "});")._(). 280 _(SchedulerPageUtil.QueueBlockUtil.class); 281 } 282 content()283 @Override protected Class<? extends SubView> content() { 284 return QueuesBlock.class; 285 } 286 percent(float f)287 static String percent(float f) { 288 return String.format("%.1f%%", f * 100); 289 } 290 width(float f)291 static String width(float f) { 292 return String.format("width:%.1f%%", f * 100); 293 } 294 left(float f)295 static String left(float f) { 296 return String.format("left:%.1f%%", f * 100); 297 } 298 } 299