github.com/hernad/nomad@v1.6.112/ui/app/controllers/jobs/job/clients.js (about)

     1  /**
     2   * Copyright (c) HashiCorp, Inc.
     3   * SPDX-License-Identifier: MPL-2.0
     4   */
     5  
     6  /* eslint-disable ember/no-incorrect-calls-with-inline-anonymous-functions */
     7  import Controller from '@ember/controller';
     8  import { action, computed } from '@ember/object';
     9  import { scheduleOnce } from '@ember/runloop';
    10  import intersection from 'lodash.intersection';
    11  import { alias } from '@ember/object/computed';
    12  import SortableFactory from 'nomad-ui/mixins/sortable-factory';
    13  import Searchable from 'nomad-ui/mixins/searchable';
    14  import WithNamespaceResetting from 'nomad-ui/mixins/with-namespace-resetting';
    15  import jobClientStatus from 'nomad-ui/utils/properties/job-client-status';
    16  import {
    17    serialize,
    18    deserializedQueryParam as selection,
    19  } from 'nomad-ui/utils/qp-serialize';
    20  import classic from 'ember-classic-decorator';
    21  import { inject as service } from '@ember/service';
    22  
    23  @classic
    24  export default class ClientsController extends Controller.extend(
    25    SortableFactory(['id', 'name', 'jobStatus']),
    26    Searchable,
    27    WithNamespaceResetting
    28  ) {
    29    @service store;
    30  
    31    queryParams = [
    32      {
    33        currentPage: 'page',
    34      },
    35      {
    36        searchTerm: 'search',
    37      },
    38      {
    39        qpStatus: 'status',
    40      },
    41      {
    42        qpDatacenter: 'dc',
    43      },
    44      {
    45        qpClientClass: 'clientclass',
    46      },
    47      {
    48        sortProperty: 'sort',
    49      },
    50      {
    51        sortDescending: 'desc',
    52      },
    53    ];
    54  
    55    qpStatus = '';
    56    qpDatacenter = '';
    57    qpClientClass = '';
    58  
    59    currentPage = 1;
    60    pageSize = 25;
    61  
    62    sortProperty = 'jobStatus';
    63    sortDescending = false;
    64  
    65    @selection('qpStatus') selectionStatus;
    66    @selection('qpDatacenter') selectionDatacenter;
    67    @selection('qpClientClass') selectionClientClass;
    68  
    69    @alias('model') job;
    70    @jobClientStatus('allNodes', 'job') jobClientStatus;
    71  
    72    @alias('filteredNodes') listToSort;
    73    @alias('listSorted') listToSearch;
    74    @alias('listSearched') sortedClients;
    75  
    76    @computed('store')
    77    get allNodes() {
    78      return this.store.peekAll('node');
    79    }
    80  
    81    @computed('allNodes', 'jobClientStatus.byNode')
    82    get nodes() {
    83      return this.allNodes.filter((node) => this.jobClientStatus.byNode[node.id]);
    84    }
    85  
    86    @computed
    87    get searchProps() {
    88      return ['node.id', 'node.name'];
    89    }
    90  
    91    @computed(
    92      'nodes',
    93      'job.allocations',
    94      'jobClientStatus.byNode',
    95      'selectionStatus',
    96      'selectionDatacenter',
    97      'selectionClientClass'
    98    )
    99    get filteredNodes() {
   100      const {
   101        selectionStatus: statuses,
   102        selectionDatacenter: datacenters,
   103        selectionClientClass: clientClasses,
   104      } = this;
   105  
   106      return this.nodes
   107        .filter((node) => {
   108          if (
   109            statuses.length &&
   110            !statuses.includes(this.jobClientStatus.byNode[node.id])
   111          ) {
   112            return false;
   113          }
   114          if (datacenters.length && !datacenters.includes(node.datacenter)) {
   115            return false;
   116          }
   117          if (clientClasses.length && !clientClasses.includes(node.nodeClass)) {
   118            return false;
   119          }
   120  
   121          return true;
   122        })
   123        .map((node) => {
   124          const allocations = this.job.allocations.filter(
   125            (alloc) => alloc.get('node.id') == node.id
   126          );
   127  
   128          return {
   129            node,
   130            jobStatus: this.jobClientStatus.byNode[node.id],
   131            allocations,
   132            createTime: eldestCreateTime(allocations),
   133            modifyTime: mostRecentModifyTime(allocations),
   134          };
   135        });
   136    }
   137  
   138    @computed
   139    get optionsJobStatus() {
   140      return [
   141        { key: 'queued', label: 'Queued' },
   142        { key: 'notScheduled', label: 'Not Scheduled' },
   143        { key: 'starting', label: 'Starting' },
   144        { key: 'running', label: 'Running' },
   145        { key: 'complete', label: 'Complete' },
   146        { key: 'degraded', label: 'Degraded' },
   147        { key: 'failed', label: 'Failed' },
   148        { key: 'lost', label: 'Lost' },
   149        { key: 'unknown', label: 'Unknown' },
   150      ];
   151    }
   152  
   153    @computed('selectionDatacenter', 'nodes')
   154    get optionsDatacenter() {
   155      const datacenters = Array.from(
   156        new Set(this.nodes.mapBy('datacenter'))
   157      ).compact();
   158  
   159      // Update query param when the list of datacenters changes.
   160      scheduleOnce('actions', () => {
   161        // eslint-disable-next-line ember/no-side-effects
   162        this.set(
   163          'qpDatacenter',
   164          serialize(intersection(datacenters, this.selectionDatacenter))
   165        );
   166      });
   167  
   168      return datacenters.sort().map((dc) => ({ key: dc, label: dc }));
   169    }
   170  
   171    @computed('selectionClientClass', 'nodes')
   172    get optionsClientClass() {
   173      const clientClasses = Array.from(
   174        new Set(this.nodes.mapBy('nodeClass'))
   175      ).compact();
   176  
   177      // Update query param when the list of datacenters changes.
   178      scheduleOnce('actions', () => {
   179        // eslint-disable-next-line ember/no-side-effects
   180        this.set(
   181          'qpClientClass',
   182          serialize(intersection(clientClasses, this.selectionClientClass))
   183        );
   184      });
   185  
   186      return clientClasses
   187        .sort()
   188        .map((clientClass) => ({ key: clientClass, label: clientClass }));
   189    }
   190  
   191    @action
   192    gotoClient(client) {
   193      this.transitionToRoute('clients.client', client);
   194    }
   195  
   196    setFacetQueryParam(queryParam, selection) {
   197      this.set(queryParam, serialize(selection));
   198    }
   199  }
   200  
   201  function eldestCreateTime(allocations) {
   202    let eldest = null;
   203    for (const alloc of allocations) {
   204      if (!eldest || alloc.createTime < eldest) {
   205        eldest = alloc.createTime;
   206      }
   207    }
   208    return eldest;
   209  }
   210  
   211  function mostRecentModifyTime(allocations) {
   212    let mostRecent = null;
   213    for (const alloc of allocations) {
   214      if (!mostRecent || alloc.modifyTime > mostRecent) {
   215        mostRecent = alloc.modifyTime;
   216      }
   217    }
   218    return mostRecent;
   219  }