github.com/Ilhicas/nomad@v1.0.4-0.20210304152020-e86851182bc3/ui/app/controllers/clients/index.js (about) 1 /* eslint-disable ember/no-incorrect-calls-with-inline-anonymous-functions */ 2 import { alias, readOnly } from '@ember/object/computed'; 3 import { inject as service } from '@ember/service'; 4 import Controller, { inject as controller } from '@ember/controller'; 5 import { action, computed } from '@ember/object'; 6 import { scheduleOnce } from '@ember/runloop'; 7 import intersection from 'lodash.intersection'; 8 import SortableFactory from 'nomad-ui/mixins/sortable-factory'; 9 import Searchable from 'nomad-ui/mixins/searchable'; 10 import { serialize, deserializedQueryParam as selection } from 'nomad-ui/utils/qp-serialize'; 11 import classic from 'ember-classic-decorator'; 12 13 @classic 14 export default class IndexController extends Controller.extend( 15 SortableFactory(['id', 'name', 'compositeStatus', 'datacenter']), 16 Searchable 17 ) { 18 @service userSettings; 19 @controller('clients') clientsController; 20 21 @alias('model.nodes') nodes; 22 @alias('model.agents') agents; 23 24 queryParams = [ 25 { 26 currentPage: 'page', 27 }, 28 { 29 searchTerm: 'search', 30 }, 31 { 32 sortProperty: 'sort', 33 }, 34 { 35 sortDescending: 'desc', 36 }, 37 { 38 qpClass: 'class', 39 }, 40 { 41 qpState: 'state', 42 }, 43 { 44 qpDatacenter: 'dc', 45 }, 46 { 47 qpVolume: 'volume', 48 }, 49 ]; 50 51 currentPage = 1; 52 @readOnly('userSettings.pageSize') pageSize; 53 54 sortProperty = 'modifyIndex'; 55 sortDescending = true; 56 57 @computed 58 get searchProps() { 59 return ['id', 'name', 'datacenter']; 60 } 61 62 qpClass = ''; 63 qpState = ''; 64 qpDatacenter = ''; 65 qpVolume = ''; 66 67 @selection('qpClass') selectionClass; 68 @selection('qpState') selectionState; 69 @selection('qpDatacenter') selectionDatacenter; 70 @selection('qpVolume') selectionVolume; 71 72 @computed('nodes.[]', 'selectionClass') 73 get optionsClass() { 74 const classes = Array.from(new Set(this.nodes.mapBy('nodeClass'))) 75 .compact() 76 .without(''); 77 78 // Remove any invalid node classes from the query param/selection 79 scheduleOnce('actions', () => { 80 // eslint-disable-next-line ember/no-side-effects 81 this.set('qpClass', serialize(intersection(classes, this.selectionClass))); 82 }); 83 84 return classes.sort().map(dc => ({ key: dc, label: dc })); 85 } 86 87 @computed 88 get optionsState() { 89 return [ 90 { key: 'initializing', label: 'Initializing' }, 91 { key: 'ready', label: 'Ready' }, 92 { key: 'down', label: 'Down' }, 93 { key: 'ineligible', label: 'Ineligible' }, 94 { key: 'draining', label: 'Draining' }, 95 ]; 96 } 97 98 @computed('nodes.[]', 'selectionDatacenter') 99 get optionsDatacenter() { 100 const datacenters = Array.from(new Set(this.nodes.mapBy('datacenter'))).compact(); 101 102 // Remove any invalid datacenters from the query param/selection 103 scheduleOnce('actions', () => { 104 // eslint-disable-next-line ember/no-side-effects 105 this.set('qpDatacenter', serialize(intersection(datacenters, this.selectionDatacenter))); 106 }); 107 108 return datacenters.sort().map(dc => ({ key: dc, label: dc })); 109 } 110 111 @computed('nodes.[]', 'selectionVolume') 112 get optionsVolume() { 113 const flatten = (acc, val) => acc.concat(val.toArray()); 114 115 const allVolumes = this.nodes.mapBy('hostVolumes').reduce(flatten, []); 116 const volumes = Array.from(new Set(allVolumes.mapBy('name'))); 117 118 scheduleOnce('actions', () => { 119 // eslint-disable-next-line ember/no-side-effects 120 this.set('qpVolume', serialize(intersection(volumes, this.selectionVolume))); 121 }); 122 123 return volumes.sort().map(volume => ({ key: volume, label: volume })); 124 } 125 126 @computed( 127 'nodes.[]', 128 'selectionClass', 129 'selectionState', 130 'selectionDatacenter', 131 'selectionVolume' 132 ) 133 get filteredNodes() { 134 const { 135 selectionClass: classes, 136 selectionState: states, 137 selectionDatacenter: datacenters, 138 selectionVolume: volumes, 139 } = this; 140 141 const onlyIneligible = states.includes('ineligible'); 142 const onlyDraining = states.includes('draining'); 143 144 // states is a composite of node status and other node states 145 const statuses = states.without('ineligible').without('draining'); 146 147 return this.nodes.filter(node => { 148 if (classes.length && !classes.includes(node.get('nodeClass'))) return false; 149 if (statuses.length && !statuses.includes(node.get('status'))) return false; 150 if (datacenters.length && !datacenters.includes(node.get('datacenter'))) return false; 151 if (volumes.length && !node.hostVolumes.find(volume => volumes.includes(volume.name))) 152 return false; 153 154 if (onlyIneligible && node.get('isEligible')) return false; 155 if (onlyDraining && !node.get('isDraining')) return false; 156 157 return true; 158 }); 159 } 160 161 @alias('filteredNodes') listToSort; 162 @alias('listSorted') listToSearch; 163 @alias('listSearched') sortedNodes; 164 165 @alias('clientsController.isForbidden') isForbidden; 166 167 setFacetQueryParam(queryParam, selection) { 168 this.set(queryParam, serialize(selection)); 169 } 170 171 @action 172 gotoNode(node) { 173 this.transitionToRoute('clients.client', node); 174 } 175 }