github.com/anth0d/nomad@v0.0.0-20221214183521-ae3a0a2cad06/ui/app/models/node.js (about) 1 import { computed } from '@ember/object'; 2 import { equal } from '@ember/object/computed'; 3 import Model from '@ember-data/model'; 4 import { attr } from '@ember-data/model'; 5 import { hasMany } from '@ember-data/model'; 6 import { fragment, fragmentArray } from 'ember-data-model-fragments/attributes'; 7 import RSVP from 'rsvp'; 8 import shortUUIDProperty from '../utils/properties/short-uuid'; 9 import ipParts from '../utils/ip-parts'; 10 import classic from 'ember-classic-decorator'; 11 12 @classic 13 export default class Node extends Model { 14 // Available from list response 15 @attr('string') name; 16 @attr('string') datacenter; 17 @attr('string') nodeClass; 18 @attr('boolean') isDraining; 19 @attr('string') schedulingEligibility; 20 @attr('string') status; 21 @attr('string') statusDescription; 22 @shortUUIDProperty('id') shortId; 23 @attr('number') modifyIndex; 24 @attr('string') version; 25 26 // Available from single response 27 @attr('string') httpAddr; 28 @attr('boolean') tlsEnabled; 29 @fragment('structured-attributes') attributes; 30 @fragment('structured-attributes') meta; 31 @fragment('resources') resources; 32 @fragment('resources') reserved; 33 @fragment('drain-strategy') drainStrategy; 34 35 @equal('schedulingEligibility', 'eligible') isEligible; 36 37 @computed('httpAddr') 38 get address() { 39 return ipParts(this.httpAddr).address; 40 } 41 42 @computed('httpAddr') 43 get port() { 44 return ipParts(this.httpAddr).port; 45 } 46 47 @computed('httpAddr') 48 get isPartial() { 49 return this.httpAddr == null; 50 } 51 52 @hasMany('allocations', { inverse: 'node' }) allocations; 53 54 @computed('allocations.@each.clientStatus') 55 get completeAllocations() { 56 return this.allocations.filterBy('clientStatus', 'complete'); 57 } 58 59 @computed('allocations.@each.isRunning') 60 get runningAllocations() { 61 return this.allocations.filterBy('isRunning'); 62 } 63 64 @computed('allocations.@each.{isMigrating,isRunning}') 65 get migratingAllocations() { 66 return this.allocations.filter( 67 (alloc) => alloc.isRunning && alloc.isMigrating 68 ); 69 } 70 71 @computed('allocations.@each.{isMigrating,isRunning,modifyTime}') 72 get lastMigrateTime() { 73 const allocation = this.allocations 74 .filterBy('isRunning', false) 75 .filterBy('isMigrating') 76 .sortBy('modifyTime') 77 .reverse()[0]; 78 if (allocation) { 79 return allocation.modifyTime; 80 } 81 82 return undefined; 83 } 84 85 @fragmentArray('node-driver') drivers; 86 @fragmentArray('node-event') events; 87 @fragmentArray('host-volume') hostVolumes; 88 89 @computed('drivers.@each.detected') 90 get detectedDrivers() { 91 return this.drivers.filterBy('detected'); 92 } 93 94 @computed('detectedDrivers.@each.healthy') 95 get unhealthyDrivers() { 96 return this.detectedDrivers.filterBy('healthy', false); 97 } 98 99 @computed('unhealthyDrivers.@each.name') 100 get unhealthyDriverNames() { 101 return this.unhealthyDrivers.mapBy('name'); 102 } 103 104 // A status attribute that includes states not included in node status. 105 // Useful for coloring and sorting nodes 106 @computed('isDraining', 'isEligible', 'status') 107 get compositeStatus() { 108 if (this.status === 'down') { 109 return 'down'; 110 } else if (this.isDraining) { 111 return 'draining'; 112 } else if (!this.isEligible) { 113 return 'ineligible'; 114 } else { 115 return this.status; 116 } 117 } 118 119 @computed('isDraining', 'isEligible', 'status') 120 get compositeStatusIcon() { 121 if (this.isDraining || !this.isEligible) { 122 return 'alert-circle-fill'; 123 } else if (this.status === 'down') { 124 return 'cancel-circle-fill'; 125 } else if (this.status === 'initializing') { 126 return 'node-init-circle-fill'; 127 } 128 return 'check-circle-fill'; 129 } 130 131 setEligible() { 132 if (this.isEligible) return RSVP.resolve(); 133 // Optimistically update schedulingEligibility for immediate feedback 134 this.set('schedulingEligibility', 'eligible'); 135 return this.store.adapterFor('node').setEligible(this); 136 } 137 138 setIneligible() { 139 if (!this.isEligible) return RSVP.resolve(); 140 // Optimistically update schedulingEligibility for immediate feedback 141 this.set('schedulingEligibility', 'ineligible'); 142 return this.store.adapterFor('node').setIneligible(this); 143 } 144 145 drain(drainSpec) { 146 return this.store.adapterFor('node').drain(this, drainSpec); 147 } 148 149 forceDrain(drainSpec) { 150 return this.store.adapterFor('node').forceDrain(this, drainSpec); 151 } 152 153 cancelDrain() { 154 return this.store.adapterFor('node').cancelDrain(this); 155 } 156 }