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