open-nomad/ui/app/components/topo-viz/node.js
2022-01-20 09:54:56 -05:00

194 lines
5.1 KiB
JavaScript

import Component from '@glimmer/component';
import { tracked } from '@glimmer/tracking';
import { action } from '@ember/object';
import { guidFor } from '@ember/object/internals';
export default class TopoVizNode extends Component {
@tracked data = { cpu: [], memory: [] };
@tracked dimensionsWidth = 0;
@tracked padding = 5;
@tracked activeAllocation = null;
get height() {
return this.args.heightScale
? this.args.heightScale(this.args.node.memory)
: 15;
}
get labelHeight() {
return this.height / 2;
}
get paddingLeft() {
const labelWidth = 20;
return this.padding + labelWidth;
}
// Since strokes are placed centered on the perimeter of fills, The width of the stroke needs to be removed from
// the height of the fill to match unstroked height and avoid clipping.
get selectedHeight() {
return this.height - 1;
}
// Since strokes are placed centered on the perimeter of fills, half the width of the stroke needs to be added to
// the yOffset to match heights with unstroked shapes.
get selectedYOffset() {
return this.height + 2.5;
}
get yOffset() {
return this.height + 2;
}
get maskHeight() {
return this.height + this.yOffset;
}
get totalHeight() {
return this.maskHeight + this.padding * 2;
}
get maskId() {
return `topo-viz-node-mask-${guidFor(this)}`;
}
get count() {
return this.allocations.length;
}
get allocations() {
// Sort by the delta between memory and cpu percent. This creates the least amount of
// drift between the positional alignment of an alloc's cpu and memory representations.
return this.args.node.allocations
.filterBy('allocation.isScheduled')
.sort((a, b) => {
const deltaA = Math.abs(a.memoryPercent - a.cpuPercent);
const deltaB = Math.abs(b.memoryPercent - b.cpuPercent);
return deltaA - deltaB;
});
}
@action
async reloadNode() {
if (this.args.node.isPartial) {
await this.args.node.reload();
this.data = this.computeData(this.dimensionsWidth);
}
}
@action
render(svg) {
this.dimensionsWidth = svg.clientWidth - this.padding - this.paddingLeft;
this.data = this.computeData(this.dimensionsWidth);
}
@action
updateRender(svg) {
// Only update all data when the width changes
const newWidth = svg.clientWidth - this.padding - this.paddingLeft;
if (newWidth !== this.dimensionsWidth) {
this.dimensionsWidth = newWidth;
this.data = this.computeData(this.dimensionsWidth);
}
}
@action
highlightAllocation(allocation, { target }) {
this.activeAllocation = allocation;
this.args.onAllocationFocus &&
this.args.onAllocationFocus(allocation, target);
}
@action
allocationBlur() {
this.args.onAllocationBlur && this.args.onAllocationBlur();
}
@action
clearHighlight() {
this.activeAllocation = null;
}
@action
selectNode() {
if (this.args.isDense && this.args.onNodeSelect) {
this.args.onNodeSelect(this.args.node.isSelected ? null : this.args.node);
}
}
@action
selectAllocation(allocation) {
if (this.args.onAllocationSelect) this.args.onAllocationSelect(allocation);
}
containsActiveTaskGroup() {
return this.args.node.allocations.some(
(allocation) =>
allocation.taskGroupName === this.args.activeTaskGroup &&
allocation.belongsTo('job').id() === this.args.activeJobId
);
}
computeData(width) {
const allocations = this.allocations;
let cpuOffset = 0;
let memoryOffset = 0;
const cpu = [];
const memory = [];
for (const allocation of allocations) {
const { cpuPercent, memoryPercent, isSelected } = allocation;
const isFirst = allocation === allocations[0];
let cpuWidth = cpuPercent * width - 1;
let memoryWidth = memoryPercent * width - 1;
if (isFirst) {
cpuWidth += 0.5;
memoryWidth += 0.5;
}
if (isSelected) {
cpuWidth--;
memoryWidth--;
}
cpu.push({
allocation,
offset: cpuOffset * 100,
percent: cpuPercent * 100,
width: Math.max(cpuWidth, 0),
x: cpuOffset * width + (isFirst ? 0 : 0.5) + (isSelected ? 0.5 : 0),
className: allocation.allocation.clientStatus,
});
memory.push({
allocation,
offset: memoryOffset * 100,
percent: memoryPercent * 100,
width: Math.max(memoryWidth, 0),
x: memoryOffset * width + (isFirst ? 0 : 0.5) + (isSelected ? 0.5 : 0),
className: allocation.allocation.clientStatus,
});
cpuOffset += cpuPercent;
memoryOffset += memoryPercent;
}
const cpuRemainder = {
x: cpuOffset * width + 0.5,
width: Math.max(width - cpuOffset * width, 0),
};
const memoryRemainder = {
x: memoryOffset * width + 0.5,
width: Math.max(width - memoryOffset * width, 0),
};
return {
cpu,
memory,
cpuRemainder,
memoryRemainder,
cpuLabel: { x: -this.paddingLeft / 2, y: this.height / 2 + this.yOffset },
memoryLabel: { x: -this.paddingLeft / 2, y: this.height / 2 },
};
}
}