blob: 7eac266505acadd537ee8e5d91f8e532077c0b09 [file] [log] [blame]
/**
* 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';
export default BaseChartComponent.extend({
CELL_WIDTH: 250,
SAMPLE_CELL_WIDTH: 100,
SAMPLE_HEIGHT: 30,
CELL_HEIGHT: 30,
CELL_MARGIN: 2,
RACK_MARGIN: 20,
filter: "",
selectedCategory: 0,
memoryLabel: "Memory",
cpuLabel: "VCores",
containersLabel: "Containers",
totalContainers: 0,
bindTP: function(element, cell) {
var currentToolTip = this.tooltip;
element.on("mouseover", function() {
currentToolTip
.style("left", (d3.event.pageX) + "px")
.style("top", (d3.event.pageY - 28) + "px");
cell.style("opacity", 1.0);
}.bind(this))
.on("mousemove", function() {
// Handle pie chart case
var text = cell.attr("tooltiptext");
currentToolTip
.style("background", "black")
.style("opacity", 0.7);
currentToolTip
.html(text)
.style('font-size', '12px')
.style('color', 'white')
.style('font-weight', '400');
currentToolTip
.style("left", (d3.event.pageX) + "px")
.style("top", (d3.event.pageY - 28) + "px");
}.bind(this))
.on("mouseout", function() {
currentToolTip.style("opacity", 0);
cell.style("opacity", 0.8);
}.bind(this));
},
bindSelectCategory: function(element, i) {
element.on("click", function() {
if (this.selectedCategory === i) {
// Remove selection for second click
this.selectedCategory = 0;
} else {
this.selectedCategory = i;
}
this.didInsertElement();
}.bind(this));
},
isNodeSelected: function(node) {
if (this.filter) {
var rack = node.get("rack");
var host = node.get("nodeHostName");
if (!rack.includes(this.filter) && !host.includes(this.filter)) {
return false;
}
}
if (this.selectedCategory === 0) {
return true;
}
var usage = this.calcUsage(node);
var lowerLimit = (this.selectedCategory - 1) * 0.2;
var upperLimit = this.selectedCategory * 0.2;
if (lowerLimit <= usage && usage <= upperLimit) {
return true;
}
return false;
},
// data:
// [{label=label1, value=value1}, ...]
// ...
renderCells: function (model, title) {
var selectedOption = d3.select("select").property("value");
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 gradientStartColor = "#60cea5";
var gradientEndColor = "#ffbc0b";
var colorFunc = d3.interpolateRgb(d3.rgb(gradientStartColor), d3.rgb(gradientEndColor));
var sampleXOffset = (layout.x2 - layout.x1) / 2 - 2.5 * this.SAMPLE_CELL_WIDTH -
2 * this.CELL_MARGIN;
var sampleYOffset = layout.margin * 2;
var text;
for (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", this.selectedCategory === i ? "#26bbf0" : colorFunc(ratio))
.attr("width", this.SAMPLE_CELL_WIDTH)
.attr("height", this.SAMPLE_HEIGHT)
.attr("class", "hyperlink");
this.bindSelectCategory(rect, i);
text = 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 hyperlink");
this.bindSelectCategory(text, i);
sampleXOffset += this.CELL_MARGIN + this.SAMPLE_CELL_WIDTH;
}
if (this.selectedCategory !== 0) {
text = g.append("text")
.text("Clear")
.attr("y", sampleYOffset + this.SAMPLE_HEIGHT / 2 + 5)
.attr("x", sampleXOffset + 20)
.attr("class", "heatmap-clear hyperlink");
this.bindSelectCategory(text, 0);
}
var chartXOffset = -1;
this.totalContainers = 0;
for (i = 0; i < racksArray.length; i++) {
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");
if (rack === racksArray[i]) {
this.totalContainers += data[j].get("numContainers");
this.addNode(g, xOffset, yOffset, colorFunc, data[j]);
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 + selectedOption + ")" , layout, false);
},
addNode: function (g, xOffset, yOffset, colorFunc, data) {
var rect = g.append("rect")
.attr("y", yOffset)
.attr("x", xOffset)
.attr("height", this.CELL_HEIGHT)
.attr("fill", colorFunc(this.calcUsage(data)))
.attr("width", this.CELL_WIDTH)
.attr("tooltiptext", data.get("toolTipText") + this.getToolTipText(data));
if (this.isNodeSelected(data)) {
rect.style("opacity", 0.8);
this.bindTP(rect, rect);
} else {
rect.style("opacity", 0.8);
rect.attr("fill", "DimGray");
}
var node_id = data.get("id"),
node_addr = encodeURIComponent(data.get("nodeHTTPAddress")),
href = `#/yarn-node/${node_id}/${node_addr}/info`;
var nodeHostName = data.get("nodeHostName");
var a = g.append("a")
.attr("href", href);
a.append("text")
.text(nodeHostName.length > 30 ? nodeHostName.substr(0, 30) + '...' : nodeHostName)
.attr("y", yOffset + this.CELL_HEIGHT / 2 + 5)
.attr("x", nodeHostName.length > 30 ? xOffset + 10 : xOffset + this.CELL_WIDTH / 2)
.style("text-anchor", nodeHostName.length > 30 ? "start" : "middle")
.attr("class", this.isNodeSelected(data) ? "heatmap-cell" : "heatmap-cell-notselected");
if (this.isNodeSelected(data)) {
this.bindTP(a, rect);
}
},
addPlaceholderNode: function(g, xOffset, yOffset) {
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 () {
var self = this;
var optionsData = [this.memoryLabel, this.cpuLabel, this.containersLabel];
d3.select("#heatmap-select")
.on('change', function() {
self.renderCells(self.get("model"), self.get("title"), self.get("textWidth"));
})
.selectAll('option')
.data(optionsData).enter()
.append('option')
.text(function (d) { return d; });
this.draw();
},
actions: {
applyFilter: function(event) {
this.filter = event.srcElement.value;
this.selectedCategory = 0;
this.didInsertElement();
}
},
calcUsage: function(data) {
var selectedOption = d3.select('select').property("value");
if (selectedOption === this.memoryLabel) {
return data.get("usedMemoryMB") /
(data.get("usedMemoryMB") + data.get("availMemoryMB"));
}
else if (selectedOption === this.cpuLabel) {
return data.get("usedVirtualCores") /
(data.get("usedVirtualCores") + data.get("availableVirtualCores"));
}
else if (selectedOption === this.containersLabel) {
var totalContainers = this.totalContainers;
if (totalContainers === 0) { return 0; }
return data.get("numContainers") / totalContainers;
}
},
getToolTipText: function(data) {
var selectedOption = d3.select('select').property("value");
if (selectedOption === this.memoryLabel) {
return "<p>Used Memory: " + Math.round(data.get("usedMemoryMB")) + " MB</p>" +
"<p>Available Memory: " + Math.round(data.get("availMemoryMB")) + " MB</p>";
}
else if (selectedOption === this.cpuLabel) {
return "<p>Used VCores: " + Math.round(data.get("usedVirtualCores")) + " VCores</p>" +
"<p>Available VCores: " + Math.round(data.get("availableVirtualCores")) + " VCores</p>";
}
else if (selectedOption === this.containersLabel) {
return "<p>Containers: " + Math.round(data.get("numContainers")) + " Containers</p>" +
"<p>Total Containers: " + this.totalContainers + " Containers</p>";
}
}
});