github.com/anth0d/nomad@v0.0.0-20221214183521-ae3a0a2cad06/ui/app/components/topo-viz/node.js (about) 1 import Component from '@glimmer/component'; 2 import { tracked } from '@glimmer/tracking'; 3 import { action } from '@ember/object'; 4 import { guidFor } from '@ember/object/internals'; 5 6 export default class TopoVizNode extends Component { 7 @tracked data = { cpu: [], memory: [] }; 8 @tracked dimensionsWidth = 0; 9 @tracked padding = 5; 10 @tracked activeAllocation = null; 11 12 get height() { 13 return this.args.heightScale 14 ? this.args.heightScale(this.args.node.memory) 15 : 15; 16 } 17 18 get labelHeight() { 19 return this.height / 2; 20 } 21 22 get paddingLeft() { 23 const labelWidth = 20; 24 return this.padding + labelWidth; 25 } 26 27 // Since strokes are placed centered on the perimeter of fills, The width of the stroke needs to be removed from 28 // the height of the fill to match unstroked height and avoid clipping. 29 get selectedHeight() { 30 return this.height - 1; 31 } 32 33 // Since strokes are placed centered on the perimeter of fills, half the width of the stroke needs to be added to 34 // the yOffset to match heights with unstroked shapes. 35 get selectedYOffset() { 36 return this.height + 2.5; 37 } 38 39 get yOffset() { 40 return this.height + 2; 41 } 42 43 get maskHeight() { 44 return this.height + this.yOffset; 45 } 46 47 get totalHeight() { 48 return this.maskHeight + this.padding * 2; 49 } 50 51 get maskId() { 52 return `topo-viz-node-mask-${guidFor(this)}`; 53 } 54 55 get count() { 56 return this.allocations.length; 57 } 58 59 get allocations() { 60 // Sort by the delta between memory and cpu percent. This creates the least amount of 61 // drift between the positional alignment of an alloc's cpu and memory representations. 62 return this.args.node.allocations 63 .filterBy('allocation.isScheduled') 64 .sort((a, b) => { 65 const deltaA = Math.abs(a.memoryPercent - a.cpuPercent); 66 const deltaB = Math.abs(b.memoryPercent - b.cpuPercent); 67 return deltaA - deltaB; 68 }); 69 } 70 71 @action 72 async reloadNode() { 73 if (this.args.node.isPartial) { 74 await this.args.node.reload(); 75 this.data = this.computeData(this.dimensionsWidth); 76 } 77 } 78 79 @action 80 render(svg) { 81 this.dimensionsWidth = svg.clientWidth - this.padding - this.paddingLeft; 82 this.data = this.computeData(this.dimensionsWidth); 83 } 84 85 @action 86 updateRender(svg) { 87 // Only update all data when the width changes 88 const newWidth = svg.clientWidth - this.padding - this.paddingLeft; 89 if (newWidth !== this.dimensionsWidth) { 90 this.dimensionsWidth = newWidth; 91 this.data = this.computeData(this.dimensionsWidth); 92 } 93 } 94 95 @action 96 highlightAllocation(allocation, { target }) { 97 this.activeAllocation = allocation; 98 this.args.onAllocationFocus && 99 this.args.onAllocationFocus(allocation, target); 100 } 101 102 @action 103 allocationBlur() { 104 this.args.onAllocationBlur && this.args.onAllocationBlur(); 105 } 106 107 @action 108 clearHighlight() { 109 this.activeAllocation = null; 110 } 111 112 @action 113 selectNode() { 114 if (this.args.isDense && this.args.onNodeSelect) { 115 this.args.onNodeSelect(this.args.node.isSelected ? null : this.args.node); 116 } 117 } 118 119 @action 120 selectAllocation(allocation) { 121 if (this.args.onAllocationSelect) this.args.onAllocationSelect(allocation); 122 } 123 124 containsActiveTaskGroup() { 125 return this.args.node.allocations.some( 126 (allocation) => 127 allocation.taskGroupName === this.args.activeTaskGroup && 128 allocation.belongsTo('job').id() === this.args.activeJobId 129 ); 130 } 131 132 computeData(width) { 133 const allocations = this.allocations; 134 let cpuOffset = 0; 135 let memoryOffset = 0; 136 137 const cpu = []; 138 const memory = []; 139 for (const allocation of allocations) { 140 const { cpuPercent, memoryPercent, isSelected } = allocation; 141 const isFirst = allocation === allocations[0]; 142 143 let cpuWidth = cpuPercent * width - 1; 144 let memoryWidth = memoryPercent * width - 1; 145 if (isFirst) { 146 cpuWidth += 0.5; 147 memoryWidth += 0.5; 148 } 149 if (isSelected) { 150 cpuWidth--; 151 memoryWidth--; 152 } 153 154 cpu.push({ 155 allocation, 156 offset: cpuOffset * 100, 157 percent: cpuPercent * 100, 158 width: Math.max(cpuWidth, 0), 159 x: cpuOffset * width + (isFirst ? 0 : 0.5) + (isSelected ? 0.5 : 0), 160 className: allocation.allocation.clientStatus, 161 }); 162 memory.push({ 163 allocation, 164 offset: memoryOffset * 100, 165 percent: memoryPercent * 100, 166 width: Math.max(memoryWidth, 0), 167 x: memoryOffset * width + (isFirst ? 0 : 0.5) + (isSelected ? 0.5 : 0), 168 className: allocation.allocation.clientStatus, 169 }); 170 171 cpuOffset += cpuPercent; 172 memoryOffset += memoryPercent; 173 } 174 175 const cpuRemainder = { 176 x: cpuOffset * width + 0.5, 177 width: Math.max(width - cpuOffset * width, 0), 178 }; 179 const memoryRemainder = { 180 x: memoryOffset * width + 0.5, 181 width: Math.max(width - memoryOffset * width, 0), 182 }; 183 184 return { 185 cpu, 186 memory, 187 cpuRemainder, 188 memoryRemainder, 189 cpuLabel: { x: -this.paddingLeft / 2, y: this.height / 2 + this.yOffset }, 190 memoryLabel: { x: -this.paddingLeft / 2, y: this.height / 2 }, 191 }; 192 } 193 }