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)
This commit is contained in:
Sunil 2016-07-15 21:16:06 +05:30 committed by Wangda Tan
parent 35f08122e2
commit 8f584a561e
95 changed files with 3419 additions and 502 deletions

View File

@ -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.
},

View File

@ -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();
},
})

View File

@ -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();
},

View File

@ -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;

View File

@ -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;
},
})

View File

@ -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"));
},

View File

@ -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();
}
}
})

View File

@ -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"]);
},
})

View File

@ -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"]);
},
})

View File

@ -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();
},
})

View File

@ -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")
}, {

View File

@ -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
}
});
}
});

View File

@ -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();
}
}
})

View File

@ -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
|--------------------------------------|

View File

@ -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 = { };

View File

@ -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',
}]
});

View File

@ -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",
}];
})
});

View File

@ -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",
}];
})
});

View File

@ -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
}];
})
});

View File

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

View File

@ -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",
}];
})
});

View File

@ -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",
}];
})
});

View File

@ -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}`,
}];
})
});

View File

@ -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',
}]
});

View File

@ -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',
}]
});

View File

@ -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",
}];
}),
});

View File

@ -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
}];
}),
});

View File

@ -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'
}]
});

View File

@ -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',
}]
});

View File

@ -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;

View File

@ -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");

View File

@ -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"

View File

@ -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(),
});

View File

@ -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;

View File

@ -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');

View File

@ -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() {

View File

@ -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
};
});
}
});

View File

@ -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'),
});
}
});

View File

@ -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'),
});
}
});

View File

@ -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({
});

View File

@ -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({
});

View File

@ -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)
});

View File

@ -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'),
});
}
});

View File

@ -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({
});

View File

@ -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({
});

View File

@ -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');
}
});

View File

@ -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);
})
}
});

View File

@ -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;
}
});

View File

@ -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);

View File

@ -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 = [];
}

View File

@ -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;
}
circle.queue {
r: 10;
fill: Steelblue;
text.heatmap-cell-notselected {
font: 14px sans-serif;
font-weight: bold;
text-anchor: middle;
fill: Silver;
text-align: center;
}
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;

View File

@ -16,47 +16,70 @@
* limitations under the License.
}}
<nav class="navbar navbar-default">
<div class="footer-pusher">
<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 '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 '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 'yarn-nodes.table' tagName="li"}}
{{#link-to 'yarn-nodes.table' class="navigation-link"}}Nodes
<span class="sr-only">(current)</span>
{{/link-to}}
{{/link-to}}
</ul>
</div><!-- /.navbar-collapse -->
</div><!-- /.container-fluid -->
</nav>
<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>
{{outlet}}
</div>
<br/>
<br/>
<br/>
</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>
<!-- 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>
{{/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}}
{{/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}}
{{/link-to}}
{{#link-to 'yarn-nodes' tagName="li"}}
{{#link-to 'yarn-nodes' class="navigation-link"}}Nodes
<span class="sr-only">(current)</span>
{{/link-to}}
{{/link-to}}
</ul>
</div><!-- /.navbar-collapse -->
</div><!-- /.container-fluid -->
</nav>
{{outlet}}
</div>
</div>

View File

@ -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 class="col-md-12 container-fluid">
{{em-breadcrumbs items=breadcrumbs}}
</div>
{{#if model}}
<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>
<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 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="runningapps-donut-chart">
{{donut-chart data=model.firstObject.getRunningAppsDataForDonutChart
title="Running Apps"
showLabels=true
parentId="runningapps-donut-chart"
ratio=0.55
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>
<hr>
{{/if}}
<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>
<hr>
<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>
<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}}
</div>
</div>
{{outlet}}

View File

@ -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>

View File

@ -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">

View File

@ -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/>

View File

@ -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>

View File

@ -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}}

View File

@ -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}}

View File

@ -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>

View File

@ -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}}

View File

@ -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>
<!-- diag info and other infos -->
<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>
{{else}}
<div class="panel panel-default">
<div class="panel-body">
Diagnostics
</div>
<div class="panel-footer">{{model.app.diagnostics}}</div>
</div>
{{/if}}
</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>
</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>
</div>
</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 class="col-md-12 container-fluid">
{{em-breadcrumbs items=breadcrumbs}}
</div>
<!--
{{simple-table table-id="app-attempt-table" paging=false bFilter=false}}
-->
{{#if model.app}}
<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.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>
</div>
</div>
<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 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 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>
</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>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>
{{#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}}
</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}}

View File

@ -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>

View File

@ -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}}

View File

@ -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}}

View File

@ -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}}

View File

@ -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}}

View File

@ -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}}

View File

@ -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}}

View File

@ -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>
<div class="col-md-12 container-fluid">
{{em-breadcrumbs items=breadcrumbs}}
</div>
{{simple-table table-id="nodes-table" bFilter=true}}
{{else}}
<h4 align = "center">No nodes found on this cluster</h4>
{{/if}}
{{outlet}}
<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>

View File

@ -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}}

View File

@ -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}}

View File

@ -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}}

View File

@ -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-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=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="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>
<hr>
<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}}

View File

@ -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}}

View File

@ -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];
}
};

View File

@ -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;
}
};

View File

@ -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;
}
};

View File

@ -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);
},
}

View File

@ -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"
}
}

View File

@ -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"
}
}

View File

@ -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);
});

View File

@ -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);
});

View File

@ -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);
});

View File

@ -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);
});

View File

@ -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);
});

View File

@ -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);
});

View File

@ -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);
});

View File

@ -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);
});

View File

@ -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);
});

View File

@ -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);
});

View File

@ -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);
});

View File

@ -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);
});

View File

@ -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);
});

View File

@ -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);
});