2021-10-07 21:11:38 +00:00
|
|
|
/* eslint-disable ember/no-incorrect-calls-with-inline-anonymous-functions */
|
|
|
|
import Controller from '@ember/controller';
|
|
|
|
import { action, computed } from '@ember/object';
|
|
|
|
import { scheduleOnce } from '@ember/runloop';
|
|
|
|
import intersection from 'lodash.intersection';
|
|
|
|
import { alias } from '@ember/object/computed';
|
|
|
|
import SortableFactory from 'nomad-ui/mixins/sortable-factory';
|
|
|
|
import Searchable from 'nomad-ui/mixins/searchable';
|
|
|
|
import WithNamespaceResetting from 'nomad-ui/mixins/with-namespace-resetting';
|
|
|
|
import jobClientStatus from 'nomad-ui/utils/properties/job-client-status';
|
2021-12-28 16:08:12 +00:00
|
|
|
import {
|
|
|
|
serialize,
|
|
|
|
deserializedQueryParam as selection,
|
|
|
|
} from 'nomad-ui/utils/qp-serialize';
|
2021-10-07 21:11:38 +00:00
|
|
|
import classic from 'ember-classic-decorator';
|
|
|
|
|
|
|
|
@classic
|
|
|
|
export default class ClientsController extends Controller.extend(
|
2021-12-28 14:45:20 +00:00
|
|
|
SortableFactory(['id', 'name', 'jobStatus']),
|
|
|
|
Searchable,
|
|
|
|
WithNamespaceResetting
|
|
|
|
) {
|
2021-10-07 21:11:38 +00:00
|
|
|
queryParams = [
|
|
|
|
{
|
|
|
|
currentPage: 'page',
|
|
|
|
},
|
|
|
|
{
|
|
|
|
searchTerm: 'search',
|
|
|
|
},
|
|
|
|
{
|
|
|
|
qpStatus: 'status',
|
|
|
|
},
|
|
|
|
{
|
|
|
|
qpDatacenter: 'dc',
|
|
|
|
},
|
|
|
|
{
|
|
|
|
qpClientClass: 'clientclass',
|
|
|
|
},
|
|
|
|
{
|
|
|
|
sortProperty: 'sort',
|
|
|
|
},
|
|
|
|
{
|
|
|
|
sortDescending: 'desc',
|
|
|
|
},
|
|
|
|
];
|
|
|
|
|
|
|
|
qpStatus = '';
|
|
|
|
qpDatacenter = '';
|
|
|
|
qpClientClass = '';
|
|
|
|
|
|
|
|
currentPage = 1;
|
|
|
|
pageSize = 25;
|
|
|
|
|
|
|
|
sortProperty = 'jobStatus';
|
|
|
|
sortDescending = false;
|
|
|
|
|
|
|
|
@selection('qpStatus') selectionStatus;
|
|
|
|
@selection('qpDatacenter') selectionDatacenter;
|
|
|
|
@selection('qpClientClass') selectionClientClass;
|
|
|
|
|
|
|
|
@alias('model') job;
|
|
|
|
@jobClientStatus('allNodes', 'job') jobClientStatus;
|
|
|
|
|
|
|
|
@alias('filteredNodes') listToSort;
|
|
|
|
@alias('listSorted') listToSearch;
|
|
|
|
@alias('listSearched') sortedClients;
|
|
|
|
|
|
|
|
@computed('store')
|
|
|
|
get allNodes() {
|
2022-01-05 17:43:51 +00:00
|
|
|
return this.store.peekAll('node');
|
2021-10-07 21:11:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
@computed('allNodes', 'jobClientStatus.byNode')
|
|
|
|
get nodes() {
|
2021-12-28 14:45:20 +00:00
|
|
|
return this.allNodes.filter((node) => this.jobClientStatus.byNode[node.id]);
|
2021-10-07 21:11:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
@computed
|
|
|
|
get searchProps() {
|
|
|
|
return ['node.id', 'node.name'];
|
|
|
|
}
|
|
|
|
|
|
|
|
@computed(
|
|
|
|
'nodes',
|
|
|
|
'job.allocations',
|
|
|
|
'jobClientStatus.byNode',
|
|
|
|
'selectionStatus',
|
|
|
|
'selectionDatacenter',
|
|
|
|
'selectionClientClass'
|
|
|
|
)
|
|
|
|
get filteredNodes() {
|
|
|
|
const {
|
|
|
|
selectionStatus: statuses,
|
|
|
|
selectionDatacenter: datacenters,
|
|
|
|
selectionClientClass: clientClasses,
|
|
|
|
} = this;
|
|
|
|
|
|
|
|
return this.nodes
|
2021-12-28 14:45:20 +00:00
|
|
|
.filter((node) => {
|
2021-12-28 16:08:12 +00:00
|
|
|
if (
|
|
|
|
statuses.length &&
|
|
|
|
!statuses.includes(this.jobClientStatus.byNode[node.id])
|
|
|
|
) {
|
2021-10-07 21:11:38 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if (datacenters.length && !datacenters.includes(node.datacenter)) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if (clientClasses.length && !clientClasses.includes(node.nodeClass)) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
})
|
2021-12-28 14:45:20 +00:00
|
|
|
.map((node) => {
|
2021-12-28 16:08:12 +00:00
|
|
|
const allocations = this.job.allocations.filter(
|
|
|
|
(alloc) => alloc.get('node.id') == node.id
|
|
|
|
);
|
2021-10-07 21:11:38 +00:00
|
|
|
|
|
|
|
return {
|
|
|
|
node,
|
|
|
|
jobStatus: this.jobClientStatus.byNode[node.id],
|
|
|
|
allocations,
|
|
|
|
createTime: eldestCreateTime(allocations),
|
|
|
|
modifyTime: mostRecentModifyTime(allocations),
|
|
|
|
};
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
@computed
|
|
|
|
get optionsJobStatus() {
|
|
|
|
return [
|
|
|
|
{ key: 'queued', label: 'Queued' },
|
|
|
|
{ key: 'notScheduled', label: 'Not Scheduled' },
|
|
|
|
{ key: 'starting', label: 'Starting' },
|
|
|
|
{ key: 'running', label: 'Running' },
|
|
|
|
{ key: 'complete', label: 'Complete' },
|
|
|
|
{ key: 'degraded', label: 'Degraded' },
|
|
|
|
{ key: 'failed', label: 'Failed' },
|
|
|
|
{ key: 'lost', label: 'Lost' },
|
|
|
|
];
|
|
|
|
}
|
|
|
|
|
|
|
|
@computed('selectionDatacenter', 'nodes')
|
|
|
|
get optionsDatacenter() {
|
2021-12-28 16:08:12 +00:00
|
|
|
const datacenters = Array.from(
|
|
|
|
new Set(this.nodes.mapBy('datacenter'))
|
|
|
|
).compact();
|
2021-10-07 21:11:38 +00:00
|
|
|
|
|
|
|
// Update query param when the list of datacenters changes.
|
|
|
|
scheduleOnce('actions', () => {
|
|
|
|
// eslint-disable-next-line ember/no-side-effects
|
2021-12-28 16:08:12 +00:00
|
|
|
this.set(
|
|
|
|
'qpDatacenter',
|
|
|
|
serialize(intersection(datacenters, this.selectionDatacenter))
|
|
|
|
);
|
2021-10-07 21:11:38 +00:00
|
|
|
});
|
|
|
|
|
2021-12-28 14:45:20 +00:00
|
|
|
return datacenters.sort().map((dc) => ({ key: dc, label: dc }));
|
2021-10-07 21:11:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
@computed('selectionClientClass', 'nodes')
|
|
|
|
get optionsClientClass() {
|
2021-12-28 16:08:12 +00:00
|
|
|
const clientClasses = Array.from(
|
|
|
|
new Set(this.nodes.mapBy('nodeClass'))
|
|
|
|
).compact();
|
2021-10-07 21:11:38 +00:00
|
|
|
|
|
|
|
// Update query param when the list of datacenters changes.
|
|
|
|
scheduleOnce('actions', () => {
|
|
|
|
// eslint-disable-next-line ember/no-side-effects
|
2021-12-28 16:08:12 +00:00
|
|
|
this.set(
|
|
|
|
'qpClientClass',
|
|
|
|
serialize(intersection(clientClasses, this.selectionClientClass))
|
|
|
|
);
|
2021-10-07 21:11:38 +00:00
|
|
|
});
|
|
|
|
|
2021-12-28 16:08:12 +00:00
|
|
|
return clientClasses
|
|
|
|
.sort()
|
|
|
|
.map((clientClass) => ({ key: clientClass, label: clientClass }));
|
2021-10-07 21:11:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
@action
|
|
|
|
gotoClient(client) {
|
|
|
|
this.transitionToRoute('clients.client', client);
|
|
|
|
}
|
|
|
|
|
|
|
|
setFacetQueryParam(queryParam, selection) {
|
|
|
|
this.set(queryParam, serialize(selection));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
function eldestCreateTime(allocations) {
|
|
|
|
let eldest = null;
|
|
|
|
for (const alloc of allocations) {
|
|
|
|
if (!eldest || alloc.createTime < eldest) {
|
|
|
|
eldest = alloc.createTime;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return eldest;
|
|
|
|
}
|
|
|
|
|
|
|
|
function mostRecentModifyTime(allocations) {
|
|
|
|
let mostRecent = null;
|
|
|
|
for (const alloc of allocations) {
|
|
|
|
if (!mostRecent || alloc.modifyTime > mostRecent) {
|
|
|
|
mostRecent = alloc.modifyTime;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return mostRecent;
|
|
|
|
}
|