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"}} |
-
-
- {{#with row.model.resources.networks.firstObject as |network|}}
- {{#each network.reservedPorts as |port|}}
- -
- {{port.Label}}:
- {{network.ip}}:{{port.Value}}
-
- {{/each}}
- {{#each network.dynamicPorts as |port|}}
- -
- {{port.Label}}:
- {{network.ip}}:{{port.Value}}
-
- {{/each}}
- {{/with}}
-
- |
-
+ {{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 (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"}}
- |
+
+ {{#link-to "allocations.allocation.task" task.allocation task class="is-primary"}}
+ {{task.name}}
+ {{/link-to}}
+ |
+ {{task.state}} |
+
+ {{#if task.events.lastObject.message}}
+ {{task.events.lastObject.message}}
+ {{else}}
+ No message
+ {{/if}}
+ |
+ {{moment-format task.events.lastObject.time "MM/DD/YY HH:mm:ss"}} |
+
+
+ {{#with task.resources.networks.firstObject as |network|}}
+ {{#each network.reservedPorts as |port|}}
+ -
+ {{port.Label}}:
+ {{network.ip}}:{{port.Value}}
+
+ {{/each}}
+ {{#each network.dynamicPorts as |port|}}
+ -
+ {{port.Label}}:
+ {{network.ip}}:{{port.Value}}
+
+ {{/each}}
+ {{/with}}
+
+ |
+
+ {{#if (and (not cpu) fetchStats.isRunning)}}
+ ...
+ {{else if (not task)}}
+ {{! nothing when there's no task}}
+ {{else if statsError}}
+
+ {{x-icon "warning" class="is-warning"}}
+
+ {{else}}
+
+ {{/if}}
+ |
+
+ {{#if (and (not memory) fetchStats.isRunning)}}
+ ...
+ {{else if (not task)}}
+ {{! nothing when there's no task}}
+ {{else if statsError}}
+
+ {{x-icon "warning" class="is-warning"}}
+
+ {{else}}
+
+ {{/if}}
+ |
diff --git a/ui/app/utils/classes/allocation-stats.js b/ui/app/utils/classes/allocation-stats.js
deleted file mode 100644
index 463affbb4d2..00000000000
--- a/ui/app/utils/classes/allocation-stats.js
+++ /dev/null
@@ -1,33 +0,0 @@
-import EmberObject, { computed } from '@ember/object';
-import { alias, readOnly } from '@ember/object/computed';
-
-export default EmberObject.extend({
- allocation: null,
- stats: null,
-
- reservedMemory: alias('allocation.taskGroup.reservedMemory'),
- reservedCPU: alias('allocation.taskGroup.reservedCPU'),
-
- memoryUsed: readOnly('stats.ResourceUsage.MemoryStats.RSS'),
- cpuUsed: computed('stats.ResourceUsage.CpuStats.TotalTicks', function() {
- return Math.floor(this.get('stats.ResourceUsage.CpuStats.TotalTicks') || 0);
- }),
-
- percentMemory: computed('reservedMemory', 'memoryUsed', function() {
- const used = this.get('memoryUsed') / 1024 / 1024;
- const total = this.get('reservedMemory');
- if (!total || !used) {
- return 0;
- }
- return used / total;
- }),
-
- percentCPU: computed('reservedCPU', 'cpuUsed', function() {
- const used = this.get('cpuUsed');
- const total = this.get('reservedCPU');
- if (!total || !used) {
- return 0;
- }
- return used / total;
- }),
-});
diff --git a/ui/tests/integration/allocation-row-test.js b/ui/tests/integration/allocation-row-test.js
index 3049737f28a..a656732eb80 100644
--- a/ui/tests/integration/allocation-row-test.js
+++ b/ui/tests/integration/allocation-row-test.js
@@ -34,7 +34,6 @@ test('Allocation row polls for stats, even when it errors or has an invalid resp
' Valid JSON',
JSON.stringify({ ResourceUsage: generateResources() }),
];
- const backoffSequence = [50];
this.server.get('/client/allocation/:id/stats', function() {
const response = frames[++currentFrame];
@@ -61,7 +60,6 @@ test('Allocation row polls for stats, even when it errors or has an invalid resp
this.setProperties({
allocation,
- backoffSequence,
context: 'job',
enablePolling: true,
});
@@ -70,7 +68,6 @@ test('Allocation row polls for stats, even when it errors or has an invalid resp
{{allocation-row
allocation=allocation
context=context
- backoffSequence=backoffSequence
enablePolling=enablePolling}}
`);
return wait();
|