Make the topo viz denser when there are >50 nodes
By hiding node details and making nodes interactive instead, we can pack more allocations on a screen.
This commit is contained in:
parent
6e55d8a6eb
commit
066502d408
|
@ -13,6 +13,7 @@ export default class TopoViz extends Component {
|
|||
@tracked element = null;
|
||||
@tracked topology = { datacenters: [] };
|
||||
|
||||
@tracked activeNode = null;
|
||||
@tracked activeAllocation = null;
|
||||
@tracked activeEdges = [];
|
||||
|
||||
|
@ -36,6 +37,12 @@ export default class TopoViz extends Component {
|
|||
return !this.isSingleColumn || (this.isSingleColumn && this.args.nodes.length <= 20);
|
||||
}
|
||||
|
||||
// Once a cluster is large enough, the exact details of a node are
|
||||
// typically irrelevant and a waste of space.
|
||||
get isDense() {
|
||||
return this.args.nodes.length > 50;
|
||||
}
|
||||
|
||||
dataForNode(node) {
|
||||
return {
|
||||
node,
|
||||
|
@ -135,6 +142,21 @@ export default class TopoViz extends Component {
|
|||
this.element = element;
|
||||
}
|
||||
|
||||
@action
|
||||
showNodeDetails(node) {
|
||||
if (this.activeNode) {
|
||||
set(this.activeNode, 'isSelected', false);
|
||||
}
|
||||
|
||||
this.activeNode = this.activeNode === node ? null : node;
|
||||
|
||||
if (this.activeNode) {
|
||||
set(this.activeNode, 'isSelected', true);
|
||||
}
|
||||
|
||||
if (this.args.onNodeSelect) this.args.onNodeSelect(this.activeNode);
|
||||
}
|
||||
|
||||
@action
|
||||
associateAllocations(allocation) {
|
||||
if (this.activeAllocation === allocation) {
|
||||
|
@ -151,6 +173,10 @@ export default class TopoViz extends Component {
|
|||
set(this.topology, 'selectedKey', null);
|
||||
}
|
||||
} else {
|
||||
if (this.activeNode) {
|
||||
set(this.activeNode, 'isSelected', false);
|
||||
}
|
||||
this.activeNode = null;
|
||||
this.activeAllocation = allocation;
|
||||
const selectedAllocations = this.topology.allocationIndex[this.topology.selectedKey];
|
||||
if (selectedAllocations) {
|
||||
|
@ -171,6 +197,7 @@ export default class TopoViz extends Component {
|
|||
}
|
||||
if (this.args.onAllocationSelect)
|
||||
this.args.onAllocationSelect(this.activeAllocation && this.activeAllocation.allocation);
|
||||
if (this.args.onNodeSelect) this.args.onNodeSelect(this.activeNode);
|
||||
}
|
||||
|
||||
@action
|
||||
|
|
|
@ -98,6 +98,13 @@ export default class TopoVizNode extends Component {
|
|||
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);
|
||||
|
|
|
@ -68,6 +68,31 @@ export default class TopologyControllers extends Controller {
|
|||
});
|
||||
}
|
||||
|
||||
@computed('activeNode')
|
||||
get nodeUtilization() {
|
||||
const node = this.activeNode;
|
||||
const [formattedMemory, memoryUnits] = reduceToLargestUnit(node.memory * 1024 * 1024);
|
||||
const totalReservedMemory = node.allocations
|
||||
.mapBy('memory')
|
||||
.reduce((sum, memory) => sum + (memory || 0), 0);
|
||||
const totalReservedCPU = node.allocations
|
||||
.mapBy('cpu')
|
||||
.reduce((sum, cpu) => sum + (cpu || 0), 0);
|
||||
|
||||
return {
|
||||
totalMemoryFormatted: formattedMemory.toFixed(2),
|
||||
totalMemoryUnits: memoryUnits,
|
||||
|
||||
totalMemory: node.memory * 1024 * 1024,
|
||||
totalReservedMemory: totalReservedMemory * 1024 * 1024,
|
||||
reservedMemoryPercent: totalReservedMemory / node.memory,
|
||||
|
||||
totalCPU: node.cpu,
|
||||
totalReservedCPU,
|
||||
reservedCPUPercent: totalReservedCPU / node.cpu,
|
||||
};
|
||||
}
|
||||
|
||||
@computed('siblingAllocations.@each.node')
|
||||
get uniqueActiveAllocationNodes() {
|
||||
return this.siblingAllocations.mapBy('node').uniq();
|
||||
|
@ -80,4 +105,9 @@ export default class TopologyControllers extends Controller {
|
|||
allocation.reload();
|
||||
}
|
||||
}
|
||||
|
||||
@action
|
||||
setNode(node) {
|
||||
this.set('activeNode', node);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,6 +11,17 @@
|
|||
fill: $white-ter;
|
||||
stroke-width: 1;
|
||||
stroke: $grey-lighter;
|
||||
|
||||
&.is-interactive:hover {
|
||||
fill: $white;
|
||||
stroke: $grey-light;
|
||||
}
|
||||
|
||||
&.is-selected,
|
||||
&.is-selected:hover {
|
||||
fill: $white;
|
||||
stroke: $grey;
|
||||
}
|
||||
}
|
||||
|
||||
.dimension-background {
|
||||
|
@ -42,6 +53,7 @@
|
|||
alignment-baseline: central;
|
||||
font-weight: $weight-normal;
|
||||
fill: $grey;
|
||||
pointer-events: none;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -10,8 +10,10 @@
|
|||
<TopoViz::Datacenter
|
||||
@datacenter={{dc}}
|
||||
@isSingleColumn={{this.datacenterIsSingleColumn}}
|
||||
@isDense={{this.isDense}}
|
||||
@heightScale={{this.topology.heightScale}}
|
||||
@onAllocationSelect={{this.associateAllocations}}
|
||||
@onNodeSelect={{this.showNodeDetails}}
|
||||
@onLoad={{action reflow}}/>
|
||||
</FlexMasonry>
|
||||
|
||||
|
|
|
@ -10,8 +10,10 @@
|
|||
<FlexMasonry @columns={{if @isSingleColumn 1 2}} @items={{@datacenter.nodes}} as |node|>
|
||||
<TopoViz::Node
|
||||
@node={{node}}
|
||||
@isDense={{@isDense}}
|
||||
@heightScale={{@heightScale}}
|
||||
@onAllocationSelect={{@onAllocationSelect}} />
|
||||
@onAllocationSelect={{@onAllocationSelect}}
|
||||
@onNodeSelect={{@onNodeSelect}}/>
|
||||
</FlexMasonry>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -1,16 +1,18 @@
|
|||
<div class="chart topo-viz-node {{unless this.allocations.length "is-empty"}}" {{did-insert this.reloadNode}}>
|
||||
<p>
|
||||
<strong>{{@node.node.name}}</strong>
|
||||
<span class="bumper-left">{{this.count}} Allocs</span>
|
||||
<span class="bumper-left is-faded">{{@node.memory}} MiB, {{@node.cpu}} Mhz</span>
|
||||
</p>
|
||||
{{#unless @isDense}}
|
||||
<p>
|
||||
<strong>{{@node.node.name}}</strong>
|
||||
<span class="bumper-left">{{this.count}} Allocs</span>
|
||||
<span class="bumper-left is-faded">{{@node.memory}} MiB, {{@node.cpu}} Mhz</span>
|
||||
</p>
|
||||
{{/unless}}
|
||||
<svg class="chart" height="{{this.totalHeight}}px" {{did-insert this.render}} {{did-update this.updateRender}} {{window-resize this.render}}>
|
||||
<defs>
|
||||
<clipPath id="{{this.maskId}}">
|
||||
<rect class="mask" x="0" y="0" width="{{this.dimensionsWidth}}px" height="{{this.maskHeight}}px" rx="2px" ry="2px" />
|
||||
</clipPath>
|
||||
</defs>
|
||||
<rect class="node-background" width="100%" height="{{this.totalHeight}}px" rx="2px" ry="2px" />
|
||||
<rect class="node-background {{if @node.isSelected "is-selected"}} {{if @isDense "is-interactive"}}" width="100%" height="{{this.totalHeight}}px" rx="2px" ry="2px" {{on "click" this.selectNode}} />
|
||||
{{#if this.allocations.length}}
|
||||
<g
|
||||
class="dimensions {{if this.activeAllocation "is-active"}}"
|
||||
|
|
|
@ -24,9 +24,67 @@
|
|||
</div>
|
||||
</div>
|
||||
<div class="boxed-section">
|
||||
<div class="boxed-section-head">{{if this.activeAllocation "Allocation" "Cluster"}} Details</div>
|
||||
<div class="boxed-section-head">
|
||||
{{#if this.activeNode}}Client{{else if this.activeAllocation}}Allocation{{else}}Cluster{{/if}} Details
|
||||
</div>
|
||||
<div class="boxed-section-body">
|
||||
{{#if this.activeAllocation}}
|
||||
{{#if this.activeNode}}
|
||||
<div class="dashboard-metric">
|
||||
<p class="metric">{{this.activeNode.allocations.length}} <span class="metric-label">Allocations</span></p>
|
||||
</div>
|
||||
<div class="dashboard-metric">
|
||||
<h3 class="pair">
|
||||
<strong>Client:</strong>
|
||||
<LinkTo @route="clients.client" @model={{this.activeNode.node}}>
|
||||
{{this.activeNode.node.shortId}}
|
||||
</LinkTo>
|
||||
</h3>
|
||||
<p><strong>Name:</strong> {{this.activeNode.node.name}}</p>
|
||||
<p><strong>Address:</strong> {{this.activeNode.node.httpAddr}}</p>
|
||||
</div>
|
||||
<div class="dashboard-metric with-divider">
|
||||
<p class="metric">{{this.nodeUtilization.totalMemoryFormatted}} <span class="metric-units">{{this.nodeUtilization.totalMemoryUnits}}</span> <span class="metric-label">of memory</span></p>
|
||||
<div class="columns graphic">
|
||||
<div class="column">
|
||||
<div class="inline-chart" data-test-percentage-bar>
|
||||
<progress
|
||||
class="progress is-danger is-small"
|
||||
value="{{this.nodeUtilization.reservedMemoryPercent}}"
|
||||
max="1">
|
||||
{{this.nodeUtilization.reservedMemoryPercent}}
|
||||
</progress>
|
||||
</div>
|
||||
</div>
|
||||
<div class="column is-minimum">
|
||||
<span class="nowrap" data-test-percentage>{{format-percentage this.nodeUtilization.reservedMemoryPercent total=1}}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="annotation" data-test-absolute-value>
|
||||
<strong>{{format-bytes this.nodeUtilization.totalReservedMemory}}</strong> / {{format-bytes this.nodeUtilization.totalMemory}} reserved
|
||||
</div>
|
||||
</div>
|
||||
<div class="dashboard-metric">
|
||||
<p class="metric">{{this.nodeUtilization.totalCPU}} <span class="metric-units">Mhz</span> <span class="metric-label">of CPU</span></p>
|
||||
<div class="columns graphic">
|
||||
<div class="column">
|
||||
<div class="inline-chart" data-test-percentage-bar>
|
||||
<progress
|
||||
class="progress is-info is-small"
|
||||
value="{{this.nodeUtilization.reservedCPUPercent}}"
|
||||
max="1">
|
||||
{{this.nodeUtilization.reservedCPUPercent}}
|
||||
</progress>
|
||||
</div>
|
||||
</div>
|
||||
<div class="column is-minimum">
|
||||
<span class="nowrap" data-test-percentage>{{format-percentage this.nodeUtilization.reservedCPUPercent total=1}}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="annotation" data-test-absolute-value>
|
||||
<strong>{{this.nodeUtilization.totalReservedCPU}} Mhz</strong> / {{this.nodeUtilization.totalCPU}} Mhz reserved
|
||||
</div>
|
||||
</div>
|
||||
{{else if this.activeAllocation}}
|
||||
<div class="dashboard-metric">
|
||||
<h3 class="pair">
|
||||
<strong>Allocation:</strong>
|
||||
|
@ -121,7 +179,11 @@
|
|||
</div>
|
||||
</div>
|
||||
<div class="column">
|
||||
<TopoViz @nodes={{this.model.nodes}} @allocations={{this.model.allocations}} @onAllocationSelect={{action this.setAllocation}} />
|
||||
<TopoViz
|
||||
@nodes={{this.model.nodes}}
|
||||
@allocations={{this.model.allocations}}
|
||||
@onAllocationSelect={{action this.setAllocation}}
|
||||
@onNodeSelect={{action this.setNode}} />
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
|
Loading…
Reference in New Issue