github.com/hernad/nomad@v1.6.112/ui/app/components/topo-viz/node.js (about)

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