Преглед на файлове

YARN-5321. [YARN-3368] Add resource usage for application by node managers (Wangda Tan via Sunil G)
YARN-5320. [YARN-3368] Add resource usage by applications and queues to cluster overview page (Wangda Tan via Sunil G)
YARN-5322. [YARN-3368] Add a node heat chart map (Wangda Tan via Sunil G)
YARN-5347. [YARN-3368] Applications page improvements (Sreenath Somarajapuram via Sunil G)
YARN-5348. [YARN-3368] Node details page improvements (Sreenath Somarajapuram via Sunil G)
YARN-5346. [YARN-3368] Queues page improvements (Sreenath Somarajapuram via Sunil G)
YARN-5345. [YARN-3368] Cluster overview page improvements (Sreenath Somarajapuram via Sunil G)
YARN-5344. [YARN-3368] Generic UI improvements (Sreenath Somarajapuram via Sunil G)

Sunil преди 8 години
родител
ревизия
7190ddf64a
променени са 95 файла, в които са добавени 3399 реда и са изтрити 482 реда
  1. 14 0
      hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/app/adapters/yarn-app.js
  2. 67 0
      hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/app/components/app-usage-donut-chart.js
  3. 5 0
      hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/app/components/bar-chart.js
  4. 36 19
      hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/app/components/base-chart-component.js
  5. 43 0
      hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/app/components/base-usage-donut-chart.js
  6. 41 14
      hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/app/components/donut-chart.js
  7. 209 0
      hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/app/components/nodes-heatmap.js
  8. 88 0
      hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/app/components/per-app-memusage-by-nodes-stacked-barchart.js
  9. 67 0
      hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/app/components/per-app-ncontainers-by-nodes-stacked-barchart.js
  10. 69 0
      hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/app/components/queue-usage-donut-chart.js
  11. 1 2
      hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/app/components/queue-view.js
  12. 7 2
      hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/app/components/simple-table.js
  13. 198 0
      hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/app/components/stacked-barchart.js
  14. 1 1
      hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/app/components/timeline-view.js
  15. 33 10
      hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/app/components/tree-selector.js
  16. 9 0
      hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/app/controllers/cluster-overview.js
  17. 40 0
      hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/app/controllers/yarn-app-attempt.js
  18. 40 0
      hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/app/controllers/yarn-app-attempts.js
  19. 38 0
      hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/app/controllers/yarn-app.js
  20. 9 0
      hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/app/controllers/yarn-apps.js
  21. 39 0
      hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/app/controllers/yarn-node-apps.js
  22. 39 0
      hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/app/controllers/yarn-node-containers.js
  23. 37 0
      hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/app/controllers/yarn-node.js
  24. 36 0
      hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/app/controllers/yarn-nodes-heatmap.js
  25. 33 0
      hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/app/controllers/yarn-nodes.js
  26. 46 0
      hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/app/controllers/yarn-queue-apps.js
  27. 20 0
      hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/app/controllers/yarn-queue.js
  28. 34 0
      hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/app/controllers/yarn-queues.js
  29. 34 0
      hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/app/controllers/yarn-services.js
  30. 1 1
      hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/app/models/cluster-metric.js
  31. 11 0
      hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/app/models/yarn-app-attempt.js
  32. 4 0
      hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/app/models/yarn-app.js
  33. 7 0
      hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/app/models/yarn-rm-node.js
  34. 13 2
      hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/app/router.js
  35. 2 0
      hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/app/routes/application.js
  36. 8 1
      hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/app/routes/cluster-overview.js
  37. 30 0
      hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/app/routes/yarn-app-attempts.js
  38. 16 1
      hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/app/routes/yarn-app.js
  39. 4 2
      hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/app/routes/yarn-apps.js
  40. 22 0
      hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/app/routes/yarn-apps/apps.js
  41. 22 0
      hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/app/routes/yarn-apps/services.js
  42. 1 0
      hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/app/routes/yarn-node.js
  43. 4 1
      hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/app/routes/yarn-nodes.js
  44. 22 0
      hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/app/routes/yarn-nodes/heatmap.js
  45. 22 0
      hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/app/routes/yarn-nodes/table.js
  46. 36 0
      hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/app/routes/yarn-queue-apps.js
  47. 38 0
      hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/app/routes/yarn-queues.js
  48. 12 7
      hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/app/serializers/yarn-app-attempt.js
  49. 6 2
      hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/app/serializers/yarn-app.js
  50. 13 7
      hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/app/serializers/yarn-container.js
  51. 119 20
      hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/app/styles/app.css
  52. 61 38
      hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/app/templates/application.hbs
  53. 123 45
      hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/app/templates/cluster-overview.hbs
  54. 8 2
      hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/app/templates/components/app-table.hbs
  55. 1 1
      hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/app/templates/components/node-menu-panel.hbs
  56. 27 0
      hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/app/templates/components/nodes-heatmap.hbs
  57. 0 4
      hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/app/templates/components/queue-configuration-table.hbs
  58. 3 11
      hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/app/templates/components/queue-navigator.hbs
  59. 2 1
      hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/app/templates/components/timeline-view.hbs
  60. 12 1
      hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/app/templates/yarn-app-attempt.hbs
  61. 57 0
      hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/app/templates/yarn-app-attempts.hbs
  62. 218 128
      hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/app/templates/yarn-app.hbs
  63. 75 7
      hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/app/templates/yarn-apps.hbs
  64. 24 0
      hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/app/templates/yarn-apps/apps.hbs
  65. 27 0
      hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/app/templates/yarn-apps/services.hbs
  66. 4 0
      hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/app/templates/yarn-node-apps.hbs
  67. 4 0
      hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/app/templates/yarn-node-container.hbs
  68. 4 0
      hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/app/templates/yarn-node-containers.hbs
  69. 87 61
      hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/app/templates/yarn-node.hbs
  70. 54 45
      hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/app/templates/yarn-nodes.hbs
  71. 30 0
      hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/app/templates/yarn-nodes/heatmap.hbs
  72. 67 0
      hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/app/templates/yarn-nodes/table.hbs
  73. 66 0
      hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/app/templates/yarn-queue-apps.hbs
  74. 83 43
      hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/app/templates/yarn-queue.hbs
  75. 72 0
      hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/app/templates/yarn-queues.hbs
  76. 67 0
      hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/app/utils/color-utils.js
  77. 17 0
      hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/app/utils/converter.js
  78. 29 0
      hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/app/utils/href-address-utils.js
  79. 36 0
      hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/app/utils/mock.js
  80. 2 1
      hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/bower.json
  81. 4 0
      hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/package.json
  82. 30 0
      hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/tests/unit/controllers/yarn-app-attempt-test.js
  83. 30 0
      hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/tests/unit/controllers/yarn-app-attempts-test.js
  84. 30 0
      hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/tests/unit/controllers/yarn-app-test.js
  85. 30 0
      hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/tests/unit/controllers/yarn-node-apps-test.js
  86. 30 0
      hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/tests/unit/controllers/yarn-node-containers-test.js
  87. 30 0
      hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/tests/unit/controllers/yarn-node-test.js
  88. 30 0
      hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/tests/unit/controllers/yarn-nodes-heatmap-test.js
  89. 30 0
      hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/tests/unit/controllers/yarn-nodes-test.js
  90. 30 0
      hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/tests/unit/controllers/yarn-queue-apps-test.js
  91. 2 2
      hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/tests/unit/controllers/yarn-queues-test.js
  92. 30 0
      hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/tests/unit/controllers/yarn-services-test.js
  93. 29 0
      hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/tests/unit/routes/yarn-app-attempts-test.js
  94. 29 0
      hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/tests/unit/routes/yarn-queue-apps-test.js
  95. 29 0
      hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/tests/unit/routes/yarn-queues-test.js

+ 14 - 0
hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/app/adapters/yarn-app.js

@@ -23,6 +23,20 @@ export default AbstractAdapter.extend({
   restNameSpace: "cluster",
   serverName: "RM",
 
+  urlForQuery(query, modelName) {
+    var url = this._buildURL();
+    if (query.state) {
+      url = url + '/apps/?state=' + query.state;
+    }
+    return url;
+  },
+
+  urlForFindRecord(id, modelName, snapshot) {
+    var url = this._buildURL();
+    url = url + '/apps/' + id;
+    return url;
+  },
+
   pathForType(modelName) {
     return 'apps'; // move to some common place, return path by modelname.
   },

+ 67 - 0
hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/app/components/app-usage-donut-chart.js

@@ -0,0 +1,67 @@
+/**
+ * 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.
+ */
+
+import Ember from 'ember';
+import DonutChart from 'yarn-ui/components/donut-chart';
+import BaseUsageDonutChart from 'yarn-ui/components/base-usage-donut-chart';
+import ColorUtils from 'yarn-ui/utils/color-utils';
+import HrefAddressUtils from 'yarn-ui/utils/href-address-utils';
+
+export default BaseUsageDonutChart.extend({
+  colors: d3.scale.category20().range(),
+
+  draw: function() {
+    this.initChart();
+    var usageByApps = [];
+    var avail = 100;
+
+    this.get("data").forEach(function (app) {
+      var v = app.get("clusterUsagePercentage");
+      if (v > 1e-2) {
+        usageByApps.push({
+          label: app.get("id"),
+          link: HrefAddressUtils.getApplicationLink(app.get("id")),
+          value: v.toFixed(2)
+        });
+
+        console.log(v);
+        avail = avail - v;
+      }
+    }.bind(this));
+
+    usageByApps.sort(function(a,b) {
+      return b.value - a.value;
+    });
+
+    usageByApps = this.mergeLongTails(usageByApps, 8);
+
+    usageByApps.push({
+      label: "Available",
+      value: avail.toFixed(4)
+    })
+
+    this.colors = ColorUtils.getColors(usageByApps.length, ["others", "good"], true);
+
+    this.renderDonutChart(usageByApps, this.get("title"), this.get("showLabels"),
+      this.get("middleLabel"), "100%", "%");
+  },
+
+  didInsertElement: function() {
+    this.draw();
+  },
+})

+ 5 - 0
hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/app/components/bar-chart.js

@@ -116,6 +116,11 @@ export default BaseChartComponent.extend({
     this.renderBarChart(this.get("data"), this.get("title"), this.get("textWidth"));
   },
 
+  _dataChange: Ember.observer("data", function() {
+    this.chart.g.selectAll("*").remove();
+    this.renderBarChart(this.get("data"), this.get("title"), this.get("textWidth"));
+  }),
+
   didInsertElement: function() {
     this.draw();
   },

+ 36 - 19
hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/app/components/base-chart-component.js

@@ -17,23 +17,26 @@
  */
 
 import Ember from 'ember';
+import Converter from 'yarn-ui/utils/converter';
 
 export default Ember.Component.extend({
-  chart: undefined,
   tooltip : undefined,
   colors: d3.scale.category10().range(),
 
-  initChart: function() {
-    this.chart = {
+  init: function () {
+    this._super();
+    this.set("chart", {
       svg: undefined,
       g: undefined,
       h: 0,
       w: 0,
       tooltip: undefined
-    };
+    });
+  },
 
+  initChart: function(removeLast = false) {
     // Init tooltip if it is not initialized
-    this.tooltip = d3.select("#chart-tooltip");
+    // this.tooltip = d3.select("#chart-tooltip");
     if (!this.tooltip) {
       this.tooltip = d3.select("body")
         .append("div")
@@ -42,13 +45,16 @@ export default Ember.Component.extend({
         .style("opacity", 0);
     }
 
-    // Init svg
-    var svg = this.chart.svg;
-    if (svg) {
-      svg.remove();
+    var parentId = this.get("parentId");
+
+    if (removeLast) {
+      // Init svg
+      var svg = d3.select("#" + parentId + "-svg");
+      if (svg) {
+        svg.remove();
+      }
     }
 
-    var parentId = this.get("parentId");
     var parent = d3.select("#" + parentId);
     var bbox = parent.node().getBoundingClientRect();
     this.chart.w = bbox.width - 30;
@@ -65,12 +71,13 @@ export default Ember.Component.extend({
 
     this.chart.svg = parent.append("svg")
       .attr("width", this.chart.w)
-      .attr("height", this.chart.h);
+      .attr("height", this.chart.h)
+      .attr("id", parentId + "-svg");
 
     this.chart.g = this.chart.svg.append("g");
   },
 
-  renderTitleAndBG: function(g, title, layout) {
+  renderTitleAndBG: function(g, title, layout, background=true) {
     var bg = g.append("g");
     bg.append("text")
       .text(title)
@@ -78,12 +85,14 @@ export default Ember.Component.extend({
       .attr("y", layout.y1 + layout.margin + 20)
       .attr("class", "chart-title");
 
-    bg.append("rect")
-      .attr("x", layout.x1)
-      .attr("y", layout.y1)
-      .attr("width", layout.x2 - layout.x1)
-      .attr("height", layout.y2 - layout.y1)
-      .attr("class", "chart-frame");
+    if (background) {
+      bg.append("rect")
+        .attr("x", layout.x1)
+        .attr("y", layout.y1)
+        .attr("width", layout.x2 - layout.x1)
+        .attr("height", layout.y2 - layout.y1)
+        .attr("class", "chart-frame");
+    }
   },
 
   bindTooltip: function(d) {
@@ -100,7 +109,11 @@ export default Ember.Component.extend({
         }
 
         this.tooltip.style("opacity", .9);
-        this.tooltip.html(data.label + " = " + data.value)
+        var value = data.value;
+        if (this.get("type") == "memory") {
+          value = Converter.memoryToSimpliedUnit(value);
+        }
+        this.tooltip.html(data.label + " = " + value)
           .style("left", (d3.event.pageX) + "px")
           .style("top", (d3.event.pageY - 28) + "px");
       }.bind(this))
@@ -109,6 +122,10 @@ export default Ember.Component.extend({
       }.bind(this));
   },
 
+  adjustMaxHeight: function(h) {
+    this.chart.svg.attr("height", h);
+  },
+
   getLayout: function() {
     var x1 = 0;
     var y1 = 0;

+ 43 - 0
hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/app/components/base-usage-donut-chart.js

@@ -0,0 +1,43 @@
+/**
+ * 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.
+ */
+
+import Ember from 'ember';
+import DonutChart from 'yarn-ui/components/donut-chart';
+
+export default DonutChart.extend({
+  mergeLongTails: function(usages, nItemsKept) {
+    var arr = [];
+    for (var i = 0; i < Math.min(usages.length, nItemsKept); i++) {
+      arr.push(usages[i]);
+    }
+
+    var others = {
+      label: "Used by others",
+      value: 0
+    }
+
+    for (var i = nItemsKept; i < usages.length; i++) {
+      others.value += Number(usages[i].value);
+    }
+    others.value = others.value.toFixed(2);
+
+    arr.push(others)
+
+    return arr;
+  },
+})

+ 41 - 14
hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/app/components/donut-chart.js

@@ -18,13 +18,15 @@
 
 import Ember from 'ember';
 import BaseChartComponent from 'yarn-ui/components/base-chart-component';
+import ColorUtils from 'yarn-ui/utils/color-utils';
+import Converter from 'yarn-ui/utils/converter';
 
 export default BaseChartComponent.extend({
   /*
    * data = [{label="xx", value=},{...}]
    */
   renderDonutChart: function(data, title, showLabels = false, 
-    middleLabel = "Total", middleValue = undefined) {
+    middleLabel = "Total", middleValue = undefined, suffix = "") {
     var g = this.chart.g;
     var layout = this.getLayout();
     this.renderTitleAndBG(g, title, layout);
@@ -39,7 +41,11 @@ export default BaseChartComponent.extend({
     }
 
     if (!middleValue) {
-      middleValue = total;
+      if (this.get("type") == "memory") {
+        middleValue = Converter.memoryToSimpliedUnit(total);
+      } else {
+        middleValue = total;
+      }
     }
 
     //Width and height
@@ -48,6 +54,8 @@ export default BaseChartComponent.extend({
     // 50 is for title
     var outerRadius = (h - 50 - 2 * layout.margin) / 2;
     var innerRadius = outerRadius * 0.618;
+    console.log("inner:" + innerRadius + " outer:" + outerRadius);
+
     var arc = d3.svg.arc()
       .innerRadius(innerRadius)
       .outerRadius(outerRadius);
@@ -104,12 +112,14 @@ export default BaseChartComponent.extend({
           return this.colors[i];
         }
       }.bind(this))
-      .attr("stroke-dasharray", function(d, i) {
-        if (d.value <= 1e-6) {
-          return "10,10";
-        }
-      }.bind(this));
     this.bindTooltip(path);
+    path.on("click", function (d) {
+      var data = d.data;
+      if (data.link) {
+        this.tooltip.remove();
+        document.location.href = data.link;
+      }
+    }.bind(this))
 
     // Show labels
     if (showLabels) {
@@ -126,27 +136,30 @@ export default BaseChartComponent.extend({
         }.bind(this))
         .attr("x", lx)
         .attr("y", function(d, i) {
-          return layout.y1 + 50 + (squareW + margin) * i + layout.margin;
+          return layout.y1 + 75 + (squareW + margin) * i + layout.margin;
         })
         .attr("width", squareW)
         .attr("height", squareW);
       select.append("text")
         .attr("x", lx + squareW + margin)
         .attr("y", function(d, i) {
-          return layout.y1 + 50 + (squareW + margin) * i + layout.margin + squareW / 2;
+          return layout.y1 + 80 + (squareW + margin) * i + layout.margin + squareW / 2;
         })
         .text(function(d) {
-          return d.label + ' = ' + d.value;
-        });
+          var value = d.value;
+          if (this.get("type") == "memory") {
+            value = Converter.memoryToSimpliedUnit(value);
+          }
+          return d.label + ' = ' + value + suffix;
+        }.bind(this));
     }
 
     if (middleLabel) {
       var highLightColor = this.colors[0];
       g.append("text").text(middleLabel).attr("x", cx).attr("y", cy - 10).
         attr("class", "donut-highlight-text").attr("fill", highLightColor);
-      g.append("text").text(middleValue).attr("x", cx).attr("y", cy + 20).
-        attr("class", "donut-highlight-text").attr("fill", highLightColor).
-        style("font-size", "30px");
+      g.append("text").text(middleValue).attr("x", cx).attr("y", cy + 15).
+        attr("class", "donut-highlight-sub").attr("fill", highLightColor);
     }
 
     path.transition()
@@ -154,8 +167,22 @@ export default BaseChartComponent.extend({
       .attrTween('d', tweenPie);
   },
 
+  _dataChange: Ember.observer("data", function() {
+    this.chart.g.selectAll("*").remove();
+    this.renderDonutChart(this.get("data"), this.get("title"), this.get("showLabels"),
+                          this.get("middleLabel"), this.get("middleValue"));
+  }),
+
   draw: function() {
     this.initChart();
+
+    var colorTargets = this.get("colorTargets");
+    if (colorTargets) {
+      var colorTargetReverse = Boolean(this.get("colorTargetReverse"));
+      var targets = colorTargets.split(" ");
+      this.colors = ColorUtils.getColors(this.get("data").length, targets, colorTargetReverse);
+    }
+
     this.renderDonutChart(this.get("data"), this.get("title"), this.get("showLabels"), 
                           this.get("middleLabel"), this.get("middleValue"));
   },

+ 209 - 0
hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/app/components/nodes-heatmap.js

@@ -0,0 +1,209 @@
+/**
+ * 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.
+ */
+
+import BaseChartComponent from 'yarn-ui/components/base-chart-component';
+import Mock from 'yarn-ui/utils/mock';
+
+export default BaseChartComponent.extend({
+  CELL_WIDTH: 250,
+  SAMPLE_CELL_WIDTH: 100,
+  SAMPLE_HEIGHT: 30,
+  CELL_HEIGHT: 30,
+  CELL_MARGIN: 2,
+  RACK_MARGIN: 20,
+  filter: "",
+
+  bindTP: function(element) {
+    element.on("mouseover", function() {
+      this.tooltip
+        .style("left", (d3.event.pageX) + "px")
+        .style("top", (d3.event.pageY - 28) + "px");
+      element.style("opacity", 1.0);
+    }.bind(this))
+      .on("mousemove", function() {
+        // Handle pie chart case
+        var text = element.attr("tooltiptext");
+
+        this.tooltip.style("opacity", .9);
+        this.tooltip.html(text)
+          .style("left", (d3.event.pageX) + "px")
+          .style("top", (d3.event.pageY - 28) + "px");
+      }.bind(this))
+      .on("mouseout", function() {
+        this.tooltip.style("opacity", 0);
+        element.style("opacity", 0.8);
+      }.bind(this));
+  },
+
+  // data:
+  //    [{label=label1, value=value1}, ...]
+  //    ...
+  renderCells: function (model, title) {
+    var data = [];
+    model.forEach(function (o) {
+      data.push(o);
+    });
+
+    this.chart.g.remove();
+    this.chart.g = this.chart.svg.append("g");
+    var g = this.chart.g;
+    var layout = this.getLayout();
+    layout.margin = 50;
+
+    let racks = new Set();
+    for (var i = 0; i < data.length; i++) {
+      racks.add(data[i].get("rack"));
+    }
+
+    let racksArray = [];
+    racks.forEach(v => racksArray.push(v));
+
+    var xOffset = layout.margin;
+    var yOffset = layout.margin * 3;
+
+    var colorFunc = d3.interpolate(d3.rgb("#bdddf5"), d3.rgb("#0f3957"));
+
+    var sampleXOffset = (layout.x2 - layout.x1) / 2 - 2.5 * this.SAMPLE_CELL_WIDTH -
+      2 * this.CELL_MARGIN;
+    var sampleYOffset = layout.margin * 2;
+
+    for (var i = 1; i <= 5; i++) {
+      var ratio = i * 0.2 - 0.1;
+
+      var rect = g.append("rect")
+        .attr("x", sampleXOffset)
+        .attr("y", sampleYOffset)
+        .attr("fill", colorFunc(ratio))
+        .attr("width", this.SAMPLE_CELL_WIDTH)
+        .attr("height", this.SAMPLE_HEIGHT);
+      g.append("text")
+        .text("" + (ratio * 100).toFixed(1) + "% Used")
+        .attr("y", sampleYOffset + this.SAMPLE_HEIGHT / 2 + 5)
+        .attr("x", sampleXOffset + this.SAMPLE_CELL_WIDTH / 2)
+        .attr("class", "heatmap-cell");
+      sampleXOffset += this.CELL_MARGIN + this.SAMPLE_CELL_WIDTH;
+    }
+
+    var chartXOffset = -1;
+
+    for (var i = 0; i < racksArray.length; i++) {
+      var text = g.append("text")
+        .text(racksArray[i])
+        .attr("y", yOffset + this.CELL_HEIGHT / 2 + 5)
+        .attr("x", layout.margin)
+        .attr("class", "heatmap-rack");
+
+      if (-1 == chartXOffset) {
+        chartXOffset = layout.margin + text.node().getComputedTextLength() + 30;
+      }
+
+      xOffset = chartXOffset;
+
+      for (var j = 0; j < data.length; j++) {
+        var rack = data[j].get("rack");
+        var host = data[j].get("nodeHostName");
+
+        if (rack == racksArray[i]) {
+          if (!rack.includes(this.filter) && !host.includes(this.filter)) {
+            this.addNode(g, xOffset, yOffset, colorFunc, data[j], false);
+            var text = g.append("text")
+              .text(host)
+              .attr("y", yOffset + this.CELL_HEIGHT / 2 + 5)
+              .attr("x", xOffset + this.CELL_WIDTH / 2)
+              .attr("class", "heatmap-cell-notselected");
+          } else {
+            this.addNode(g, xOffset, yOffset, colorFunc, data[j], true);
+            g.append("text")
+              .text(host)
+              .attr("y", yOffset + this.CELL_HEIGHT / 2 + 5)
+              .attr("x", xOffset + this.CELL_WIDTH / 2)
+              .attr("class", "heatmap-cell");
+          }
+
+          xOffset += this.CELL_MARGIN + this.CELL_WIDTH;
+          if (xOffset + this.CELL_MARGIN + this.CELL_WIDTH >= layout.x2 -
+            layout.margin) {
+            xOffset = chartXOffset;
+            yOffset = yOffset + this.CELL_MARGIN + this.CELL_HEIGHT;
+          }
+
+        }
+      }
+
+      while (xOffset > chartXOffset && xOffset + this.CELL_MARGIN +
+        this.CELL_WIDTH < layout.x2 - layout.margin) {
+        this.addPlaceholderNode(g, xOffset, yOffset);
+        xOffset += this.CELL_MARGIN + this.CELL_WIDTH;
+      }
+
+      if (xOffset != chartXOffset) {
+        xOffset = chartXOffset;
+        yOffset += this.CELL_MARGIN + this.CELL_HEIGHT;
+      }
+      yOffset += this.RACK_MARGIN;
+    }
+
+    layout.y2 = yOffset + layout.margin;
+    this.adjustMaxHeight(layout.y2);
+    this.renderTitleAndBG(g, title, layout, false);
+  },
+
+  addNode: function (g, xOffset, yOffset, colorFunc, data, selected) {
+    var rect = g.append("rect")
+      .attr("y", yOffset)
+      .attr("x", xOffset)
+      .attr("height", this.CELL_HEIGHT)
+      .attr("fill", colorFunc(data.get("usedMemoryMB") /
+        (data.get("usedMemoryMB") + data.get("availMemoryMB"))))
+      .attr("width", this.CELL_WIDTH)
+      .attr("tooltiptext", data.get("toolTipText"));
+    if (selected) {
+      rect.style("opacity", 0.8);
+      this.bindTP(rect);
+    } else {
+      rect.style("opacity", 0.8);
+      rect.attr("fill", "DimGray");
+    }
+  },
+
+  addPlaceholderNode: function(g, xOffset, yOffset) {
+    var rect = g.append("rect")
+      .attr("y", yOffset)
+      .attr("x", xOffset)
+      .attr("height", this.CELL_HEIGHT)
+      .attr("fill", "grey")
+      .attr("width", this.CELL_WIDTH)
+      .style("opacity", 0.20);
+  },
+
+  draw: function() {
+    this.initChart(true);
+    this.renderCells(this.get("model"), this.get("title"), this.get("textWidth"));
+  },
+
+  didInsertElement: function () {
+    this.draw();
+  },
+
+  actions: {
+    applyFilter: function(event) {
+      this.filter = event.srcElement.value;
+      this.didInsertElement();
+    }
+  }
+})

+ 88 - 0
hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/app/components/per-app-memusage-by-nodes-stacked-barchart.js

@@ -0,0 +1,88 @@
+/**
+ * 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.
+ */
+
+import StackedBarchart from 'yarn-ui/components/stacked-barchart';
+import Converter from 'yarn-ui/utils/converter';
+
+export default StackedBarchart.extend({
+  getDataForRender: function(containers, nodes) {
+    var arr = [];
+    var nodeToResources = {};
+    nodes.forEach(function(n) {
+      nodeToResources[n.id] =
+      {
+        used: Number(n.get("usedMemoryMB")),
+        avail: Number(n.get("availMemoryMB"))
+      }
+    });
+
+    containers.forEach(function(c) {
+      res = nodeToResources[c.get("assignedNodeId")];
+      if (res) {
+        if (!res.usedByTheApp) {
+          res.usedByTheApp = 0;
+        }
+        res.usedByTheApp += Number(c.get("allocatedMB"));
+      }
+    });
+
+    for (var nodeId in nodeToResources) {
+      var res = nodeToResources[nodeId];
+
+      var subArr = [];
+      var value = res.usedByTheApp ? res.usedByTheApp : 0;
+      subArr.push({
+        value: value,
+        bindText: "This app uses " + Converter.memoryToSimpliedUnit(value) + ". On node=" + nodeId,
+      });
+
+      value = res.used - value;
+      value = Math.max(value, 0);
+      subArr.push({
+        value: value,
+        bindText: "Other applications uses " + Converter.memoryToSimpliedUnit(value) + ". On node=" + nodeId,
+      });
+
+      subArr.push({
+        value: res.avail,
+        bindText: "Free resource " + Converter.memoryToSimpliedUnit(res.avail) + " . On node=" + nodeId
+      });
+
+      arr.push(subArr);
+    }
+
+    console.log(arr);
+
+    return arr;
+  },
+
+  didInsertElement: function() {
+    this.initChart(true);
+
+    this.colors = ["Orange", "Grey", "LimeGreen"];
+
+    var containers = this.get("rmContainers");
+    var nodes = this.get("nodes");
+
+    var data = this.getDataForRender(containers, nodes);
+
+    this.show(
+      data, this.get("title"), ["Used by this app", "Used by other apps",
+        "Available"]);
+  },
+})

+ 67 - 0
hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/app/components/per-app-ncontainers-by-nodes-stacked-barchart.js

@@ -0,0 +1,67 @@
+/**
+ * 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.
+ */
+
+import StackedBarchart from 'yarn-ui/components/stacked-barchart';
+
+export default StackedBarchart.extend({
+  getDataForRender: function(containers, nodes) {
+    var arr = [];
+    var nodeToContainers = {};
+    nodes.forEach(function(n) {
+      nodeToContainers[n.id] = 0;
+    });
+
+    containers.forEach(function(c) {
+      var nodeId = c.get("assignedNodeId");
+      var n = nodeToContainers[nodeId];
+      if (undefined != n) {
+        nodeToContainers[nodeId] += 1;
+      }
+    });
+
+    for (var nodeId in nodeToContainers) {
+      var n = nodeToContainers[nodeId];
+
+      var subArr = [];
+      subArr.push({
+        value: n,
+        bindText: "This app has " + n + " containers running on node=" + nodeId
+      });
+
+      arr.push(subArr);
+    }
+
+    console.log(arr);
+
+    return arr;
+  },
+
+  didInsertElement: function() {
+    this.initChart(true);
+
+    this.colors = ["Orange", "Grey", "Gainsboro"];
+
+    var containers = this.get("rmContainers");
+    var nodes = this.get("nodes");
+
+    var data = this.getDataForRender(containers, nodes);
+
+    this.show(
+      data, this.get("title"), ["Running containers from this app"]);
+  },
+})

+ 69 - 0
hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/app/components/queue-usage-donut-chart.js

@@ -0,0 +1,69 @@
+/**
+ * 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.
+ */
+
+import Ember from 'ember';
+import DonutChart from 'yarn-ui/components/donut-chart';
+import BaseUsageDonutChart from 'yarn-ui/components/base-usage-donut-chart';
+import ColorUtils from 'yarn-ui/utils/color-utils';
+import HrefAddressUtils from 'yarn-ui/utils/href-address-utils';
+
+export default BaseUsageDonutChart.extend({
+  colors: d3.scale.category20().range(),
+
+  draw: function() {
+    this.initChart();
+    var usageByQueues = [];
+    var avail = 100;
+
+    this.get("data").forEach(function (queue) {
+      var v = queue.get("absUsedCapacity");
+
+      if (queue.get("isLeafQueue")) {
+        if (v > 1e-2) {
+          usageByQueues.push({
+            label: queue.get("id"),
+            link: HrefAddressUtils.getQueueLink(queue.get("id")),
+            value: v.toFixed(2)
+          });
+
+          avail = avail - v;
+        }
+      }
+    });
+
+    usageByQueues.sort(function(a, b) {
+      return b.value - a.value;
+    });
+
+    usageByQueues = this.mergeLongTails(usageByQueues, 8);
+
+    usageByQueues.push({
+      label: "Available",
+      value: avail.toFixed(4)
+    });
+
+    this.colors = ColorUtils.getColors(usageByQueues.length, ["others", "good"], true);
+
+    this.renderDonutChart(usageByQueues, this.get("title"), this.get("showLabels"),
+      this.get("middleLabel"), "100%", "%");
+  },
+
+  didInsertElement: function() {
+    this.draw();
+  },
+})

+ 1 - 2
hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/app/components/queue-view.js

@@ -90,7 +90,6 @@ export default Ember.Component.extend(ChartUtilsMixin, {
       .attr("class", "queue");
 
     circle.on('mouseover', function () {
-      circle.style("fill", this.queueColors[1]);
     }.bind(this));
     circle.on('mouseout', function () {
       if (circle != this.queues.selectedQueueCircle) {
@@ -206,7 +205,7 @@ export default Ember.Component.extend(ChartUtilsMixin, {
 
   renderQueueCapacities: function (queue, layout) {
     // Render bar chart
-    this.renderBarChart(this.charts.g, [{
+    this.renderCells(this.charts.g, [{
       label: "Cap",
       value: queue.get("capacity")
     }, {

+ 7 - 2
hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/app/components/simple-table.js

@@ -1,3 +1,4 @@
+
 /**
  * Licensed to the Apache Software Foundation (ASF) under one
  * or more contributor license agreements.  See the NOTICE file
@@ -24,6 +25,7 @@ export default Ember.Component.extend({
     var ordering = this.get("ordering") ? true : this.get("ordering");
     var info = this.get("info") ? true : this.get("info");
     var bFilter = this.get("bFilter") ? true : this.get("bFilter");
+    var defaultSearch = this.get("defaultSearch") ? this.get("defaultSearch") : "";
 
     // Defines sorter for the columns if not default.
     // Can also specify a custom sorter.
@@ -66,11 +68,14 @@ export default Ember.Component.extend({
     console.log(orderArr[0]);
     Ember.$('#' + this.get('table-id')).DataTable({
       "paging":   paging,
-      "ordering": ordering, 
+      "ordering": ordering,
       "info":     info,
       "bFilter": bFilter,
       "order": orderArr,
-      "columnDefs": colDefs
+      "columnDefs": colDefs,
+      "oSearch": {
+        "sSearch": defaultSearch
+      }
     });
   }
 });

+ 198 - 0
hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/app/components/stacked-barchart.js

@@ -0,0 +1,198 @@
+/**
+ * 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.
+ */
+
+import BaseChartComponent from 'yarn-ui/components/base-chart-component';
+import Mock from 'yarn-ui/utils/mock';
+
+export default BaseChartComponent.extend({
+  MAX_BAR_HEIGHT: 120,
+  MAX_BAR_WIDTH: 30,
+  GAP: 5,
+  filter: "",
+  WIDTH_OF_SAMPLE: 200,
+
+  bindTP: function(element) {
+    element.on("mouseover", function() {
+      this.tooltip
+        .style("left", (d3.event.pageX) + "px")
+        .style("top", (d3.event.pageY - 28) + "px");
+      element.style("opacity", 1.0);
+    }.bind(this))
+      .on("mousemove", function() {
+        // Handle pie chart case
+        var text = element.attr("tooltiptext");
+
+        this.tooltip.style("opacity", .9);
+        this.tooltip.html(text)
+          .style("left", (d3.event.pageX) + "px")
+          .style("top", (d3.event.pageY - 28) + "px");
+      }.bind(this))
+      .on("mouseout", function() {
+        this.tooltip.style("opacity", 0);
+        element.style("opacity", 0.8);
+      }.bind(this));
+
+    element.on("click", function() {
+      if (element.attr("link")) {
+        this.tooltip.remove();
+        document.location.href = element.attr("link");
+      }
+    }.bind(this));
+  },
+
+  printSamples: function(n, layout, g, colorTitles) {
+    var yOffset = layout.margin * 3;
+
+    for (var i = 0; i < n; i++) {
+      var xOffset = layout.x2 - this.WIDTH_OF_SAMPLE - layout.margin;
+      g.append("rect").
+        attr("fill", this.colors[i]).
+        attr("x", xOffset).
+        attr("y", yOffset).
+        attr("width", 20).
+        attr("height", 20);
+
+      g.append("text").
+        attr("x", xOffset + 30).
+        attr("y", yOffset + 10).
+        text(colorTitles[i]);
+
+      yOffset = yOffset + 30;
+    }
+  },
+
+  // data:
+  //    [[{value=xx, bindText=xx}, {value=yy, bindText=yy}],  [  ...    ]]
+  //     __________________________________________________   ___________
+  //                          bar-1                              bar-2
+  show: function (data, title, colorTitles) {
+    var width = this.MAX_BAR_WIDTH;
+    var height = this.MAX_BAR_HEIGHT;
+
+    this.chart.g.remove();
+    this.chart.g = this.chart.svg.append("g");
+    var g = this.chart.g;
+    var layout = this.getLayout();
+    layout.margin = 50;
+
+    var nBarPerRow = Math.floor((layout.x2 - layout.x1 - 3 * layout.margin -
+      this.WIDTH_OF_SAMPLE) /
+      (width + this.GAP));
+
+    var xOffset;
+    var yOffset = layout.margin * 2;
+
+    var maxValue = 0;
+    var maxN = 0;
+    for (var i = 0; i < data.length; i++) {
+      var total = 0;
+      for (var j = 0; j < data[i].length; j++) {
+        total += data[i][j].value;
+      }
+
+      if (total > maxValue) {
+        maxValue = total;
+      }
+      if (data[i].length > maxN) {
+        maxN = data[i].length;
+      }
+    }
+
+    // print samples
+    this.printSamples(maxN, layout, g, colorTitles);
+
+    // print data
+    data.sort(function(a, b) {
+      return b[0].value - a[0].value;
+    });
+
+    for (var i = 0; i < data.length; i++) {
+      if (i % nBarPerRow == 0) {
+        xOffset = layout.margin;
+        yOffset += layout.margin + height;
+      }
+
+      var leftTopY = yOffset;
+      for (var j = 0; j < data[i].length; j++) {
+        var dy = data[i][j].value * height / maxValue;
+        if (dy > 0) {
+          leftTopY = leftTopY - dy;
+
+          var node = g.append("rect").
+            attr("fill", this.colors[j]).
+            attr("x", xOffset).
+            attr("y", leftTopY).
+            attr("width", width).
+            attr("height", dy).
+            attr("tooltiptext",
+              (data[i][j].bindText) ? data[i][j].bindText : data[i][j].value).
+            attr("link", data[i][j].link)
+            .style("opacity", 0.8);
+
+          this.bindTP(node);
+        }
+      }
+
+      if (data[i].length == 1) {
+        g.append("text")
+          .text(data[i][0].value)
+          .attr("y", leftTopY - 10)
+          .attr("x", xOffset + width / 2)
+          .attr("class", "heatmap-cell")
+          .style("fill", "black");
+      }
+
+      xOffset += width + this.GAP;
+    }
+
+    layout.y2 = yOffset + layout.margin;
+    this.adjustMaxHeight(layout.y2);
+    this.renderTitleAndBG(g, title, layout, false);
+  },
+
+  draw: function(data, title, textWidth) {
+    this.initChart(true);
+    //Mock.initMockNodesData(this);
+
+    // mock data
+    var arr = [];
+    for (var i = 0; i < 5; i++) {
+      var subArr = [];
+      for (var j = 0; j < Math.random() * 4 + 1; j++) {
+        subArr.push({
+          value : Math.abs(Math.random())
+        });
+      }
+      arr.push(subArr);
+    }
+
+    this.show(
+      arr, this.get("title"));
+  },
+
+  didInsertElement: function () {
+    this.draw();
+  },
+
+  actions: {
+    applyFilter: function(event) {
+      this.filter = event.srcElement.value;
+      this.didInsertElement();
+    }
+  }
+})

+ 1 - 1
hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/app/components/timeline-view.js

@@ -105,7 +105,7 @@ export default Ember.Component.extend({
     var border = 30;
     var singleBarHeight = this.getPerItemHeight();
     var gap = this.getPerItemGap();
-    var textWidth = 50;
+    var textWidth = 200;
     /*
      start-time                              end-time
       |--------------------------------------|

+ 33 - 10
hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/app/components/tree-selector.js

@@ -18,6 +18,8 @@
 
 import Ember from 'ember';
 
+const INBETWEEN_HEIGHT = 130;
+
 export default Ember.Component.extend({
   // Map: <queue-name, queue>
   map : undefined,
@@ -124,12 +126,25 @@ export default Ember.Component.extend({
     var nodeEnter = node.enter().append("g")
       .attr("class", "node")
       .attr("transform", function(d) { return "translate(" + source.y0 + "," + source.x0 + ")"; })
-      .on("click", function(d,i){
+      .on("mouseover", function(d,i){
         if (d.queueData.get("name") != this.get("selected")) {
-            document.location.href = "#/yarn-queue/" + d.queueData.get("name");
+            document.location.href = "#/yarn-queues/" + d.queueData.get("name");
         }
-      }.bind(this));
-      // .on("click", click);
+
+        Ember.run.later(this, function () {
+          var treeWidth = this.maxDepth * 200;
+          var treeHeight = this.numOfLeafQueue * INBETWEEN_HEIGHT;
+          var tree = d3.layout.tree().size([treeHeight, treeWidth]);
+          var diagonal = d3.svg.diagonal()
+            .projection(function(d) { return [d.y, d.x]; });
+
+          this.update(this.treeData, this.treeData, tree, diagonal);
+        }, 100);
+
+      }.bind(this))
+    .on("click", function (d) {
+      document.location.href = "#/yarn-queue/" + d.queueData.get("name");
+    });
 
     nodeEnter.append("circle")
       .attr("r", 1e-6)
@@ -148,6 +163,7 @@ export default Ember.Component.extend({
     nodeEnter.append("text")
       .attr("x", function(d) { return 0; })
       .attr("dy", ".35em")
+      .attr("fill", "white")
       .attr("text-anchor", function(d) { return "middle"; })
       .text(function(d) {
         var usedCap = d.queueData.get("usedCapacity");
@@ -161,9 +177,9 @@ export default Ember.Component.extend({
 
     // append queue name
     nodeEnter.append("text")
-      .attr("x", function(d) { return 40; })
-      .attr("dy", ".35em")
-      .attr("text-anchor", function(d) { return "start"; })
+      .attr("x", "0px")
+      .attr("dy", "45px")
+      .attr("text-anchor", "middle")
       .text(function(d) { return d.name; })
       .style("fill-opacity", 1e-6);
 
@@ -173,14 +189,21 @@ export default Ember.Component.extend({
       .attr("transform", function(d) { return "translate(" + d.y + "," + d.x + ")"; });
 
     nodeUpdate.select("circle")
-      .attr("r", 20)
+      .attr("r", 30)
       .attr("href", 
         function(d) {
           return "#/yarn-queues/" + d.queueData.get("name");
         })
+      .style("stroke-width", function(d) {
+        if (d.queueData.get("name") == this.get("selected")) {
+          return 7;
+        } else {
+          return 2;
+        }
+      }.bind(this))
       .style("stroke", function(d) {
         if (d.queueData.get("name") == this.get("selected")) {
-          return "red";
+          return "gray";
         } else {
           return "gray";
         }
@@ -239,7 +262,7 @@ export default Ember.Component.extend({
 
     var margin = {top: 20, right: 120, bottom: 20, left: 120};
     var treeWidth = this.maxDepth * 200;
-    var treeHeight = this.numOfLeafQueue * 80;
+    var treeHeight = this.numOfLeafQueue * INBETWEEN_HEIGHT;
     var width = treeWidth + margin.left + margin.right;
     var height = treeHeight + margin.top + margin.bottom;
     var layout = { };

+ 9 - 0
hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/app/controllers/cluster-overview.js

@@ -20,4 +20,13 @@ import Ember from 'ember';
 
 export default Ember.Controller.extend({
   loading: true,
+
+  breadcrumbs: [{
+    text: "Home",
+    routeName: 'application'
+  }, {
+    text: "Cluster Overview",
+    routeName: 'cluster-overview',
+  }]
+
 });

+ 40 - 0
hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/app/controllers/yarn-app-attempt.js

@@ -0,0 +1,40 @@
+/**
+ * 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.
+ */
+
+import Ember from 'ember';
+
+export default Ember.Controller.extend({
+
+  breadcrumbs: Ember.computed("model.attempt.appId", function () {
+    var appId = this.get("model.attempt.appId");
+    return [{
+      text: "Home",
+      routeName: 'application'
+    },{
+      text: "Applications",
+      routeName: 'yarn-apps'
+    }, {
+      text: `App [${appId}]`,
+      routeName: 'yarn-app',
+      model: appId
+    }, {
+      text: "Attempt",
+    }];
+  })
+
+});

+ 40 - 0
hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/app/controllers/yarn-app-attempts.js

@@ -0,0 +1,40 @@
+/**
+ * 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.
+ */
+
+import Ember from 'ember';
+
+export default Ember.Controller.extend({
+
+  breadcrumbs: Ember.computed("model.appId", function () {
+    var appId = this.get("model.appId");
+    return [{
+      text: "Home",
+      routeName: 'application'
+    },{
+      text: "Applications",
+      routeName: 'yarn-apps'
+    }, {
+      text: `App [${appId}]`,
+      routeName: 'yarn-app',
+      model: appId
+    }, {
+      text: "Attempts",
+    }];
+  })
+
+});

+ 38 - 0
hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/app/controllers/yarn-app.js

@@ -0,0 +1,38 @@
+/**
+ * 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.
+ */
+
+import Ember from 'ember';
+
+export default Ember.Controller.extend({
+
+  breadcrumbs: Ember.computed("model.app.id", function () {
+    var appId = this.get("model.app.id");
+    return [{
+      text: "Home",
+      routeName: 'application'
+    },{
+      text: "Applications",
+      routeName: 'yarn-apps'
+    }, {
+      text: `App [${appId}]`,
+      routeName: 'yarn-app',
+      model: appId
+    }];
+  })
+
+});

+ 9 - 0
hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/app/controllers/yarn-apps.js

@@ -19,4 +19,13 @@
 import Ember from 'ember';
 
 export default Ember.Controller.extend({
+
+  breadcrumbs: [{
+    text: "Home",
+    routeName: 'application'
+  }, {
+    text: "Applications",
+    routeName: 'yarn-apps',
+  }]
+
 });

+ 39 - 0
hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/app/controllers/yarn-node-apps.js

@@ -0,0 +1,39 @@
+/**
+ * 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.
+ */
+
+import Ember from 'ember';
+
+export default Ember.Controller.extend({
+
+  breadcrumbs: Ember.computed("model.attempt.appId", function () {
+    var nodeInfo = this.get("model.nodeInfo");
+    return [{
+      text: "Home",
+      routeName: 'application'
+    },{
+      text: "Nodes",
+      routeName: 'yarn-nodes'
+    }, {
+      text: `Node [ ${nodeInfo.id} ]`,
+      href: `/#/yarn-node/${nodeInfo.id}/${nodeInfo.addr}`,
+    }, {
+      text: "Applications",
+    }];
+  })
+
+});

+ 39 - 0
hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/app/controllers/yarn-node-containers.js

@@ -0,0 +1,39 @@
+/**
+ * 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.
+ */
+
+import Ember from 'ember';
+
+export default Ember.Controller.extend({
+
+  breadcrumbs: Ember.computed("model.nodeInfo", function () {
+    var nodeInfo = this.get("model.nodeInfo");
+    return [{
+      text: "Home",
+      routeName: 'application'
+    },{
+      text: "Nodes",
+      routeName: 'yarn-nodes'
+    }, {
+      text: `Node [ ${nodeInfo.id} ]`,
+      href: `/#/yarn-node/${nodeInfo.id}/${nodeInfo.addr}`,
+    }, {
+      text: "Containers",
+    }];
+  })
+
+});

+ 37 - 0
hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/app/controllers/yarn-node.js

@@ -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.
+ */
+
+import Ember from 'ember';
+
+export default Ember.Controller.extend({
+
+  breadcrumbs: Ember.computed("model.attempt.appId", function () {
+    var nodeInfo = this.get("model.nodeInfo");
+    return [{
+      text: "Home",
+      routeName: 'application'
+    },{
+      text: "Nodes",
+      routeName: 'yarn-nodes'
+    }, {
+      text: `Node [ ${nodeInfo.id} ]`,
+      href: `/#/yarn-node/${nodeInfo.id}/${nodeInfo.addr}`,
+    }];
+  })
+
+});

+ 36 - 0
hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/app/controllers/yarn-nodes-heatmap.js

@@ -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.
+ */
+
+import Ember from 'ember';
+
+export default Ember.Controller.extend({
+  needReload: true,
+  selectedQueue: undefined,
+
+  breadcrumbs: [{
+    text: "Home",
+    routeName: 'application'
+  }, {
+    text: "Nodes",
+    routeName: 'yarn-nodes',
+  }, {
+    text: "Heatmap",
+    routeName: 'yarn-nodes-heatmap',
+  }]
+
+});

+ 33 - 0
hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/app/controllers/yarn-nodes.js

@@ -0,0 +1,33 @@
+/**
+ * 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.
+ */
+
+import Ember from 'ember';
+
+export default Ember.Controller.extend({
+  needReload: true,
+  selectedQueue: undefined,
+
+  breadcrumbs: [{
+    text: "Home",
+    routeName: 'application'
+  }, {
+    text: "Nodes",
+    routeName: 'yarn-nodes',
+  }]
+
+});

+ 46 - 0
hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/app/controllers/yarn-queue-apps.js

@@ -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.
+ */
+
+import Ember from 'ember';
+
+export default Ember.Controller.extend({
+  needReload: true,
+  selectedQueue: undefined,
+
+  breadcrumbs: Ember.computed("model.selected", function () {
+    var queueName = this.get("model.selected");
+
+    return [{
+      text: "Home",
+      routeName: 'application'
+    }, {
+      text: "Queues",
+      routeName: 'yarn-queues',
+      model: 'root'
+    }, {
+      text: `Queue [ ${queueName} ]`,
+      routeName: 'yarn-queue',
+      model: queueName
+    }, {
+      text: "Applications",
+    }];
+
+  }),
+
+
+});

+ 20 - 0
hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/app/controllers/yarn-queue.js

@@ -21,4 +21,24 @@ import Ember from 'ember';
 export default Ember.Controller.extend({
   needReload: true,
   selectedQueue: undefined,
+
+  breadcrumbs: Ember.computed("model.selected", function () {
+    var queueName = this.get("model.selected");
+
+    return [{
+      text: "Home",
+      routeName: 'application'
+    }, {
+      text: "Queues",
+      routeName: 'yarn-queues',
+      model: 'root'
+    }, {
+      text: `Queue [ ${queueName} ]`,
+      routeName: 'yarn-queue',
+      model: queueName
+    }];
+
+  }),
+
+
 });

+ 34 - 0
hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/app/controllers/yarn-queues.js

@@ -0,0 +1,34 @@
+/**
+ * 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.
+ */
+
+import Ember from 'ember';
+
+export default Ember.Controller.extend({
+  needReload: true,
+  selectedQueue: undefined,
+
+  breadcrumbs: [{
+    text: "Home",
+    routeName: 'application'
+  }, {
+    text: "Queues",
+    routeName: 'yarn-queues',
+    model: 'root'
+  }]
+
+});

+ 34 - 0
hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/app/controllers/yarn-services.js

@@ -0,0 +1,34 @@
+/**
+ * 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.
+ */
+
+import Ember from 'ember';
+
+export default Ember.Controller.extend({
+
+  breadcrumbs: [{
+    text: "Home",
+    routeName: 'application'
+  }, {
+    text: "Applications",
+    routeName: 'yarn-apps',
+  }, {
+    text: "Long Running Services",
+    routeName: 'yarn-services',
+  }]
+
+});

+ 1 - 1
hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/app/models/cluster-metric.js

@@ -125,7 +125,7 @@ export default DS.Model.extend({
     });
     arr.push({
       label: "Available",
-      value: this.get("available" + type)
+      value: Math.max(this.get("available" + type), 0)
     });
 
     return arr;

+ 11 - 0
hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/app/models/yarn-app-attempt.js

@@ -30,6 +30,17 @@ export default DS.Model.extend({
   hosts: DS.attr('string'),
   logsLink: DS.attr('string'),
   state: DS.attr('string'),
+  appAttemptId: DS.attr('string'),
+
+  appId: Ember.computed("id",function () {
+    var id = this.get("id");
+    id = id.split("_");
+
+    id[0] = "application";
+    id.pop();
+
+    return id.join("_");
+  }),
 
   attemptStartedTime: function() {
     var startTime = this.get("startTime");

+ 4 - 0
hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/app/models/yarn-app.js

@@ -36,6 +36,7 @@ export default DS.Model.extend({
   unmanagedApplication: DS.attr('string'),
   amNodeLabelExpression: DS.attr('string'),
   applicationTags: DS.attr('string'),
+  applicationType: DS.attr('string'),
   priority: DS.attr('number'),
   allocatedMB: DS.attr('number'),
   allocatedVCores: DS.attr('number'),
@@ -46,6 +47,9 @@ export default DS.Model.extend({
   preemptedResourceVCores: DS.attr('number'),
   numNonAMContainerPreempted: DS.attr('number'),
   numAMContainerPreempted: DS.attr('number'),
+  clusterUsagePercentage: DS.attr('number'),
+  queueUsagePercentage: DS.attr('number'),
+  currentAppAttemptId: DS.attr('string'),
 
   isFailed: function() {
     return this.get('finalStatus') == "FAILED"

+ 7 - 0
hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/app/models/yarn-rm-node.js

@@ -89,4 +89,11 @@ export default DS.Model.extend({
     });
     return arr;
   }.property("availableVirtualCores", "usedVirtualCores"),
+
+  toolTipText: function() {
+    return "<p>Rack: " + this.get("rack") + '</p>' +
+           "<p>Host: " + this.get("nodeHostName") + '</p>' +
+           "<p>Used Memory: " + Math.round(this.get("usedMemoryMB")) + ' MB</p>' +
+           "<p>Available Memory: " + Math.round(this.get("availMemoryMB")) + ' MB</p>';
+  }.property(),
 });

+ 13 - 2
hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/app/router.js

@@ -24,8 +24,15 @@ var Router = Ember.Router.extend({
 });
 
 Router.map(function() {
-  this.route('yarn-apps');
-  this.route('yarn-nodes');
+  this.route('yarn-apps', function () {
+    this.route('apps');
+    this.route('services');
+  });
+  this.route('yarn-nodes', function(){
+    this.route('table');
+    this.route('heatmap');
+  });
+  this.route('yarn-nodes-heatmap');
   this.route('yarn-node', { path: '/yarn-node/:node_id/:node_addr' });
   this.route('yarn-node-apps', { path: '/yarn-node-apps/:node_id/:node_addr' });
   this.route('yarn-node-app',
@@ -37,11 +44,15 @@ Router.map(function() {
   this.route('yarn-container-log', { path:
       '/yarn-container-log/:node_id/:node_addr/:container_id/:filename' });
   this.route('yarn-queue', { path: '/yarn-queue/:queue_name' });
+
   this.route('cluster-overview');
   this.route('yarn-app', { path: '/yarn-app/:app_id' });
   this.route('yarn-app-attempt', { path: '/yarn-app-attempt/:app_attempt_id'});
   this.route('error');
   this.route('notfound', { path: '*:' });
+  this.route('yarn-app-attempts', { path: '/yarn-app-attempts/:app_id' });
+  this.route('yarn-queues', { path: '/yarn-queues/:queue_name' });
+  this.route('yarn-queue-apps', { path: '/yarn-queue-apps/:queue_name' });
 });
 
 export default Router;

+ 2 - 0
hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/app/routes/application.js

@@ -27,6 +27,8 @@ export default Ember.Route.extend({
      * error handler page.
      */
     error: function (error) {
+      Ember.Logger.log(error.stack);
+
       if (error && error.errors[0] &&
           error.errors[0].status == 404) {
         this.intermediateTransitionTo('/notfound');

+ 8 - 1
hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/app/routes/cluster-overview.js

@@ -20,7 +20,14 @@ import Ember from 'ember';
 
 export default Ember.Route.extend({
   model() {
-    return this.store.findAll('ClusterMetric');
+    return Ember.RSVP.hash({
+      clusterMetrics: this.store.findAll('ClusterMetric'),
+      apps: this.store.query('yarn-app',
+        {
+          state: "RUNNING"
+        }),
+      queues: this.store.findAll('yarn-queue'),
+    });
   },
 
   afterModel() {

+ 30 - 0
hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/app/routes/yarn-app-attempts.js

@@ -0,0 +1,30 @@
+/**
+ * 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.
+ */
+
+import Ember from 'ember';
+
+export default Ember.Route.extend({
+  model(param) {
+    return this.store.query('yarn-app-attempt', { appId: param.app_id}).then(function (attempts) {
+      return {
+        appId: param.app_id,
+        attempts: attempts
+      };
+    });
+  }
+});

+ 16 - 1
hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/app/routes/yarn-app.js

@@ -22,7 +22,22 @@ export default Ember.Route.extend({
   model(param) {
     return Ember.RSVP.hash({
       app: this.store.find('yarn-app', param.app_id),
-      attempts: this.store.query('yarn-app-attempt', { appId: param.app_id})
+
+      rmContainers: this.store.find('yarn-app', param.app_id).then(function(app) {
+        return this.store.query('yarn-app-attempt', {appId: param.app_id}).then(function (attempts) {
+          if (attempts && attempts.get('firstObject')) {
+            var appAttemptId = attempts.get('firstObject').get('appAttemptId');
+            var rmContainers = this.store.query('yarn-container',
+              {
+                app_attempt_id: appAttemptId,
+                is_rm: true
+              });
+            return rmContainers;
+          }
+        }.bind(this));
+      }.bind(this)),
+
+      nodes: this.store.findAll('yarn-rm-node'),
     });
   }
 });

+ 4 - 2
hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/app/routes/yarn-apps.js

@@ -20,7 +20,9 @@ import Ember from 'ember';
 
 export default Ember.Route.extend({
   model() {
-    var apps = this.store.findAll('yarn-app');
-    return apps;
+    return Ember.RSVP.hash({
+      apps: this.store.findAll('yarn-app'),
+      clusterMetrics: this.store.findAll('ClusterMetric'),
+    });
   }
 });

+ 22 - 0
hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/app/routes/yarn-apps/apps.js

@@ -0,0 +1,22 @@
+/**
+ * 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.
+ */
+
+import Ember from 'ember';
+
+export default Ember.Route.extend({
+});

+ 22 - 0
hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/app/routes/yarn-apps/services.js

@@ -0,0 +1,22 @@
+/**
+ * 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.
+ */
+
+import Ember from 'ember';
+
+export default Ember.Route.extend({
+});

+ 1 - 0
hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/app/routes/yarn-node.js

@@ -22,6 +22,7 @@ export default Ember.Route.extend({
   model(param) {
     // Fetches data from both NM and RM. RM is queried to get node usage info.
     return Ember.RSVP.hash({
+      nodeInfo: { id: param.node_id, addr: param.node_addr },
       node: this.store.findRecord('yarn-node', param.node_addr),
       rmNode: this.store.findRecord('yarn-rm-node', param.node_id)
     });

+ 4 - 1
hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/app/routes/yarn-nodes.js

@@ -20,6 +20,9 @@ import Ember from 'ember';
 
 export default Ember.Route.extend({
   model() {
-    return this.store.findAll('yarn-rm-node');
+    return Ember.RSVP.hash({
+      nodes: this.store.findAll('yarn-rm-node'),
+      clusterMetrics: this.store.findAll('ClusterMetric'),
+    });
   }
 });

+ 22 - 0
hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/app/routes/yarn-nodes/heatmap.js

@@ -0,0 +1,22 @@
+/**
+ * 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.
+ */
+
+import Ember from 'ember';
+
+export default Ember.Route.extend({
+});

+ 22 - 0
hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/app/routes/yarn-nodes/table.js

@@ -0,0 +1,22 @@
+/**
+ * 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.
+ */
+
+import Ember from 'ember';
+
+export default Ember.Route.extend({
+});

+ 36 - 0
hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/app/routes/yarn-queue-apps.js

@@ -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.
+ */
+
+import Ember from 'ember';
+
+export default Ember.Route.extend({
+  model(param) {
+    return Ember.RSVP.hash({
+      selected : param.queue_name,
+      queues: this.store.findAll('yarn-queue'),
+      selectedQueue : undefined,
+      apps: undefined, // apps of selected queue
+    });
+  },
+
+  afterModel(model) {
+    var store = this.store;
+    model.selectedQueue = this.store.peekRecord('yarn-queue', model.selected);
+    model.apps = store.findAll('yarn-app');
+  }
+});

+ 38 - 0
hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/app/routes/yarn-queues.js

@@ -0,0 +1,38 @@
+/**
+ * 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.
+ */
+
+import Ember from 'ember';
+
+export default Ember.Route.extend({
+  model(param) {
+    return Ember.RSVP.hash({
+      selected : param.queue_name,
+      queues: this.store.findAll('yarn-queue'),
+      selectedQueue : undefined,
+      apps: undefined, // apps of selected queue
+    });
+  },
+
+  afterModel(model) {
+    model.selectedQueue = this.store.peekRecord('yarn-queue', model.selected);
+    model.apps = this.store.findAll('yarn-app');
+    model.apps.forEach(function(o) {
+      console.log(o);
+    })
+  }
+});

+ 12 - 7
hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/app/serializers/yarn-app-attempt.js

@@ -40,7 +40,8 @@ export default DS.JSONAPISerializer.extend({
           nodeId: payload.nodeId,
           hosts: payload.host,
           state: payload.appAttemptState,
-          logsLink: payload.logsLink
+          logsLink: payload.logsLink,
+          appAttemptId: payload.appAttemptId
         }
       };
 
@@ -59,12 +60,16 @@ export default DS.JSONAPISerializer.extend({
       // return expected is { data: [ {}, {} ] }
       var normalizedArrayResponse = {};
 
-      // payload has apps : { app: [ {},{},{} ]  }
-      // need some error handling for ex apps or app may not be defined.
-      normalizedArrayResponse.data = payload.appAttempts.appAttempt.map(singleApp => {
-        return this.internalNormalizeSingleResponse(store, primaryModelClass,
-          singleApp, singleApp.id, requestType);
-      }, this);
+      if (payload.appAttempts && payload.appAttempts.appAttempt) {
+        // payload has apps : { app: [ {},{},{} ]  }
+        // need some error handling for ex apps or app may not be defined.
+        normalizedArrayResponse.data = payload.appAttempts.appAttempt.map(singleApp => {
+          return this.internalNormalizeSingleResponse(store, primaryModelClass,
+            singleApp, singleApp.id, requestType);
+        }, this);
+      } else {
+        normalizedArrayResponse.data = [];
+      }
       return normalizedArrayResponse;
     }
 });

+ 6 - 2
hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/app/serializers/yarn-app.js

@@ -39,6 +39,7 @@ export default DS.JSONAPISerializer.extend({
           finishedTime: Converter.timeStampToDate(payload.finishedTime),
           finalStatus: payload.finalStatus,
           progress: payload.progress,
+          applicationType: payload.applicationType,
           diagnostics: payload.diagnostics,
           amContainerLogs: payload.amContainerLogs,
           amHostHttpAddress: payload.amHostHttpAddress,
@@ -54,7 +55,10 @@ export default DS.JSONAPISerializer.extend({
           preemptedResourceMB: payload.preemptedResourceMB,
           preemptedResourceVCores: payload.preemptedResourceVCores,
           numNonAMContainerPreempted: payload.numNonAMContainerPreempted,
-          numAMContainerPreempted: payload.numAMContainerPreempted
+          numAMContainerPreempted: payload.numAMContainerPreempted,
+          clusterUsagePercentage: payload.clusterUsagePercentage,
+          queueUsagePercentage: payload.queueUsagePercentage,
+          currentAppAttemptId: payload.currentAppAttemptId
         }
       };
 
@@ -75,7 +79,7 @@ export default DS.JSONAPISerializer.extend({
 
       // payload has apps : { app: [ {},{},{} ]  }
       // need some error handling for ex apps or app may not be defined.
-      if(payload.apps) {
+      if(payload.apps && payload.apps.app) {
         normalizedArrayResponse.data = payload.apps.app.map(singleApp => {
           return this.internalNormalizeSingleResponse(store, primaryModelClass,
           singleApp, singleApp.id, requestType);

+ 13 - 7
hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/app/serializers/yarn-container.js

@@ -57,13 +57,19 @@ export default DS.JSONAPISerializer.extend({
       var normalizedArrayResponse = {};
 
       if (payload && payload.container) {
-        // payload has apps : { app: [ {},{},{} ]  }
-        // need some error handling for ex apps or app may not be defined.
-        normalizedArrayResponse.data = payload.container.map(singleContainer => {
-          return this.internalNormalizeSingleResponse(store, primaryModelClass,
-            singleContainer, singleContainer.id, requestType);
-        }, this);
-        return normalizedArrayResponse;  
+        if (Array.isArray(payload.container)) {
+          // payload has apps : { app: [ {},{},{} ]  }
+          // need some error handling for ex apps or app may not be defined.
+          normalizedArrayResponse.data = payload.container.map(singleContainer => {
+            return this.internalNormalizeSingleResponse(store, primaryModelClass,
+              singleContainer, singleContainer.id, requestType);
+          }, this);
+        } else {
+          normalizedArrayResponse.data = [this.internalNormalizeSingleResponse(
+            store, primaryModelClass, payload.container, payload.container.id,
+            requestType)];
+        }
+        return normalizedArrayResponse;
       } else {
         normalizedArrayResponse.data = [];
       }

+ 119 - 20
hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/app/styles/app.css

@@ -16,11 +16,20 @@
  * limitations under the License.
  */
 
+body, html, body > .ember-view {
+  height: 100%;
+  overflow: visible;
+  color: @text-color;
+}
+body, html {
+  min-width: 1024px;
+}
+
 /*
  Over all style
  */
 text {
-  font: 12px sans-serif;
+  font: 16px sans-serif;
 }
 
 text.small {
@@ -35,6 +44,7 @@ html, body
     width: 100%;
 }
 
+
 /*
  queue's style (left banner of queues)
  */
@@ -45,14 +55,30 @@ text.queue {
   fill : gray;
 }
 
-path.queue {
-  stroke: gray;
-  fill: none;
+text.heatmap-cell {
+  font: 14px sans-serif;
+  font-weight: bold;
+  text-anchor: middle;
+  fill: Azure;
+  text-align: center;
+}
+
+text.heatmap-cell-notselected {
+  font: 14px sans-serif;
+  font-weight: bold;
+  text-anchor: middle;
+  fill: Silver;
+  text-align: center;
 }
 
-circle.queue {
-  r: 10;
-  fill: Steelblue;
+text.heatmap-rack {
+  font: 20px sans-serif;
+  fill: DimGray;
+}
+
+path.queue {
+  stroke: "red";
+  fill: none;
 }
 
 /*
@@ -76,24 +102,21 @@ text.chart-title {
   fill: Gray;
 }
 
-text.donut-highlight-text {
-  font-size: 20px;
+text.donut-highlight-text, text.donut-highlight-sub {
+  font-size: 15px;
   font-family: sans-serif;
   text-anchor: middle;
   fill: Gray;
   vertical-align: middle;
 }
 
-rect.chart-frame {
-  fill: none;
-  stroke: gray;
-  stroke-dasharray: 10,10;
+text.donut-highlight-sub {
+  font-size: 23px;
+  margin-top: 10px;
 }
 
-line.chart-leftbanner {
-  stroke-width: 2;
-  stroke: gray;
-  stroke-dasharray: 10,10;
+rect.chart-frame {
+  fill: none;
 }
 
 text.bar-chart-text {
@@ -106,9 +129,8 @@ text.bar-chart-text {
 div.tooltip {   
   position: absolute;           
   text-align: center;
-  /*height: 28px;*/
-  padding: 2px;             
-  font: 12px sans-serif;        
+  padding: 2px;
+  font: 24px sans-serif;
   background: lightsteelblue;   
   border: 0px;      
   border-radius: 8px;
@@ -135,6 +157,19 @@ table.dataTable thead .sorting_desc_disabled {
   background-image: url("/assets/images/datatables/sort_desc_disabled.png");
 }
 
+.add-ellipsis {
+  overflow: hidden;
+  text-overflow: ellipsis;
+}
+
+.breadcrumb {
+  padding-bottom: 3px;
+}
+
+.navbar-default .navbar-nav > li > a {
+  color: #337ab7;
+}
+
 /*
  * Queue selector
  */
@@ -158,6 +193,70 @@ table.dataTable thead .sorting_desc_disabled {
   stroke-width: 2px;
 }
 
+.lr-margin {
+  margin: 0px 30px;
+}
+
+.footer {
+  background-color: @white;
+  color: @text-color;
+
+  padding: 10px 0px;
+  margin: 0px;
+
+  border-top: 1px lightgrey solid;
+
+  font-size: .9em;
+}
+
+.table {
+  margin-bottom: 0px;
+  border: none;
+
+  overflow: hidden;
+}
+
+.table-bordered > thead > tr > th, .table-bordered > tbody > tr > th, .table-bordered > tfoot > tr > th, .table-bordered > thead > tr > td, .table-bordered > tbody > tr > td, .table-bordered > tfoot > tr > td {
+  border: none !important;
+}
+
+.dataTables_wrapper .table {
+  border: 1px solid lightgrey;
+  border-bottom: 1px solid lightgrey !important;
+  border-radious: 5px;
+}
+
+.dataTables_wrapper .table-bordered > thead > tr > th, .table-bordered > tbody > tr > th, .table-bordered > tfoot > tr > th, .table-bordered > thead > tr > td, .table-bordered > tbody > tr > td, .table-bordered > tfoot > tr > td {
+  border: 1px solid lightgrey;
+}
+
+td {
+ padding: 8px 15px 8px 15px !important;
+}
+
+.footer-frame {
+  height: 60px;
+}
+.footer {
+  height: 40px;
+}
+
+.footer-pusher {
+  min-height: 100%;
+  height: auto !important;
+  height: 100%;
+  margin: 0 auto -40px; // Must be same as footer & footer-frame
+}
+
+.panel-default .container-fluid {
+  margin-top: -45px !important;
+  margin-bottom: -10px !important;
+}
+
+.panel-heading {
+  font-weight: bold;
+}
+
 .hadoop-brand-image {
   margin-top: -10px;
   width: auto;

+ 61 - 38
hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/app/templates/application.hbs

@@ -16,47 +16,70 @@
  * limitations under the License.
 }}
 
-<nav class="navbar navbar-default">
-  <div class="container-fluid">
-    <!-- Brand and toggle get grouped for better mobile display -->
-    <div class="navbar-header">
-      <a class="navbar-brand" href="#">
-        <img class="hadoop-brand-image" alt="Apache Hadoop" src="assets/images/hadoop_logo.png"  />
-      </a>
-      <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#bs-example-navbar-collapse-1" aria-expanded="false">
-        <span class="sr-only">Toggle navigation</span>
-        <span class="icon-bar"></span>
-        <span class="icon-bar"></span>
-        <span class="icon-bar"></span>
-      </button>
-    </div>
+<div class="footer-pusher">
 
-    <!-- Collect the nav links, forms, and other content for toggling -->
-    <div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1">
-      <ul class="nav navbar-nav">
-        {{#link-to 'yarn-queue' 'root' tagName="li"}}
-          {{#link-to 'yarn-queue' 'root' class="navigation-link"}}Queues
-            <span class="sr-only">(current)</span>
+  <nav class="navbar navbar-default">
+    <div class="container-fluid">
+      <!-- Brand and toggle get grouped for better mobile display -->
+      <div class="navbar-header">
+        <a class="navbar-brand" href="#">
+         <img class="hadoop-brand-image" alt="Apache Hadoop" src="assets/images/hadoop_logo.png"  />
+        </a>
+        <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#bs-example-navbar-collapse-1" aria-expanded="false">
+          <span class="sr-only">Toggle navigation</span>
+          <span class="icon-bar"></span>
+          <span class="icon-bar"></span>
+          <span class="icon-bar"></span>
+        </button>
+      </div>
+
+      <!-- Collect the nav links, forms, and other content for toggling -->
+      <div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1">
+        <ul class="nav navbar-nav">
+          {{#link-to 'cluster-overview' tagName="li"}}
+            {{#link-to 'cluster-overview' class="navigation-link"}}Cluster Overview
+              <span class="sr-only">(current)</span>
+            {{/link-to}}
           {{/link-to}}
-        {{/link-to}}
-        {{#link-to 'yarn-apps' tagName="li"}}
-          {{#link-to 'yarn-apps' class="navigation-link"}}Applications
-            <span class="sr-only">(current)</span>
+          {{#link-to 'yarn-queues' 'root' tagName="li"}}
+            {{#link-to 'yarn-queues' 'root' class="navigation-link"}}Queues
+              <span class="sr-only">(current)</span>
+            {{/link-to}}
           {{/link-to}}
-        {{/link-to}}
-        {{#link-to 'cluster-overview' tagName="li"}}
-          {{#link-to 'cluster-overview' class="navigation-link"}}Cluster Overview
-            <span class="sr-only">(current)</span>
+          {{#link-to 'yarn-apps.apps' tagName="li"}}
+            {{#link-to 'yarn-apps.apps' class="navigation-link"}}Applications
+              <span class="sr-only">(current)</span>
+            {{/link-to}}
           {{/link-to}}
-        {{/link-to}}
-        {{#link-to 'yarn-nodes' tagName="li"}}
-          {{#link-to 'yarn-nodes' class="navigation-link"}}Nodes
-            <span class="sr-only">(current)</span>
+          {{#link-to 'yarn-nodes.table' tagName="li"}}
+            {{#link-to 'yarn-nodes.table' class="navigation-link"}}Nodes
+              <span class="sr-only">(current)</span>
+            {{/link-to}}
           {{/link-to}}
-        {{/link-to}}
-      </ul>
-    </div><!-- /.navbar-collapse -->
-  </div><!-- /.container-fluid -->
-</nav>
+        </ul>
+      </div><!-- /.navbar-collapse -->
+    </div><!-- /.container-fluid -->
+  </nav>
+
+  <div class="container-fluid">
+    {{outlet}}
+  </div>
+
+  <br/>
+  <br/>
+  <br/>
 
-{{outlet}}
+</div>
+
+<div class="footer">
+  <div class="container-fluid content">
+    <a href={{env.app.hrefs.license}} target="_blank">
+      Licensed under the Apache License, Version 2.0.
+    </a>
+    <div class="ui-info">
+      {{#if env.app.timezone}}
+        <span>Timezone <b>{{env.app.timezone}}</b></span>
+      {{/if}}
+    </div>
+  </div>
+</div>

+ 123 - 45
hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/app/templates/cluster-overview.hbs

@@ -16,59 +16,137 @@
  * limitations under the License.
 }}
 
-<div class="row">
-  <div class="col-lg-3 container-fluid" id="finishedapps-donut-chart">
-    {{donut-chart data=model.firstObject.getFinishedAppsDataForDonutChart
-        title="Finished Apps"
-        showLabels=true
-        parentId="finishedapps-donut-chart"
-        ratio=0.55
-        maxHeight=350}}
-  </div>
-
-  <div class="col-lg-3 container-fluid" id="runningapps-donut-chart">
-    {{donut-chart data=model.firstObject.getRunningAppsDataForDonutChart
-        title="Running Apps"
-        showLabels=true
-        parentId="runningapps-donut-chart"
-        ratio=0.55
-        maxHeight=350}}
-  </div>
+<div class="col-md-12 container-fluid">
+  {{em-breadcrumbs items=breadcrumbs}}
 </div>
 
-<hr>
+{{#if model}}
 
-<div class="row">
-  <div class="col-lg-3 container-fluid" id="nodes-donut-chart">
-    {{donut-chart data=model.firstObject.getNodesDataForDonutChart
-        title="Node Managers"
-        showLabels=true
-        parentId="nodes-donut-chart"
-        ratio=0.55
-        maxHeight=350}}
-  </div>
-</div>
+  <div class="col-md-12 container-fluid">
+    <div class="row">
+      <div class="col-lg-6 container-fluid">
+        <div class="panel panel-default">
+          <div class="panel-heading">
+            Cluster Resource Usage By Applications
+          </div>
+          <div class="container-fluid" id="appusage-donut-chart">
+            {{app-usage-donut-chart data=model.apps
+            showLabels=true
+            parentId="appusage-donut-chart"
+            ratio=0.6
+            maxHeight=400}}
+          </div>
+        </div>
+      </div>
+
+      <div class="col-lg-6 container-fluid">
+        <div class="panel panel-default">
+          <div class="panel-heading">
+            Cluster Resource Usage By Leaf Queues
+          </div>
+          <div class="container-fluid" id="queueusage-donut-chart">
+            {{queue-usage-donut-chart data=model.queues
+            showLabels=true
+            parentId="queueusage-donut-chart"
+            ratio=0.6
+            maxHeight=400}}
+          </div>
+        </div>
+      </div>
+    </div>
+    <hr>
 
-<hr>
+  <div class="row">
+    <div class="col-lg-4 container-fluid">
+      <div class="panel panel-default">
+        <div class="panel-heading">
+          Finished Apps
+        </div>
+        <div class="container-fluid" id="finishedapps-donut-chart">
+          {{donut-chart data=model.clusterMetrics.firstObject.getFinishedAppsDataForDonutChart
+          showLabels=true
+          parentId="finishedapps-donut-chart"
+          ratio=0.6
+          maxHeight=350
+          colorTargets="good warn error"}}
+        </div>
+      </div>
+    </div>
 
-<div class="row">
-  <div class="col-lg-3 container-fluid" id="mem-donut-chart">
-    {{donut-chart data=model.firstObject.getMemoryDataForDonutChart
-        title="Resource - Memory"
-        showLabels=true
-        parentId="mem-donut-chart"
-        ratio=0.55
-        maxHeight=350}}
+    <div class="col-lg-4 container-fluid">
+      <div class="panel panel-default">
+        <div class="panel-heading">
+          Running Apps
+        </div>
+        <div class="container-fluid" id="runningapps-donut-chart">
+          {{donut-chart data=model.clusterMetrics.firstObject.getRunningAppsDataForDonutChart
+          showLabels=true
+          parentId="runningapps-donut-chart"
+          ratio=0.6
+          maxHeight=350
+          colorTargets="warn good"}}
+        </div>
+      </div>
+    </div>
+
+    <div class="col-lg-4 container-fluid">
+      <div class="panel panel-default">
+        <div class="panel-heading">
+          Node Managers
+        </div>
+        <div class="container-fluid" id="nodes-donut-chart">
+          {{donut-chart data=model.clusterMetrics.firstObject.getNodesDataForDonutChart
+          showLabels=true
+          parentId="nodes-donut-chart"
+          ratio=0.6
+          maxHeight=350
+          colorTargets="good error warn"}}
+        </div>
+      </div>
+    </div>
   </div>
 
-  <div class="col-lg-3 container-fluid" id="vcore-donut-chart">
-    {{donut-chart data=model.firstObject.getVCoreDataForDonutChart
-        title="Resource - VCores"
-        showLabels=true
-        parentId="vcore-donut-chart"
-        ratio=0.6
-        maxHeight=350}}
+  <hr>
+  <div class="row">
+
+    <div class="col-lg-4 container-fluid">
+      <div class="panel panel-default">
+        <div class="panel-heading">
+          Resource - Memory
+        </div>
+        <div class="container-fluid" id="mem-donut-chart">
+          {{donut-chart data=model.clusterMetrics.firstObject.getMemoryDataForDonutChart
+          showLabels=true
+          parentId="mem-donut-chart"
+          ratio=0.6
+          maxHeight=350
+          colorTargets="good"
+          colorTargetReverse=true
+          type="memory"}}
+        </div>
+      </div>
+    </div>
+
+    <div class="col-lg-4 container-fluid">
+      <div class="panel panel-default">
+        <div class="panel-heading">
+          Resource - VCores
+        </div>
+        <div class="container-fluid" id="vcore-donut-chart">
+          {{donut-chart data=model.clusterMetrics.firstObject.getVCoreDataForDonutChart
+          showLabels=true
+          parentId="vcore-donut-chart"
+          ratio=0.6
+          maxHeight=350
+          colorTargets="good"
+          colorTargetReverse=true}}
+        </div>
+      </div>
+    </div>
   </div>
 </div>
 
+{{/if}}
+
+
 {{outlet}}

+ 8 - 2
hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/app/templates/components/app-table.hbs

@@ -20,6 +20,7 @@
   <thead>
     <tr>
           <th>Application ID</th>
+          <th>Application Type</th>
           <th>Name</th>
           <th>User</th>
           <th>Queue</th>
@@ -30,13 +31,15 @@
           <th>Finished Time</th>
           <th>Priority</th>
           <th>Progress</th>
-        </tr>
+          <th>%Cluster</th>
+    </tr>
   </thead>
   <tbody>
     {{#if arr}}
       {{#each arr as |app|}}
         <tr>
           <td><a href="#/yarn-app/{{app.id}}">{{app.id}}</a></td>
+          <td>{{app.applicationType}}</td>
           <td>{{app.appName}}</td>
           <td>{{app.user}}</td>
           <td>{{app.queue}}</td>
@@ -53,11 +56,13 @@
               </div>
             </div>
           </td>
+          <td>{{app.clusterUsagePercentage}}</td>
         </tr>
       {{/each}}
     {{else}}
       <tr>
           <td><a href="#/yarn-app/{{app.id}}">{{app.id}}</a></td>
+          <td>{{app.applicationType}}</td>
           <td>{{app.appName}}</td>
           <td>{{app.user}}</td>
           <td>{{app.queue}}</td>
@@ -74,7 +79,8 @@
               </div>
             </div>
           </td>
-       </tr>
+          <td>{{app.clusterUsagePercentage}}</td>
+      </tr>
     {{/if}}
   </tbody>
 </table>

+ 1 - 1
hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/app/templates/components/node-menu-panel.hbs

@@ -19,7 +19,7 @@
 <div class="col-md-2 container-fluid">
   <div class="panel panel-default">
     <div class="panel-heading">
-       <h4>Node Manager<br>({{node-name nodeId}})</h4>
+       <h4>Node Manager</h4>
     </div>
       <div class="panel-body">
         <ul class="nav nav-pills nav-stacked" id="stacked-menu">

+ 27 - 0
hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/app/templates/components/nodes-heatmap.hbs

@@ -0,0 +1,27 @@
+{{!
+ * 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.
+}}
+
+<div class="container-fluid">
+  <div class="row">
+    <div class="col-md-6 container-fluid">
+      <input type="text" class="form-control" aria-label="..." placeholder="Enter part of host/rack to filter nodes"
+             onchange={{action "applyFilter"}}>
+    </div>
+  </div>
+</div>
+<p/>

+ 0 - 4
hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/app/templates/components/queue-configuration-table.hbs

@@ -24,10 +24,6 @@
     </tr>
   </thead>
   <tbody>
-    <tr>
-      <td>Queue Name</td>
-      <td>{{queue.id}}</td>
-    </tr>
     <tr>
       <td>Configured Capacity</td>
       <td>{{queue.capacity}}</td>

+ 3 - 11
hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/app/templates/components/queue-navigator.hbs

@@ -16,21 +16,13 @@
  * limitations under the License.
 }}
 
-<div class="row">
-  <div class="col-lg-4">
-      <select class="js-example-basic-single" width="100%" id="queue-name-selector">
-        {{item-selector element-id="queue-name-selector" prefix="Queue : " model=model}}
-      </select>
-  </div>
-</div><!-- /.row -->
-
 <!-- queue selector -->
 <div class="row">
-  <div class="col-md-12 container-fluid" id="tree-selector-container">
+  <div class="col-md-12 container-fluid">
+    <div class="panel panel-default" id="tree-selector-container">
      {{tree-selector model=model parentId="tree-selector-container" selected=selected}}
+    </div>
   </div>
 </div>
 
-<hr>
-
 {{outlet}}

+ 2 - 1
hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/app/templates/components/timeline-view.hbs

@@ -26,12 +26,13 @@
       {{/if}}
     </div>
     <div class="panel-body">
+      <br/><br/>
       <div class="col-md-8 container-fluid" id={{parent-id}}>
       </div>
 
       <!-- diag info -->
       <div class="col-md-4 container-fluid">
-        <div class="panel panel-default">
+        <div class="panel panel-default add-ellipsis">
           <div class="panel-heading">
             {{#if selected.link}}
               {{#link-to selected.linkname selected.id}}{{selected.id}}{{/link-to}}

+ 12 - 1
hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/app/templates/yarn-app-attempt.hbs

@@ -16,10 +16,21 @@
  * limitations under the License.
 }}
 
+<div class="col-md-12 container-fluid">
+  {{em-breadcrumbs items=breadcrumbs}}
+</div>
+<br/><br/><br/>
 <div class="container-fluid">
   <div class="row">
     {{#if model.attempt}}
-    {{app-attempt-table attempt=model.attempt}}
+      <div class="container-fluid">
+        <div class="panel panel-default">
+          <div class="panel-heading">
+            Application attempt Information
+          </div>
+          {{app-attempt-table attempt=model.attempt}}
+        </div>
+      </div>
     {{/if}}
   </div>
 

+ 57 - 0
hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/app/templates/yarn-app-attempts.hbs

@@ -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.
+}}
+
+<div class="col-md-12 container-fluid">
+  {{em-breadcrumbs items=breadcrumbs}}
+</div>
+
+<div class="col-md-12 container-fluid">
+  <div class="row">
+
+    <div class="col-md-2 container-fluid">
+      <div class="panel panel-default">
+        <div class="panel-heading">
+          <h4>Application</h4>
+        </div>
+        <div class="panel-body">
+          <ul class="nav nav-pills nav-stacked" id="stacked-menu">
+            <ul class="nav nav-pills nav-stacked collapse in">
+              {{#link-to 'yarn-app' tagName="li"}}
+                {{#link-to 'yarn-app' model.appId}}Information
+                {{/link-to}}
+              {{/link-to}}
+              {{#link-to 'yarn-app-attempts' tagName="li"}}
+                {{#link-to 'yarn-app-attempts' model.appId}}Attempts List
+                {{/link-to}}
+              {{/link-to}}
+            </ul>
+          </ul>
+        </div>
+      </div>
+    </div>
+
+    <div class="col-md-10 container-fluid">
+      <!-- timeline view of children -->
+      <div class="row">
+         {{timeline-view parent-id="attempt-timeline-div" my-id="timeline-view" height="100%" rmModel=model.attempts label="shortAppAttemptId" attemptModel=true}}
+      </div>
+    </div>
+
+  </div>
+</div>
+{{outlet}}

+ 218 - 128
hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/app/templates/yarn-app.hbs

@@ -16,148 +16,238 @@
  * limitations under the License.
 }}
 
-<div class="container-fluid">
-  <!-- app table -->
-  <div class="row">
-    <div class="col-md-12 container-fluid">
-      <div class="panel panel-default">
-          <div class="panel-heading">
-            Application Basic Information
-          </div>
-          {{app-table table-id="app-table" app=model.app}}
-       </div>
-    </div>
-  </div>
+<div class="col-md-12 container-fluid">
+  {{em-breadcrumbs items=breadcrumbs}}
+</div>
 
-  <!-- diag info and other infos -->
+{{#if model.app}}
+<div class="col-md-12 container-fluid">
   <div class="row">
-    <!-- diag info -->
-    <div class="col-md-4 container-fluid">
-      {{#if model.app.isFailed}}
-        <div class="panel panel-danger">
-          <div class="panel-heading">
-            Diagnostics
-          </div>
-          <div class="panel-body">{{model.app.diagnostics}}</div>
+
+    <div class="col-md-2 container-fluid">
+      <div class="panel panel-default">
+        <div class="panel-heading">
+          <h4>Application</h4>
         </div>
-      {{else}}
-        <div class="panel panel-default">
-          <div class="panel-body">
-            Diagnostics
-          </div>
-          <div class="panel-footer">{{model.app.diagnostics}}</div>
+        <div class="panel-body">
+          <ul class="nav nav-pills nav-stacked" id="stacked-menu">
+            <ul class="nav nav-pills nav-stacked collapse in">
+              {{#link-to 'yarn-app' tagName="li"}}
+                {{#link-to 'yarn-app' model.app.id}}Information
+                {{/link-to}}
+              {{/link-to}}
+              {{#link-to 'yarn-app-attempts' tagName="li"}}
+                {{#link-to 'yarn-app-attempts' model.app.id}}Attempts List
+                {{/link-to}}
+              {{/link-to}}
+            </ul>
+          </ul>
         </div>
-      {{/if}}
+      </div>
     </div>
 
-    <div class="col-md-5 container-fluid">
-      <div class="panel panel-default">
-        <div class="panel-heading">Scheduling Info</div>
-        <table class="table">
-          <tbody>
-            <tr>
-              <td>Allocated Resource</td>
-              <td>{{model.app.allocatedResource}}</td>
-            </tr>
-            <tr>
-              <td>Running Containers</td>
-              <td>{{model.app.runningContainersNumber}}</td>
-            </tr>
-            <tr>
-              <td>Preempted Resource</td>
-              <td>{{model.app.preemptedResource}}</td>
-            </tr>
-            <tr>
-              <td>Num Non-AM container preempted</td>
-              <td>{{model.app.numAMContainerPreempted}}</td>
-            </tr>
-            <tr>
-              <td>Num AM container preempted</td>
-              <td>{{model.app.numAMContainerPreempted}}</td>
-            </tr>
-            <tr>
-              <td>Aggregated Resource Usage</td>
-              <td>{{model.app.aggregatedResourceUsage}}</td>
-            </tr>
-          </tbody>
-        </table>
+    <div class="col-md-10 container-fluid">
+      <div class="row">
+        <div class="col-md-12 container-fluid">
+          <div class="panel panel-default">
+            <div class="panel-heading">Basic Info</div>
+            <table class="display table table-striped table-bordered"
+                   cellspacing="0" width="100%">
+              <thead>
+                <tr>
+                  <th>Application ID</th>
+                  <th>Name</th>
+                  <th>User</th>
+                  <th>Queue</th>
+                  <th>State</th>
+                  <th>Final Status</th>
+                  <th>Start Time</th>
+                  <th>Elapsed Time</th>
+                  <th>Finished Time</th>
+                  <th>Priority</th>
+                  <th>Progress</th>
+                  <th>Is Unmanaged AM</th>
+                </tr>
+              </thead>
+
+              <tbody>
+                <tr>
+                  <td>{{model.app.id}}</td>
+                  <td>{{model.app.appName}}</td>
+                  <td>{{model.app.user}}</td>
+                  <td>{{model.app.queue}}</td>
+                  <td>{{model.app.state}}</td>
+                  <td>
+                    <span class={{model.app.finalStatusStyle}}>
+                      {{model.app.finalStatus}}
+                    </span>
+                  </td>
+                  <td>{{model.app.startTime}}</td>
+                  <td>{{model.app.elapsedTime}}</td>
+                  <td>{{model.app.validatedFinishedTs}}</td>
+                  <td>{{model.app.priority}}</td>
+                  <td>
+                    <div class="progress" style="margin-bottom: 0;">
+                      <div class="progress-bar" role="progressbar"
+                           aria-valuenow="60" aria-valuemin="0"
+                           aria-valuemax="100"
+                           style={{model.app.progressStyle}}>
+                        {{model.app.progress}}%
+                      </div>
+                    </div>
+                  </td>
+                  <td>{{model.app.unmanagedApplication}}</td>
+                </tr>
+              </tbody>
+            </table>
+          </div>
+        </div>
       </div>
-    </div>
 
-    <!-- other info -->
-    <div class="col-md-3 container-fluid">
-      <div class="panel panel-default">
-        <div class="panel-heading">Other Info</div>
-        <table class="table">
-          <tbody>
-            <tr>
-              <td>AM Container Log</td>
-              <td><a href={{model.app.amContainerLogs}}>Link</a></td>
-            </tr>
-            <tr>
-              <td>AM Host Http Addr</td>
-              <td><a href={{model.app.amHostHttpAddress}}>Link</a></td>
-            </tr>
-            <tr>
-              <td>Log Aggregation Status</td>
-              <td>{{model.app.logAggregationStatus}}</td>
-            </tr>
-            <tr>
-              <td>Is Unmanaged AM</td>
-              <td>{{model.app.unmanagedApplication}}</td>
-            </tr>
-            <tr>
-              <td>AM Node Label Expression</td>
-              <td>{{model.app.amNodeLabelExpression}}</td>
-            </tr>
-          </tbody>
-        </table>
+      <div class="row">
+        {{#if model.app.diagnostics}}
+          <div class="col-md-12 container-fluid">
+            {{#if model.app.isFailed}}
+              <div class="panel panel-danger">
+                <div class="panel-heading">
+                  Diagnostics
+                </div>
+                <div class="panel-body">{{model.app.diagnostics}}</div>
+              </div>
+            {{else}}
+              <div class="panel panel-default">
+                <div class="panel-body">
+                  Diagnostics
+                </div>
+                <div class="panel-footer">{{model.app.diagnostics}}</div>
+              </div>
+            {{/if}}
+          </div>
+        {{/if}}
       </div>
-    </div>
-  </div>
 
-<!--
-  <div class="row">
-    <div class="col-md-12 container-fluid">
-      <div class="panel panel-default">
-          <div class="panel-heading">
-            Application Attempts
+      <div class="row">
+        <div class="col-md-8 container-fluid">
+          <div class="panel panel-default">
+            <div class="panel-heading">Scheduling Info</div>
+            <table class="display table table-striped table-bordered"
+                   cellspacing="0" width="100%">
+              <thead>
+              <tr>
+                <th>Allocated Resource</th>
+                <th>Running Containers</th>
+                <th>Preempted Resource</th>
+                <th>Num Non-AM container preempted</th>
+                <th>Num AM container preempted</th>
+                <th>Aggregated Resource Usage</th>
+              </tr>
+              </thead>
+
+              <tbody>
+              <tr>
+                <td>{{model.app.allocatedResource}}</td>
+                <td>{{model.app.runningContainersNumber}}</td>
+                <td>{{model.app.preemptedResource}}</td>
+                <td>{{model.app.numAMContainerPreempted}}</td>
+                <td>{{model.app.numAMContainerPreempted}}</td>
+                <td>{{model.app.aggregatedResourceUsage}}</td>
+              </tr>
+              </tbody>
+            </table>
           </div>
-          <table id="app-attempt-table" class="table table-striped table-bordered" cellspacing="0" width="100%" height="100%">
-            <thead>
+        </div>
+
+        <div class="col-md-4 container-fluid">
+          <div class="panel panel-default">
+            <div class="panel-heading">App Master Info</div>
+            <table class="display table table-striped table-bordered"
+                   cellspacing="0" width="100%">
+              <thead>
               <tr>
-                    <th>Start Time</th>
-                    <th>Master ContainerId</th>
-                    <th>Node Http Address</th>
-                    <th>Node Id</th>
-                    <th>Logs Link</th>
-                  </tr>
-            </thead>
-            <tbody>
-              {{#each model.attempts as |attempt|}}
-                <tr>
-                  <td>{{attempt.startTime}}</td>
-                  <td>{{attempt.containerId}}</td>
-                  <td><a href={{attempt.nodeHttpAddress}}>{{attempt.nodeHttpAddress}}</a></td>
-                  <td>{{attempt.nodeId}}</td>
-                  <td><a href={{attempt.logsLink}}>link</a></td>
-                </tr>
-              {{/each}}
-            </tbody>
-        </table>
+                <th>Master Container Log</th>
+                <td>Master Node</td>
+                <td>Master Node Label Expr</td>
+              </tr>
+              </thead>
+
+              <tbody>
+              <tr>
+                <td><a href={{model.app.amContainerLogs}}>Link</a></td>
+                <td><a href={{model.app.amHostHttpAddress}}>Link</a></td>
+                <td>{{model.app.amNodeLabelExpression}}</td>
+              </tr>
+              </tbody>
+            </table>
+          </div>
         </div>
-    </div>
-  </div>
--->
-  <!-- timeline view of children -->
-  <div class="row">
-     {{timeline-view parent-id="attempt-timeline-div" my-id="timeline-view" height="100%" rmModel=model.attempts label="shortAppAttemptId" attemptModel=true}}
-  </div>
-</div>
+      </div>
+
+      {{#if model.nodes}}
+        {{#if model.rmContainers}}
+          <div class="row" id="stackd-bar-chart-mem">
+            {{per-app-memusage-by-nodes-stacked-barchart
+            nodes=model.nodes
+            rmContainers=model.rmContainers
+            parentId="stackd-bar-chart-mem"
+            title=(concat 'Memory usage by nodes for: [' model.app.id ']')}}
+          </div>
+
+          <hr>
+
+          <div class="row" id="stackd-bar-chart-ncontainer">
+            {{per-app-ncontainers-by-nodes-stacked-barchart
+            nodes=model.nodes
+            rmContainers=model.rmContainers
+            parentId="stackd-bar-chart-ncontainer"
+            title=(concat 'Running #Containers by nodes for: [' model.app.id ']')}}
+          </div>
+        {{/if}}
+      {{/if}}
 
-<!--
-{{simple-table table-id="app-attempt-table" paging=false bFilter=false}}
--->
 
+      </div>
+
+    <!--
+      <div class="row">
+        <div class="col-md-12 container-fluid">
+          <div class="panel panel-default">
+              <div class="panel-heading">
+                Application Attempts
+              </div>
+              <table id="app-attempt-table" class="table table-striped table-bordered" cellspacing="0" width="100%" height="100%">
+                <thead>
+                  <tr>
+                        <th>Start Time</th>
+                        <th>Master ContainerId</th>
+                        <th>Node Http Address</th>
+                        <th>Node Id</th>
+                        <th>Logs Link</th>
+                      </tr>
+                </thead>
+                <tbody>
+                  {{#each model.attempts as |attempt|}}
+                    <tr>
+                      <td>{{attempt.startTime}}</td>
+                      <td>{{attempt.containerId}}</td>
+                      <td><a href={{attempt.nodeHttpAddress}}>{{attempt.nodeHttpAddress}}</a></td>
+                      <td>{{attempt.nodeId}}</td>
+                      <td><a href={{attempt.logsLink}}>link</a></td>
+                    </tr>
+                  {{/each}}
+                </tbody>
+            </table>
+            </div>
+        </div>
+      </div>
+    -->
 
+      <!-- timeline view of children -->
+    <!--
+      <div class="row">
+         {{timeline-view parent-id="attempt-timeline-div" my-id="timeline-view" height="100%" rmModel=model.attempts label="shortAppAttemptId" attemptModel=true}}
+      </div>
+    -->
+  </div>
+</div>
+{{/if}}
 {{outlet}}

+ 75 - 7
hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/app/templates/yarn-apps.hbs

@@ -15,10 +15,78 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
 }}
-{{#if model}}
-  {{app-table table-id="apps-table" arr=model}}
-  {{simple-table table-id="apps-table" bFilter=true colsOrder="0,desc" colTypes="natural elapsed-time" colTargets="0 7"}}
-{{else}}
-  <h4 align = "center">Could not find any applications from this cluster</h4>
-{{/if}}
-{{outlet}}
+
+<div class="col-md-12 container-fluid">
+  {{em-breadcrumbs items=breadcrumbs}}
+</div>
+
+<div class="col-md-12 container-fluid">
+  <div class="row">
+
+    <div class="col-md-2 container-fluid">
+      <div class="panel panel-default">
+        <div class="panel-heading">
+          <h4>Application</h4>
+        </div>
+        <div class="panel-body">
+          <ul class="nav nav-pills nav-stacked" id="stacked-menu">
+            <ul class="nav nav-pills nav-stacked collapse in">
+              {{#link-to 'yarn-apps.apps' tagName="li"}}
+                {{#link-to 'yarn-apps.apps'}}All Applications
+                {{/link-to}}
+              {{/link-to}}
+              {{#link-to 'yarn-apps.services' tagName="li"}}
+                {{#link-to 'yarn-apps.services'}}Long Running Services
+                {{/link-to}}
+              {{/link-to}}
+            </ul>
+          </ul>
+        </div>
+      </div>
+    </div>
+
+    <div class="col-md-10 container-fluid">
+      {{#if model.clusterMetrics}}
+        <div class="row">
+          <div class="col-lg-4 container-fluid">
+            <div class="panel panel-default">
+              <div class="panel-heading">
+                Finished Apps
+              </div>
+              <div class="container-fluid" id="finishedapps-donut-chart">
+                {{donut-chart data=model.clusterMetrics.firstObject.getFinishedAppsDataForDonutChart
+                showLabels=true
+                parentId="finishedapps-donut-chart"
+                ratio=0.6
+                maxHeight=350
+                colorTargets="good warn error"
+                }}
+              </div>
+            </div>
+          </div>
+
+          <div class="col-lg-4 container-fluid">
+            <div class="panel panel-default">
+              <div class="panel-heading">
+                Running Apps
+              </div>
+              <div class="container-fluid" id="runningapps-donut-chart">
+                {{donut-chart data=model.clusterMetrics.firstObject.getRunningAppsDataForDonutChart
+                showLabels=true
+                parentId="runningapps-donut-chart"
+                ratio=0.6
+                maxHeight=350
+                colorTargets="warn good"
+                }}
+              </div>
+            </div>
+          </div>
+        </div>
+      {{/if}}
+
+      <div class="row">
+        {{outlet}}
+      </div>
+    </div>
+  </div>
+</div>

+ 24 - 0
hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/app/templates/yarn-apps/apps.hbs

@@ -0,0 +1,24 @@
+{{!--
+  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.
+--}}
+
+{{#if model.apps}}
+  {{app-table table-id="apps-table" arr=model.apps}}
+  {{simple-table table-id="apps-table" bFilter=true colsOrder="0,desc" colTypes="natural elapsed-time" colTargets="0 7"}}
+{{else}}
+  <h4 align="center">Could not find any applications from this cluster</h4>
+{{/if}}

+ 27 - 0
hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/app/templates/yarn-apps/services.hbs

@@ -0,0 +1,27 @@
+{{!--
+  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.
+--}}
+
+{{#if model.apps}}
+  {{app-table table-id="apps-table" arr=model.apps}}
+  {{simple-table table-id="apps-table" bFilter=true colsOrder="0,desc"
+  colTypes="natural elapsed-time" colTargets="0 7" defaultSearch="slider"}}
+{{else}}
+  <h4 align="center">Could not find any applications from this cluster</h4>
+{{/if}}
+
+{{outlet}}

+ 4 - 0
hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/app/templates/yarn-node-apps.hbs

@@ -16,6 +16,10 @@
   limitations under the License.
 --}}
 
+<div class="col-md-12 container-fluid">
+  {{em-breadcrumbs items=breadcrumbs}}
+</div>
+
 <div class="col-md-12 container-fluid">
   <div class="row">
     {{node-menu-panel path="yarn-node-apps" nodeAddr=model.nodeInfo.addr nodeId=model.nodeInfo.id}}

+ 4 - 0
hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/app/templates/yarn-node-container.hbs

@@ -16,6 +16,10 @@
   limitations under the License.
 --}}
 
+<div class="col-md-12 container-fluid">
+  {{em-breadcrumbs items=breadcrumbs}}
+</div>
+
 <div class="col-md-12 container-fluid">
   <div class="row">
     {{node-menu-panel path="yarn-node-container" nodeAddr=model.nodeInfo.addr nodeId=model.nodeInfo.id}}

+ 4 - 0
hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/app/templates/yarn-node-containers.hbs

@@ -16,6 +16,10 @@
   limitations under the License.
 --}}
 
+<div class="col-md-12 container-fluid">
+  {{em-breadcrumbs items=breadcrumbs}}
+</div>
+
 <div class="col-md-12 container-fluid">
   <div class="row">
     {{node-menu-panel path="yarn-node-containers" nodeAddr=model.nodeInfo.addr nodeId=model.nodeInfo.id}}

+ 87 - 61
hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/app/templates/yarn-node.hbs

@@ -16,79 +16,105 @@
   limitations under the License.
 --}}
 
+<div class="col-md-12 container-fluid">
+  {{em-breadcrumbs items=breadcrumbs}}
+</div>
+
 <div class="col-md-12 container-fluid">
   <div class="row">
+
     {{node-menu-panel path="yarn-node" nodeId=model.rmNode.id nodeAddr=model.node.id}}
+
     <div class="col-md-10 container-fluid">
-      <div class="panel panel-default">
-        <div class="panel-heading">Node Information</div>
+
+      <div class="row">
+        <div class="col-md-12 container-fluid">
+          <div class="panel panel-default">
+          <div class="panel-heading">Node Information</div>
           <table class="table">
             <tbody>
-              <tr>
-                <td>Total Vmem allocated for Containers</td>
-                <td>{{divide num=model.node.totalVmemAllocatedContainersMB den=1024}} GB</td>
-              </tr>
-              <tr>
-                <td>Vmem enforcement enabled</td>
-                <td>{{model.node.vmemCheckEnabled}}</td>
-              </tr>
-              <tr>
-                <td>Total Pmem allocated for Containers</td>
-                <td>{{divide num=model.node.totalPmemAllocatedContainersMB den=1024}} GB</td>
-              </tr>
-              <tr>
-                <td>Pmem enforcement enabled</td>
-                <td>{{model.node.pmemCheckEnabled}}</td>
-              </tr>
-              <tr>
-                <td>Total VCores allocated for Containers</td>
-                <td>{{model.node.totalVCoresAllocatedContainers}}</td>
-              </tr>
-              <tr>
-                <td>Node Healthy Status</td>
-                <td>{{model.node.nodeHealthy}}</td>
-              </tr>
-              <tr>
-                <td>Last Node Health Report Time</td>
-                <td>{{model.node.lastNodeUpdateTime}}</td>
-              </tr>
-              <tr>
-                <td>Node Health Report</td>
-                <td>{{model.node.healthReport}}</td>
-              </tr>
-              <tr>
-                <td>Node Manager Start Time</td>
-                <td>{{model.node.nmStartupTime}}</td>
-              </tr>
-              <tr>
-                <td>Node Manager Version</td>
-                <td>{{model.node.nodeManagerBuildVersion}}</td>
-              </tr>
-              <tr>
-                <td>Hadoop Version</td>
-                <td>{{model.node.hadoopBuildVersion}}</td>
-              </tr>
+            <tr>
+              <td>Total Vmem allocated for Containers</td>
+              <td>{{divide num=model.node.totalVmemAllocatedContainersMB den=1024}} GB</td>
+            </tr>
+            <tr>
+              <td>Vmem enforcement enabled</td>
+              <td>{{model.node.vmemCheckEnabled}}</td>
+            </tr>
+            <tr>
+              <td>Total Pmem allocated for Containers</td>
+              <td>{{divide num=model.node.totalPmemAllocatedContainersMB den=1024}} GB</td>
+            </tr>
+            <tr>
+              <td>Pmem enforcement enabled</td>
+              <td>{{model.node.pmemCheckEnabled}}</td>
+            </tr>
+            <tr>
+              <td>Total VCores allocated for Containers</td>
+              <td>{{model.node.totalVCoresAllocatedContainers}}</td>
+            </tr>
+            <tr>
+              <td>Node Healthy Status</td>
+              <td>{{model.node.nodeHealthy}}</td>
+            </tr>
+            <tr>
+              <td>Last Node Health Report Time</td>
+              <td>{{model.node.lastNodeUpdateTime}}</td>
+            </tr>
+            <tr>
+              <td>Node Health Report</td>
+              <td>{{model.node.healthReport}}</td>
+            </tr>
+            <tr>
+              <td>Node Manager Start Time</td>
+              <td>{{model.node.nmStartupTime}}</td>
+            </tr>
+            <tr>
+              <td>Node Manager Version</td>
+              <td>{{model.node.nodeManagerBuildVersion}}</td>
+            </tr>
+            <tr>
+              <td>Hadoop Version</td>
+              <td>{{model.node.hadoopBuildVersion}}</td>
+            </tr>
             </tbody>
           </table>
         </div>
-      <div class="col-lg-4 container-fluid" id="mem-donut-chart">
-        {{donut-chart data=model.rmNode.getMemoryDataForDonutChart
-          title="Resource - Memory (in MB)"
-          showLabels=true
-          parentId="mem-donut-chart"
-          ratio=0.55
-          maxHeight=350}}
+        </div>
       </div>
 
-      <div class="col-lg-4 container-fluid" id="vcore-donut-chart">
-        {{donut-chart data=model.rmNode.getVCoreDataForDonutChart
-          title="Resource - VCores"
-          showLabels=true
-          parentId="vcore-donut-chart"
-          ratio=0.55
-          maxHeight=350}}
-      </div>
+      <div class="row">
+        <div class="col-lg-4 container-fluid">
+          <div class="panel panel-default">
+            <div class="panel-heading">
+              Resource - Memory (in MB)
+            </div>
+            <div class="container-fluid" id="mem-donut-chart">
+              {{donut-chart data=model.rmNode.getMemoryDataForDonutChart
+              showLabels=true
+              parentId="mem-donut-chart"
+              ratio=0.6
+              maxHeight=350}}
+            </div>
+          </div>
+        </div>
+
+        <div class="col-lg-4 container-fluid">
+          <div class="panel panel-default">
+            <div class="panel-heading">
+              Resource - VCores
+            </div>
+            <div class="container-fluid" id="vcore-donut-chart">
+              {{donut-chart data=model.rmNode.getVCoreDataForDonutChart
+              showLabels=true
+              parentId="vcore-donut-chart"
+              ratio=0.6
+              maxHeight=350}}
+            </div>
+          </div>
+        </div>
       </div>
     </div>
+  </div>
 </div>
 {{outlet}}

+ 54 - 45
hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/app/templates/yarn-nodes.hbs

@@ -16,48 +16,57 @@
   limitations under the License.
 --}}
 
-{{#if model}}
-<table id="nodes-table" class="display table table-striped table-bordered" cellspacing="0" width="100%">
-  <thead>
-    <tr>
-          <th>Node Labels</th>
-          <th>Rack</th>
-          <th>Node State</th>
-          <th>Node Address</th>
-          <th>Node HTTP Address</th>
-          <th>Last Health Update</th>
-          <th>Health-Report</th>
-          <th>Containers</th>
-          <th>Mem Used</th>
-          <th>Mem Avail</th>
-          <th>VCores Used</th>
-          <th>VCores Avail</th>
-          <th>Version</th>
-    </tr>
-  </thead>
-  <tbody>
-      {{#each model as |node|}}
-          <tr>
-            <td>{{node.nodeLabelsAsString}}</td>
-            <td>{{node.rack}}</td>
-            <td><span class={{node.nodeStateStyle}}>{{node.state}}</span></td>
-            <td>{{node.id}}</td>
-            {{node-link nodeId=node.id nodeHTTPAddress=node.nodeHTTPAddress nodeState=node.state}}
-            <td>{{node.lastHealthUpdate}}</td>
-            <td>{{node.healthReport}}</td>
-            <td>{{node.numContainers}}</td>
-            <td>{{divide num=node.usedMemoryMB den=1024}} GB</td>
-            <td>{{divide num=node.availMemoryMB den=1024}} GB</td>
-            <td>{{node.usedVirtualCores}}</td>
-            <td>{{node.availableVirtualCores}}</td>
-            <td>{{node.version}}</td>
-          </tr>
-      {{/each}}
-  </tbody>
-</table>
-
-{{simple-table table-id="nodes-table" bFilter=true}}
-{{else}}
-  <h4 align = "center">No nodes found on this cluster</h4>
-{{/if}}
-{{outlet}}
+<div class="col-md-12 container-fluid">
+  {{em-breadcrumbs items=breadcrumbs}}
+</div>
+
+<div class="row">
+  <div class="col-md-2 container-fluid">
+    <div class="panel panel-default">
+      <div class="panel-heading">
+        <h4>Nodes</h4>
+      </div>
+      <div class="panel-body">
+        <ul class="nav nav-pills nav-stacked" id="stacked-menu">
+          <ul class="nav nav-pills nav-stacked collapse in">
+
+            {{#link-to "yarn-nodes.table" tagName="li"}}
+              {{#link-to 'yarn-nodes.table'}}Nodes Table
+              {{/link-to}}
+            {{/link-to}}
+
+            {{#link-to 'yarn-nodes.heatmap' tagName="li"}}
+              {{#link-to 'yarn-nodes.heatmap'}}Nodes Heatmap Chart
+              {{/link-to}}
+            {{/link-to}}
+
+          </ul>
+        </ul>
+      </div>
+    </div>
+  </div>
+
+  <div class="col-md-10 container-fluid">
+    {{#if model.clusterMetrics}}
+      <div class="row">
+        <div class="col-lg-4 container-fluid">
+          <div class="panel panel-default">
+            <div class="panel-heading">
+              Node Managers
+            </div>
+            <div class="container-fluid" id="nodes-donut-chart">
+              {{donut-chart data=model.clusterMetrics.firstObject.getNodesDataForDonutChart
+              showLabels=true
+              parentId="nodes-donut-chart"
+              ratio=0.6
+              maxHeight=350
+              colorTargets="good error warn"}}
+            </div>
+          </div>
+        </div>
+      </div>
+    {{/if}}
+
+    {{outlet}}
+  </div>
+</div>

+ 30 - 0
hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/app/templates/yarn-nodes/heatmap.hbs

@@ -0,0 +1,30 @@
+{{!--
+  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.
+--}}
+
+{{#if model.nodes}}
+
+  <div class="row">
+    <div class="col-lg-12 container-fluid" id="nodes-heatmap-chart">
+      {{nodes-heatmap model=model.nodes parentId="nodes-heatmap-chart"
+        title="Node Heatmap Chart (Usage of Memory)"}}
+    </div>
+  </div>
+
+{{/if}}
+
+{{outlet}}

+ 67 - 0
hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/app/templates/yarn-nodes/table.hbs

@@ -0,0 +1,67 @@
+{{!--
+  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.
+--}}
+<div class="row">
+
+  {{#if model.nodes}}
+    <table id="nodes-table" class="display table table-striped table-bordered"
+           cellspacing="0" width="100%">
+      <thead>
+      <tr>
+        <th>Node Labels</th>
+        <th>Rack</th>
+        <th>Node State</th>
+        <th>Node Address</th>
+        <th>Node HTTP Address</th>
+        <th>Last Health Update</th>
+        <th>Health-Report</th>
+        <th>Containers</th>
+        <th>Mem Used</th>
+        <th>Mem Avail</th>
+        <th>VCores Used</th>
+        <th>VCores Avail</th>
+        <th>Version</th>
+      </tr>
+      </thead>
+      <tbody>
+      {{#each model.nodes as |node|}}
+        <tr>
+          <td>{{node.nodeLabelsAsString}}</td>
+          <td>{{node.rack}}</td>
+          <td><span class={{node.nodeStateStyle}}>{{node.state}}</span></td>
+          <td>{{node.id}}</td>
+          {{node-link nodeId=node.id nodeHTTPAddress=node.nodeHTTPAddress nodeState=node.state}}
+          <td>{{node.lastHealthUpdate}}</td>
+          <td>{{node.healthReport}}</td>
+          <td>{{node.numContainers}}</td>
+          <td>{{divide num=node.usedMemoryMB den=1024}} GB</td>
+          <td>{{divide num=node.availMemoryMB den=1024}} GB</td>
+          <td>{{node.usedVirtualCores}}</td>
+          <td>{{node.availableVirtualCores}}</td>
+          <td>{{node.version}}</td>
+        </tr>
+      {{/each}}
+      </tbody>
+    </table>
+
+    {{simple-table table-id="nodes-table" bFilter=true}}
+  {{else}}
+    <h4 align="center">No nodes found on this cluster</h4>
+  {{/if}}
+</div>
+
+{{outlet}}

+ 66 - 0
hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/app/templates/yarn-queue-apps.hbs

@@ -0,0 +1,66 @@
+{{!
+ * 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.
+}}
+
+<div class="col-md-12 container-fluid">
+  {{em-breadcrumbs items=breadcrumbs}}
+</div>
+
+<div class="col-md-12 container-fluid">
+  <div class="row">
+
+    <div class="col-md-2 container-fluid">
+      <div class="panel panel-default">
+        <div class="panel-heading">
+          <h4>Application</h4>
+        </div>
+        <div class="panel-body">
+          <ul class="nav nav-pills nav-stacked" id="stacked-menu">
+            <ul class="nav nav-pills nav-stacked collapse in">
+              {{#link-to 'yarn-queue' tagName="li"}}
+                {{#link-to 'yarn-queue' model.selected}}Information
+                {{/link-to}}
+              {{/link-to}}
+              {{#link-to 'yarn-queue-apps' tagName="li"}}
+                {{#link-to 'yarn-queue-apps' model.selected}}Applications List
+                {{/link-to}}
+              {{/link-to}}
+            </ul>
+          </ul>
+        </div>
+      </div>
+    </div>
+
+    <div class="col-md-10 container-fluid">
+      <!-- timeline view of children -->
+      <div class="row">
+
+        <div class="col-lg-12 container-fluid">
+          {{#if model.apps}}
+            {{app-table table-id="apps-table" arr=model.apps}}
+            {{simple-table table-id="apps-table" bFilter=true colTypes="elapsed-time" colTargets="7"}}
+          {{else}}
+            <h4 align = "center">Could not find any applications from this cluster</h4>
+          {{/if}}
+        </div>
+
+      </div>
+    </div>
+
+  </div>
+</div>
+{{outlet}}

+ 83 - 43
hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/app/templates/yarn-queue.hbs

@@ -16,55 +16,95 @@
  * limitations under the License.
 }}
 
-<div class="container-fluid">
-  {{queue-navigator model=model.queues selected=model.selected}}
+<div class="col-md-12 container-fluid">
+  {{em-breadcrumbs items=breadcrumbs}}
 </div>
 
-<div class="row">
-  <div class="col-lg-3 container-fluid">
-    {{queue-configuration-table queue=model.selectedQueue}}
-  </div>
+<div class="col-md-12 container-fluid">
+  <div class="row">
 
-  <div class="col-lg-3 container-fluid" id="capacity-bar-chart">
-    {{bar-chart data=model.selectedQueue.capacitiesBarChartData 
-        title="Queue Capacities" 
-        parentId="capacity-bar-chart"
-        textWidth=150
-        ratio=0.5
-        maxHeight=350}}
-  </div>
+    <div class="col-md-2 container-fluid">
+      <div class="panel panel-default">
+        <div class="panel-heading">
+          <h4>Application</h4>
+        </div>
+        <div class="panel-body">
+          <ul class="nav nav-pills nav-stacked" id="stacked-menu">
+            <ul class="nav nav-pills nav-stacked collapse in">
+              {{#link-to 'yarn-queue' tagName="li"}}
+                {{#link-to 'yarn-queue' model.selected}}Information
+                {{/link-to}}
+              {{/link-to}}
+              {{#link-to 'yarn-queue-apps' tagName="li"}}
+                {{#link-to 'yarn-queue-apps' model.selected}}Applications List
+                {{/link-to}}
+              {{/link-to}}
+            </ul>
+          </ul>
+        </div>
+      </div>
+    </div>
 
-{{#if model.selectedQueue.hasUserUsages}}
-  <div class="col-lg-3 container-fluid" id="userusage-donut-chart">
-    {{donut-chart data=model.selectedQueue.userUsagesDonutChartData 
-        title="User Usages" 
-        showLabels=true
-        parentId="userusage-donut-chart"
-        maxHeight=350}}
-  </div>
-{{/if}}
+    <div class="col-md-10 container-fluid">
+      <!-- timeline view of children -->
+      <div class="row">
 
-  <div class="col-lg-3 container-fluid" id="numapplications-donut-chart">
-    {{donut-chart data=model.selectedQueue.numOfApplicationsDonutChartData 
-        title="Running Apps" 
-        showLabels=true
-        parentId="numapplications-donut-chart"
-        ratio=0.5
-        maxHeight=350}}
-  </div>
-</div>
+        <div class="col-lg-4 container-fluid">
+          <div class="panel panel-default">
+            <div class="panel-heading">
+              Queue Information
+            </div>
+            {{queue-configuration-table queue=model.selectedQueue}}
+          </div>
+        </div>
 
-<hr>
+        <div class="col-lg-4 container-fluid">
+          <div class="panel panel-default">
+            <div class="panel-heading">
+              Queue Capacities
+            </div>
+            <div class="container-fluid" id="capacity-bar-chart">
+              <br/>
+              {{bar-chart data=model.selectedQueue.capacitiesBarChartData
+              title=""
+              parentId="capacity-bar-chart"
+              textWidth=170
+              ratio=0.55
+              maxHeight=350}}
+            </div>
+          </div>
+        </div>
+
+        {{#if model.selectedQueue.hasUserUsages}}
+          <div class="col-lg-4 container-fluid" id="userusage-donut-chart">
+            {{donut-chart data=model.selectedQueue.userUsagesDonutChartData
+            title="User Usages"
+            showLabels=true
+            parentId="userusage-donut-chart"
+            type="memory"
+            ratio=0.6
+            maxHeight=350}}
+          </div>
+        {{/if}}
+
+        <div class="col-lg-4 container-fluid">
+          <div class="panel panel-default">
+            <div class="panel-heading">
+              Running Apps
+            </div>
+            <div class="container-fluid" id="numapplications-donut-chart">
+              {{donut-chart data=model.selectedQueue.numOfApplicationsDonutChartData
+              showLabels=true
+              parentId="numapplications-donut-chart"
+              ratio=0.6
+              maxHeight=350}}
+            </div>
+          </div>
+        </div>
+
+      </div>
+    </div>
 
-<div class="row">
-  <div class="col-md-12 container-fluid">
-    {{#if model.apps}}
-      {{app-table table-id="apps-table" arr=model.apps}}
-      {{simple-table table-id="apps-table" bFilter=true colTypes="elapsed-time" colTargets="7"}}
-    {{else}}
-      <h4 align = "center">Could not find any applications from this cluster</h4>
-    {{/if}}
   </div>
 </div>
-
-{{outlet}}
+{{outlet}}

+ 72 - 0
hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/app/templates/yarn-queues.hbs

@@ -0,0 +1,72 @@
+{{!
+ * 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.
+}}
+
+<div class="col-md-12 container-fluid">
+  {{em-breadcrumbs items=breadcrumbs}}
+</div>
+
+<div class="container-fluid">
+  {{queue-navigator model=model.queues selected=model.selected}}
+
+  <div class="row">
+
+    <div class="col-lg-4 container-fluid">
+      <div class="panel panel-default">
+        <div class="panel-heading">
+          Queue Information
+        </div>
+        {{queue-configuration-table queue=model.selectedQueue}}
+      </div>
+    </div>
+
+    <div class="col-lg-4 container-fluid">
+      <div class="panel panel-default">
+        <div class="panel-heading">
+          Queue Capacities
+        </div>
+        <div class="container-fluid" id="capacity-bar-chart">
+          <br/>
+          {{bar-chart data=model.selectedQueue.capacitiesBarChartData
+          title=""
+          parentId="capacity-bar-chart"
+          textWidth=150
+          ratio=0.55
+          maxHeight=350}}
+        </div>
+      </div>
+    </div>
+
+    <div class="col-lg-4 container-fluid">
+      <div class="panel panel-default">
+        <div class="panel-heading">
+          Running Apps
+        </div>
+        <div class="container-fluid" id="numapplications-donut-chart">
+          {{donut-chart data=model.selectedQueue.numOfApplicationsDonutChartData
+          showLabels=true
+          parentId="numapplications-donut-chart"
+          ratio=0.6
+          maxHeight=350}}
+        </div>
+      </div>
+    </div>
+
+  </div>
+</div>
+
+{{outlet}}

+ 67 - 0
hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/app/utils/color-utils.js

@@ -0,0 +1,67 @@
+/**
+ * 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.
+ */
+
+import Constants from 'yarn-ui/constants';
+
+export default {
+  preDefinedColors : ["#1f77b4", "#aec7e8", "#ffbb78",
+    "#98df8a", "#ff9896", "#9467bd", "#c5b0d5", "#8c564b",
+    "#c49c94", "#e377c2", "#f7b6d2", "#c7c7c7", "#bcbd22",
+    "#dbdb8d", "#17becf", "#9edae5"],
+
+  colorMap: {
+    "warn": "#ff7f0e",
+    "good": "#2ca02c",
+    "error": "#d62728",
+    "others": "#7f7f7f",
+  },
+
+  getColors: function(nColors, colorsTarget, reverse = false) {
+    var colors = [];
+    for (var i = 0; i < nColors; i++) {
+      colors.push(undefined);
+    }
+
+    var startIdx = 0;
+
+    if (reverse) {
+      startIdx = Math.max(nColors - colorsTarget.length, 0);
+    }
+
+    for (var i = 0; i < colorsTarget.length; i++) {
+      if (i + startIdx < nColors) {
+        colors[i + startIdx] = this.getColorByTarget(colorsTarget[i]);
+      }
+    }
+
+    var idx = 0;
+    for (var i = 0; i < nColors; i++) {
+      if (!colors[i]) {
+        colors[i] = this.preDefinedColors[i % this.preDefinedColors.length];
+        idx ++;
+      }
+    }
+
+    console.log(colors);
+    return colors;
+  },
+
+  getColorByTarget: function(target) {
+    return this.colorMap[target];
+  }
+};

+ 17 - 0
hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/app/utils/converter.js

@@ -106,4 +106,21 @@ export default {
       return [splits[0], splits[1], fileName];
     }
   },
+  memoryToSimpliedUnit: function(mb) {
+    var unit = "MB"
+    var value = mb;
+    if (value / 1024 >= 0.9) {
+      value = value / 1024;
+      unit = "GB";
+    }
+    if (value / 1024 >= 0.9) {
+      value = value / 1024;
+      unit = "TB";
+    }
+    if (value / 1024 >= 0.9) {
+      value = value / 1024;
+      unit = "PB";
+    }
+    return value.toFixed(1) + " " + unit;
+  }
 };

+ 29 - 0
hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/app/utils/href-address-utils.js

@@ -0,0 +1,29 @@
+/**
+ * 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.
+ */
+
+import Constants from 'yarn-ui/constants';
+
+export default {
+  getApplicationLink: function(applicationId) {
+    return "#/yarn-app/" + applicationId;
+  },
+
+  getQueueLink: function(queueName) {
+    return '#/yarn-queue/' + queueName;
+  }
+};

+ 36 - 0
hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/app/utils/mock.js

@@ -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.
+ */
+
+export default {
+  initMockNodesData: function(ref) {
+    var data = [];
+    for (var i = 0; i < 3; i++) {
+      for (var j = 0; j < 38; j++) {
+        var node = ref.get('targetObject.store').createRecord('YarnRmNode', {
+          rack: "/rack-" + i,
+          nodeHostName: "hadoop-" + ["centos6", "ubuntu7", "win"][i % 3] + "-" + ["web", "etl", "dm"][j % 3] + "-" + j,
+          usedMemoryMB: Math.abs(Math.random() * 10000),
+          availMemoryMB: Math.abs(Math.random() * 10000)
+        });
+        data.push(node);
+      }
+    }
+
+    ref.set("model", data);
+  },
+}

+ 2 - 1
hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/bower.json

@@ -18,6 +18,7 @@
     "datatables": "~1.10.8",
     "spin.js": "~2.3.2",
     "momentjs": "~2.10.6",
-    "select2": "4.0.0"
+    "select2": "4.0.0",
+    "snippet-ss": "~1.11.0"
   }
 }

+ 4 - 0
hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/package.json

@@ -21,6 +21,7 @@
   "devDependencies": {
     "broccoli-asset-rev": "2.4.2",
     "broccoli-funnel": "1.0.1",
+    "em-table": "0.1.6",
     "ember-bootstrap": "0.5.1",
     "ember-array-contains-helper": "1.0.2",
     "ember-cli": "1.13.13",
@@ -45,5 +46,8 @@
     "ember-spin-spinner": "0.2.3",
     "ember-truth-helpers": "1.2.0",
     "select2": "4.0.0"
+  },
+  "dependencies": {
+    "em-helpers": "^0.5.13"
   }
 }

+ 30 - 0
hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/tests/unit/controllers/yarn-app-attempt-test.js

@@ -0,0 +1,30 @@
+/**
+ * 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.
+ */
+
+import { moduleFor, test } from 'ember-qunit';
+
+moduleFor('controller:yarn-app-attempt', 'Unit | Controller | yarn app attempt', {
+  // Specify the other units that are required for this test.
+  // needs: ['controller:foo']
+});
+
+// Replace this with your real tests.
+test('it exists', function(assert) {
+  let controller = this.subject();
+  assert.ok(controller);
+});

+ 30 - 0
hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/tests/unit/controllers/yarn-app-attempts-test.js

@@ -0,0 +1,30 @@
+/**
+ * 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.
+ */
+
+import { moduleFor, test } from 'ember-qunit';
+
+moduleFor('controller:yarn-app-attempts', 'Unit | Controller | yarn app attempts', {
+  // Specify the other units that are required for this test.
+  // needs: ['controller:foo']
+});
+
+// Replace this with your real tests.
+test('it exists', function(assert) {
+  let controller = this.subject();
+  assert.ok(controller);
+});

+ 30 - 0
hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/tests/unit/controllers/yarn-app-test.js

@@ -0,0 +1,30 @@
+/**
+ * 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.
+ */
+
+import { moduleFor, test } from 'ember-qunit';
+
+moduleFor('controller:yarn-app', 'Unit | Controller | yarn app', {
+  // Specify the other units that are required for this test.
+  // needs: ['controller:foo']
+});
+
+// Replace this with your real tests.
+test('it exists', function(assert) {
+  let controller = this.subject();
+  assert.ok(controller);
+});

+ 30 - 0
hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/tests/unit/controllers/yarn-node-apps-test.js

@@ -0,0 +1,30 @@
+/**
+ * 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.
+ */
+
+import { moduleFor, test } from 'ember-qunit';
+
+moduleFor('controller:yarn-node-apps', 'Unit | Controller | yarn node apps', {
+  // Specify the other units that are required for this test.
+  // needs: ['controller:foo']
+});
+
+// Replace this with your real tests.
+test('it exists', function(assert) {
+  let controller = this.subject();
+  assert.ok(controller);
+});

+ 30 - 0
hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/tests/unit/controllers/yarn-node-containers-test.js

@@ -0,0 +1,30 @@
+/**
+ * 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.
+ */
+
+import { moduleFor, test } from 'ember-qunit';
+
+moduleFor('controller:yarn-node-containers', 'Unit | Controller | yarn node containers', {
+  // Specify the other units that are required for this test.
+  // needs: ['controller:foo']
+});
+
+// Replace this with your real tests.
+test('it exists', function(assert) {
+  let controller = this.subject();
+  assert.ok(controller);
+});

+ 30 - 0
hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/tests/unit/controllers/yarn-node-test.js

@@ -0,0 +1,30 @@
+/**
+ * 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.
+ */
+
+import { moduleFor, test } from 'ember-qunit';
+
+moduleFor('controller:yarn-node', 'Unit | Controller | yarn node', {
+  // Specify the other units that are required for this test.
+  // needs: ['controller:foo']
+});
+
+// Replace this with your real tests.
+test('it exists', function(assert) {
+  let controller = this.subject();
+  assert.ok(controller);
+});

+ 30 - 0
hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/tests/unit/controllers/yarn-nodes-heatmap-test.js

@@ -0,0 +1,30 @@
+/**
+ * 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.
+ */
+
+import { moduleFor, test } from 'ember-qunit';
+
+moduleFor('controller:yarn-nodes-heatmap', 'Unit | Controller | yarn nodes heatmap', {
+  // Specify the other units that are required for this test.
+  // needs: ['controller:foo']
+});
+
+// Replace this with your real tests.
+test('it exists', function(assert) {
+  let controller = this.subject();
+  assert.ok(controller);
+});

+ 30 - 0
hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/tests/unit/controllers/yarn-nodes-test.js

@@ -0,0 +1,30 @@
+/**
+ * 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.
+ */
+
+import { moduleFor, test } from 'ember-qunit';
+
+moduleFor('controller:yarn-nodes', 'Unit | Controller | yarn nodes', {
+  // Specify the other units that are required for this test.
+  // needs: ['controller:foo']
+});
+
+// Replace this with your real tests.
+test('it exists', function(assert) {
+  let controller = this.subject();
+  assert.ok(controller);
+});

+ 30 - 0
hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/tests/unit/controllers/yarn-queue-apps-test.js

@@ -0,0 +1,30 @@
+/**
+ * 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.
+ */
+
+import { moduleFor, test } from 'ember-qunit';
+
+moduleFor('controller:yarn-queue-apps', 'Unit | Controller | yarn queue apps', {
+  // Specify the other units that are required for this test.
+  // needs: ['controller:foo']
+});
+
+// Replace this with your real tests.
+test('it exists', function(assert) {
+  let controller = this.subject();
+  assert.ok(controller);
+});

+ 2 - 2
hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/tests/unit/controllers/yarn-queues-test.js

@@ -18,13 +18,13 @@
 
 import { moduleFor, test } from 'ember-qunit';
 
-moduleFor('controller:yarn-queues', {
+moduleFor('controller:yarn-queues', 'Unit | Controller | yarn queues', {
   // Specify the other units that are required for this test.
   // needs: ['controller:foo']
 });
 
 // Replace this with your real tests.
 test('it exists', function(assert) {
-  var controller = this.subject();
+  let controller = this.subject();
   assert.ok(controller);
 });

+ 30 - 0
hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/tests/unit/controllers/yarn-services-test.js

@@ -0,0 +1,30 @@
+/**
+ * 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.
+ */
+
+import { moduleFor, test } from 'ember-qunit';
+
+moduleFor('controller:yarn-services', 'Unit | Controller | yarn services', {
+  // Specify the other units that are required for this test.
+  // needs: ['controller:foo']
+});
+
+// Replace this with your real tests.
+test('it exists', function(assert) {
+  let controller = this.subject();
+  assert.ok(controller);
+});

+ 29 - 0
hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/tests/unit/routes/yarn-app-attempts-test.js

@@ -0,0 +1,29 @@
+/**
+ * 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.
+ */
+
+import { moduleFor, test } from 'ember-qunit';
+
+moduleFor('route:yarn-app-attempts', 'Unit | Route | yarn app attempts', {
+  // Specify the other units that are required for this test.
+  // needs: ['controller:foo']
+});
+
+test('it exists', function(assert) {
+  let route = this.subject();
+  assert.ok(route);
+});

+ 29 - 0
hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/tests/unit/routes/yarn-queue-apps-test.js

@@ -0,0 +1,29 @@
+/**
+ * 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.
+ */
+
+import { moduleFor, test } from 'ember-qunit';
+
+moduleFor('route:yarn-queue-apps', 'Unit | Route | yarn queue apps', {
+  // Specify the other units that are required for this test.
+  // needs: ['controller:foo']
+});
+
+test('it exists', function(assert) {
+  let route = this.subject();
+  assert.ok(route);
+});

+ 29 - 0
hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/tests/unit/routes/yarn-queues-test.js

@@ -0,0 +1,29 @@
+/**
+ * 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.
+ */
+
+import { moduleFor, test } from 'ember-qunit';
+
+moduleFor('route:yarn-queues', 'Unit | Route | yarn queues', {
+  // Specify the other units that are required for this test.
+  // needs: ['controller:foo']
+});
+
+test('it exists', function(assert) {
+  let route = this.subject();
+  assert.ok(route);
+});