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