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  }