diff --git a/ui/app/components/allocation-row.js b/ui/app/components/allocation-row.js index d2a7433020b..5a85cb020d4 100644 --- a/ui/app/components/allocation-row.js +++ b/ui/app/components/allocation-row.js @@ -2,12 +2,15 @@ import Ember from 'ember'; import { inject as service } from '@ember/service'; import Component from '@ember/component'; import { computed } from '@ember/object'; +import { alias } from '@ember/object/computed'; import { run } from '@ember/runloop'; -import { lazyClick } from '../helpers/lazy-click'; import { task, timeout } from 'ember-concurrency'; +import { lazyClick } from '../helpers/lazy-click'; +import AllocationStatsTracker from 'nomad-ui/utils/classes/allocation-stats-tracker'; export default Component.extend({ store: service(), + token: service(), tagName: 'tr', @@ -18,14 +21,21 @@ export default Component.extend({ // Used to determine whether the row should mention the node or the job context: null, - backoffSequence: computed(() => [500, 800, 1300, 2100, 3400, 5500]), - // Internal state - stats: null, statsError: false, enablePolling: computed(() => !Ember.testing), + stats: computed('allocation', function() { + return AllocationStatsTracker.create({ + fetch: url => this.get('token').authorizedRequest(url), + allocation: this.get('allocation'), + }); + }), + + cpu: alias('stats.cpu.lastObject'), + memory: alias('stats.memory.lastObject'), + onClick() {}, click(event) { @@ -39,23 +49,18 @@ export default Component.extend({ run.scheduleOnce('afterRender', this, qualifyAllocation); } else { this.get('fetchStats').cancelAll(); - this.set('stats', null); } }, - fetchStats: task(function*(allocation) { - const backoffSequence = this.get('backoffSequence').slice(); - const maxTiming = backoffSequence.pop(); - + fetchStats: task(function*() { do { try { - const stats = yield allocation.fetchStats(); - this.set('stats', stats); + yield this.get('stats.poll').perform(); this.set('statsError', false); } catch (error) { this.set('statsError', true); } - yield timeout(backoffSequence.shift() || maxTiming); + yield timeout(500); } while (this.get('enablePolling')); }).drop(), }); @@ -63,7 +68,7 @@ export default Component.extend({ function qualifyAllocation() { const allocation = this.get('allocation'); return allocation.reload().then(() => { - this.get('fetchStats').perform(allocation); + this.get('fetchStats').perform(); // Make sure that the job record in the store for this allocation // is complete and not a partial from the list endpoint diff --git a/ui/app/components/task-row.js b/ui/app/components/task-row.js new file mode 100644 index 00000000000..970ba43df0f --- /dev/null +++ b/ui/app/components/task-row.js @@ -0,0 +1,64 @@ +import Ember from 'ember'; +import Component from '@ember/component'; +import { inject as service } from '@ember/service'; +import { computed } from '@ember/object'; +import { alias } from '@ember/object/computed'; +import { task, timeout } from 'ember-concurrency'; +import { lazyClick } from '../helpers/lazy-click'; + +export default Component.extend({ + store: service(), + token: service(), + statsTrackersRegistry: service('stats-trackers-registry'), + + tagName: 'tr', + classNames: ['task-row', 'is-interactive'], + + task: null, + + // Internal state + statsError: false, + + enablePolling: computed(() => !Ember.testing), + + // Since all tasks for an allocation share the same tracker, use the registry + stats: computed('task', function() { + return this.get('statsTrackersRegistry').getTracker(this.get('task.allocation')); + }), + + taskStats: computed('task.name', 'stats.tasks.[]', function() { + const ret = this.get('stats.tasks').findBy('task', this.get('task.name')); + return ret; + }), + + cpu: alias('taskStats.cpu.lastObject'), + memory: alias('taskStats.memory.lastObject'), + + onClick() {}, + + click(event) { + lazyClick([this.get('onClick'), event]); + }, + + fetchStats: task(function*() { + do { + try { + yield this.get('stats.poll').perform(); + this.set('statsError', false); + } catch (error) { + this.set('statsError', true); + } + yield timeout(500); + } while (this.get('enablePolling')); + }).drop(), + + didReceiveAttrs() { + const allocation = this.get('task.allocation'); + + if (allocation) { + this.get('fetchStats').perform(); + } else { + this.get('fetchStats').cancelAll(); + } + }, +}); diff --git a/ui/app/models/allocation.js b/ui/app/models/allocation.js index f98e3fc22fa..1a7e030a530 100644 --- a/ui/app/models/allocation.js +++ b/ui/app/models/allocation.js @@ -6,7 +6,6 @@ import { belongsTo } from 'ember-data/relationships'; import { fragment, fragmentArray } from 'ember-data-model-fragments/attributes'; import intersection from 'lodash.intersection'; import shortUUIDProperty from '../utils/properties/short-uuid'; -import AllocationStats from '../utils/classes/allocation-stats'; const STATUS_ORDER = { pending: 1, @@ -74,18 +73,6 @@ export default Model.extend({ return []; }), - fetchStats() { - return this.get('token') - .authorizedRequest(`/v1/client/allocation/${this.get('id')}/stats`) - .then(res => res.json()) - .then(json => { - return new AllocationStats({ - stats: json, - allocation: this, - }); - }); - }, - states: fragmentArray('task-state'), rescheduleEvents: fragmentArray('reschedule-event'), diff --git a/ui/app/templates/allocations/allocation/index.hbs b/ui/app/templates/allocations/allocation/index.hbs index 72deebf802a..335d2cf5aac 100644 --- a/ui/app/templates/allocations/allocation/index.hbs +++ b/ui/app/templates/allocations/allocation/index.hbs @@ -50,52 +50,14 @@ Last Event {{#t.sort-by prop="events.lastObject.time"}}Time{{/t.sort-by}} Addresses + CPU + Memory {{/t.head}} {{#t.body as |row|}} - - - {{#if (not row.model.driverStatus.healthy)}} - - {{x-icon "warning" class="is-warning"}} - - {{/if}} - - - {{#link-to "allocations.allocation.task" row.model.allocation row.model class="is-primary"}} - {{row.model.name}} - {{/link-to}} - - {{row.model.state}} - - {{#if row.model.events.lastObject.message}} - {{row.model.events.lastObject.message}} - {{else}} - No message - {{/if}} - - {{moment-format row.model.events.lastObject.time "MM/DD/YY HH:mm:ss"}} - - - - + {{task-row + data-test-task-row=row.model.name + task=row.model + onClick=(action "taskClick" row.model.allocation row.model)}} {{/t.body}} {{/list-table}} diff --git a/ui/app/templates/components/allocation-row.hbs b/ui/app/templates/components/allocation-row.hbs index fd16e5bc906..229efef853f 100644 --- a/ui/app/templates/components/allocation-row.hbs +++ b/ui/app/templates/components/allocation-row.hbs @@ -1,11 +1,11 @@ {{#if allocation.unhealthyDrivers.length}} - + {{x-icon "warning" class="is-warning"}} {{/if}} {{#if allocation.nextAllocation}} - + {{x-icon "history" class="is-faded"}} {{/if}} @@ -42,41 +42,41 @@ {{allocation.jobVersion}} {{/if}} - {{#if (and (not stats) fetchStats.isRunning)}} + {{#if (and (not cpu) fetchStats.isRunning)}} ... {{else if (not allocation)}} {{! nothing when there's no allocation}} {{else if statsError}} - + {{x-icon "warning" class="is-warning"}} {{else}} -
+ {{/if}} - {{#if (and (not stats) fetchStats.isRunning)}} + {{#if (and (not memory) fetchStats.isRunning)}} ... {{else if (not allocation)}} {{! nothing when there's no allocation}} {{else if statsError}} - + {{x-icon "warning" class="is-warning"}} {{else}} -
+ {{/if}} diff --git a/ui/app/templates/components/client-node-row.hbs b/ui/app/templates/components/client-node-row.hbs index 269e522a39c..21bf6597bef 100644 --- a/ui/app/templates/components/client-node-row.hbs +++ b/ui/app/templates/components/client-node-row.hbs @@ -1,6 +1,6 @@ {{#if node.unhealthyDrivers.length}} - + {{x-icon "warning" class="is-warning"}} {{/if}} diff --git a/ui/app/templates/components/freestyle/sg-progress-bar.hbs b/ui/app/templates/components/freestyle/sg-progress-bar.hbs index 24e34f00e6d..edf0d096cda 100644 --- a/ui/app/templates/components/freestyle/sg-progress-bar.hbs +++ b/ui/app/templates/components/freestyle/sg-progress-bar.hbs @@ -1,5 +1,5 @@ {{#freestyle-usage "progress-bar" title="Progress Bar"}} -
+