github.com/hernad/nomad@v1.6.112/ui/app/utils/classes/allocation-stats-tracker.js (about)

     1  /**
     2   * Copyright (c) HashiCorp, Inc.
     3   * SPDX-License-Identifier: MPL-2.0
     4   */
     5  
     6  import EmberObject, { get, computed } from '@ember/object';
     7  import { alias } from '@ember/object/computed';
     8  import RollingArray from 'nomad-ui/utils/classes/rolling-array';
     9  import AbstractStatsTracker from 'nomad-ui/utils/classes/abstract-stats-tracker';
    10  import classic from 'ember-classic-decorator';
    11  
    12  const percent = (numerator, denominator) => {
    13    if (!numerator || !denominator) {
    14      return 0;
    15    }
    16    return numerator / denominator;
    17  };
    18  
    19  const empty = (ts) => ({ timestamp: ts, used: null, percent: null });
    20  
    21  // Tasks are sorted by their lifecycle phase in this order:
    22  const sortMap = [
    23    'main',
    24    'prestart-sidecar',
    25    'poststart-sidecar',
    26    'prestart-ephemeral',
    27    'poststart-ephemeral',
    28    'poststop',
    29  ].reduce((map, phase, index) => {
    30    map[phase] = index;
    31    return map;
    32  }, {});
    33  
    34  const taskPrioritySort = (a, b) =>
    35    sortMap[a.lifecycleName] - sortMap[b.lifecycleName];
    36  
    37  // Select the value for memory usage.
    38  // Must match logic in command/alloc_status.go.
    39  const memoryUsed = (frame) =>
    40    frame.ResourceUsage.MemoryStats.RSS ||
    41    frame.ResourceUsage.MemoryStats.Usage ||
    42    0;
    43  
    44  @classic
    45  class AllocationStatsTracker extends EmberObject.extend(AbstractStatsTracker) {
    46    // Set via the stats computed property macro
    47    allocation = null;
    48  
    49    @computed('allocation.id')
    50    get url() {
    51      return `/v1/client/allocation/${this.get('allocation.id')}/stats`;
    52    }
    53  
    54    append(frame) {
    55      const timestamp = new Date(Math.floor(frame.Timestamp / 1000000));
    56  
    57      const cpuUsed = Math.floor(frame.ResourceUsage.CpuStats.TotalTicks) || 0;
    58      this.cpu.pushObject({
    59        timestamp,
    60        used: cpuUsed,
    61        percent: percent(cpuUsed, this.reservedCPU),
    62      });
    63  
    64      const memUsed = memoryUsed(frame);
    65      this.memory.pushObject({
    66        timestamp,
    67        used: memUsed,
    68        percent: percent(memUsed / 1024 / 1024, this.reservedMemory),
    69      });
    70  
    71      let aggregateCpu = 0;
    72      let aggregateMemory = 0;
    73      for (var stats of this.tasks) {
    74        const taskFrame = frame.Tasks[stats.task];
    75  
    76        // If the task is not present in the frame data (because it hasn't started or
    77        // it has already stopped), just keep going.
    78        if (!taskFrame) continue;
    79  
    80        const frameTimestamp = new Date(
    81          Math.floor(taskFrame.Timestamp / 1000000)
    82        );
    83  
    84        const taskCpuUsed =
    85          Math.floor(taskFrame.ResourceUsage.CpuStats.TotalTicks) || 0;
    86        const percentCpuTotal = percent(taskCpuUsed, this.reservedCPU);
    87        stats.cpu.pushObject({
    88          timestamp: frameTimestamp,
    89          used: taskCpuUsed,
    90          percent: percent(taskCpuUsed, stats.reservedCPU),
    91          percentTotal: percentCpuTotal,
    92          percentStack: percentCpuTotal + aggregateCpu,
    93        });
    94  
    95        const taskMemoryUsed = memoryUsed(taskFrame);
    96        const percentMemoryTotal = percent(
    97          taskMemoryUsed / 1024 / 1024,
    98          this.reservedMemory
    99        );
   100        stats.memory.pushObject({
   101          timestamp: frameTimestamp,
   102          used: taskMemoryUsed,
   103          percent: percent(taskMemoryUsed / 1024 / 1024, stats.reservedMemory),
   104          percentTotal: percentMemoryTotal,
   105          percentStack: percentMemoryTotal + aggregateMemory,
   106        });
   107  
   108        aggregateCpu += percentCpuTotal;
   109        aggregateMemory += percentMemoryTotal;
   110      }
   111    }
   112  
   113    pause() {
   114      const ts = new Date();
   115      this.memory.pushObject(empty(ts));
   116      this.cpu.pushObject(empty(ts));
   117      this.tasks.forEach((task) => {
   118        task.memory.pushObject(empty(ts));
   119        task.cpu.pushObject(empty(ts));
   120      });
   121    }
   122  
   123    // Static figures, denominators for stats
   124    @alias('allocation.taskGroup.reservedCPU') reservedCPU;
   125    @alias('allocation.taskGroup.reservedMemory') reservedMemory;
   126  
   127    // Dynamic figures, collected over time
   128    // []{ timestamp: Date, used: Number, percent: Number }
   129    @computed('allocation', 'bufferSize')
   130    get cpu() {
   131      return RollingArray(this.bufferSize);
   132    }
   133  
   134    @computed('allocation', 'bufferSize')
   135    get memory() {
   136      return RollingArray(this.bufferSize);
   137    }
   138  
   139    @computed('allocation.taskGroup.tasks', 'bufferSize')
   140    get tasks() {
   141      const bufferSize = this.bufferSize;
   142      const tasks = this.get('allocation.taskGroup.tasks') || [];
   143      return tasks
   144        .slice()
   145        .sort(taskPrioritySort)
   146        .map((task) => ({
   147          task: get(task, 'name'),
   148  
   149          // Static figures, denominators for stats
   150          reservedCPU: get(task, 'reservedCPU'),
   151          reservedMemory: get(task, 'reservedMemory'),
   152  
   153          // Dynamic figures, collected over time
   154          // []{ timestamp: Date, used: Number, percent: Number }
   155          cpu: RollingArray(bufferSize),
   156          memory: RollingArray(bufferSize),
   157        }));
   158    }
   159  }
   160  
   161  export default AllocationStatsTracker;
   162  
   163  export function stats(allocationProp, fetch) {
   164    return computed(allocationProp, function () {
   165      return AllocationStatsTracker.create({
   166        fetch: fetch.call(this),
   167        allocation: this.get(allocationProp),
   168      });
   169    });
   170  }