Skip to content

Commit

Permalink
Merge pull request #7872 from hashicorp/f-ui/csi-phase-2
Browse files Browse the repository at this point in the history
UI: CSI Plugins Pages
  • Loading branch information
DingoEatingFuzz committed May 7, 2020
2 parents 9990650 + 877cadf commit 3393869
Show file tree
Hide file tree
Showing 39 changed files with 962 additions and 91 deletions.
24 changes: 11 additions & 13 deletions ui/app/components/allocation-row.js
Original file line number Diff line number Diff line change
Expand Up @@ -71,24 +71,22 @@ export default Component.extend({
}).drop(),
});

function qualifyAllocation() {
async function qualifyAllocation() {
const allocation = this.allocation;

// Make sure the allocation is a complete record and not a partial so we
// can show information such as preemptions and rescheduled allocation.
return allocation.reload().then(() => {
this.fetchStats.perform();
await allocation.reload();

if (allocation.get('job.isPending')) {
// Make sure the job is loaded before starting the stats tracker
await allocation.get('job');
} else if (!allocation.get('taskGroup')) {
// Make sure that the job record in the store for this allocation
// is complete and not a partial from the list endpoint
if (
allocation &&
allocation.get('job') &&
!allocation.get('job.isPending') &&
!allocation.get('taskGroup')
) {
const job = allocation.get('job.content');
job && job.reload();
}
});
const job = allocation.get('job.content');
if (job) await job.reload();
}

this.fetchStats.perform();
}
44 changes: 44 additions & 0 deletions ui/app/components/allocation-stat.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import Component from '@ember/component';
import { computed } from '@ember/object';
import { alias } from '@ember/object/computed';
import { formatBytes } from 'nomad-ui/helpers/format-bytes';

export default Component.extend({
tagName: '',

allocation: null,
statsTracker: null,
isLoading: false,
error: null,
metric: 'memory', // Either memory or cpu

statClass: computed('metric', function() {
return this.metric === 'cpu' ? 'is-info' : 'is-danger';
}),

cpu: alias('statsTracker.cpu.lastObject'),
memory: alias('statsTracker.memory.lastObject'),

stat: computed('metric', 'cpu', 'memory', function() {
const { metric } = this;
if (metric === 'cpu' || metric === 'memory') {
return this[this.metric];
}
}),

formattedStat: computed('metric', 'stat.used', function() {
if (!this.stat) return;
if (this.metric === 'memory') return formatBytes([this.stat.used]);
return this.stat.used;
}),

formattedReserved: computed(
'metric',
'statsTracker.reservedMemory',
'statsTracker.reservedCPU',
function() {
if (this.metric === 'memory') return `${this.statsTracker.reservedMemory} MiB`;
if (this.metric === 'cpu') return `${this.statsTracker.reservedCPU} MHz`;
}
),
});
7 changes: 7 additions & 0 deletions ui/app/components/plugin-allocation-row.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { alias } from '@ember/object/computed';
import AllocationRow from 'nomad-ui/components/allocation-row';

export default AllocationRow.extend({
pluginAllocation: null,
allocation: alias('pluginAllocation.allocation'),
});
5 changes: 5 additions & 0 deletions ui/app/controllers/csi/plugins.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import Controller from '@ember/controller';

export default Controller.extend({
isForbidden: false,
});
40 changes: 40 additions & 0 deletions ui/app/controllers/csi/plugins/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import { inject as service } from '@ember/service';
import { alias, readOnly } from '@ember/object/computed';
import Controller, { inject as controller } from '@ember/controller';
import SortableFactory from 'nomad-ui/mixins/sortable-factory';
import { lazyClick } from 'nomad-ui/helpers/lazy-click';

export default Controller.extend(SortableFactory([]), {
userSettings: service(),
pluginsController: controller('csi/plugins'),

isForbidden: alias('pluginsController.isForbidden'),

queryParams: {
currentPage: 'page',
sortProperty: 'sort',
sortDescending: 'desc',
},

currentPage: 1,
pageSize: readOnly('userSettings.pageSize'),

sortProperty: 'id',
sortDescending: false,

listToSort: alias('model'),
sortedPlugins: alias('listSorted'),

// TODO: Remove once this page gets search capability
resetPagination() {
if (this.currentPage != null) {
this.set('currentPage', 1);
}
},

actions: {
gotoPlugin(plugin, event) {
lazyClick([() => this.transitionToRoute('csi.plugins.plugin', plugin.plainId), event]);
},
},
});
18 changes: 18 additions & 0 deletions ui/app/controllers/csi/plugins/plugin.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import Controller from '@ember/controller';
import { computed } from '@ember/object';

export default Controller.extend({
sortedControllers: computed('model.controllers.@each.updateTime', function() {
return this.model.controllers.sortBy('updateTime').reverse();
}),

sortedNodes: computed('model.nodes.@each.updateTime', function() {
return this.model.nodes.sortBy('updateTime').reverse();
}),

actions: {
gotoAllocation(allocation) {
this.transitionToRoute('allocations.allocation', allocation);
},
},
});
8 changes: 6 additions & 2 deletions ui/app/controllers/csi/volumes/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { computed } from '@ember/object';
import { alias, readOnly } from '@ember/object/computed';
import Controller, { inject as controller } from '@ember/controller';
import SortableFactory from 'nomad-ui/mixins/sortable-factory';
import { lazyClick } from 'nomad-ui/helpers/lazy-click';

export default Controller.extend(
SortableFactory([
Expand Down Expand Up @@ -58,8 +59,11 @@ export default Controller.extend(
},

actions: {
gotoVolume(volume) {
this.transitionToRoute('csi.volumes.volume', volume.get('plainId'));
gotoVolume(volume, event) {
lazyClick([
() => this.transitionToRoute('csi.volumes.volume', volume.get('plainId')),
event,
]);
},
},
}
Expand Down
1 change: 1 addition & 0 deletions ui/app/controllers/csi/volumes/volume.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { inject as service } from '@ember/service';
import { computed } from '@ember/object';

export default Controller.extend({
// Used in the template
system: service(),

sortedReadAllocations: computed('model.readAllocations.@each.modifyIndex', function() {
Expand Down
4 changes: 2 additions & 2 deletions ui/app/helpers/format-month-ts.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import moment from 'moment';
import Helper from '@ember/component/helper';

export function formatMonthTs([date]) {
const format = 'MMM DD HH:mm:ss ZZ';
export function formatMonthTs([date], options = {}) {
const format = options.short ? 'MMM D' : 'MMM DD HH:mm:ss ZZ';
return moment(date).format(format);
}

Expand Down
4 changes: 2 additions & 2 deletions ui/app/helpers/format-ts.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import moment from 'moment';
import Helper from '@ember/component/helper';

export function formatTs([date]) {
const format = "MMM DD, 'YY HH:mm:ss ZZ";
export function formatTs([date], options = {}) {
const format = options.short ? 'MMM D' : "MMM DD, 'YY HH:mm:ss ZZ";
return moment(date).format(format);
}

Expand Down
23 changes: 20 additions & 3 deletions ui/app/models/plugin.js
Original file line number Diff line number Diff line change
@@ -1,13 +1,30 @@
import { computed } from '@ember/object';
import Model from 'ember-data/model';
import attr from 'ember-data/attr';
// import { fragmentArray } from 'ember-data-model-fragments/attributes';
import { fragmentArray } from 'ember-data-model-fragments/attributes';

export default Model.extend({
plainId: attr('string'),

topologies: attr(),
provider: attr('string'),
version: attr('string'),

controllers: fragmentArray('storage-controller', { defaultValue: () => [] }),
nodes: fragmentArray('storage-node', { defaultValue: () => [] }),

controllerRequired: attr('boolean'),
controllersHealthy: attr('number'),
controllersExpected: attr('number'),

controllersHealthyProportion: computed('controllersHealthy', 'controllersExpected', function() {
return this.controllersHealthy / this.controllersExpected;
}),

nodesHealthy: attr('number'),
nodesExpected: attr('number'),

// controllers: fragmentArray('storage-controller', { defaultValue: () => [] }),
// nodes: fragmentArray('storage-node', { defaultValue: () => [] }),
nodesHealthyProportion: computed('nodesHealthy', 'nodesExpected', function() {
return this.nodesHealthy / this.nodesExpected;
}),
});
14 changes: 13 additions & 1 deletion ui/app/models/storage-controller.js
Original file line number Diff line number Diff line change
@@ -1,13 +1,25 @@
import { computed } from '@ember/object';
import attr from 'ember-data/attr';
import { belongsTo } from 'ember-data/relationships';
import Fragment from 'ember-data-model-fragments/fragment';
import { fragmentOwner } from 'ember-data-model-fragments/attributes';
import PromiseObject from 'nomad-ui/utils/classes/promise-object';

export default Fragment.extend({
plugin: fragmentOwner(),

node: belongsTo('node'),
allocation: belongsTo('allocation'),
allocID: attr('string'),

// Model fragments don't support relationships, but with an allocation ID
// a "belongsTo" can be sufficiently mocked.
allocation: computed('allocID', function() {
if (!this.allocID) return null;
return PromiseObject.create({
promise: this.store.findRecord('allocation', this.allocID),
reload: () => this.store.findRecord('allocation', this.allocID),
});
}),

provider: attr('string'),
version: attr('string'),
Expand Down
14 changes: 13 additions & 1 deletion ui/app/models/storage-node.js
Original file line number Diff line number Diff line change
@@ -1,13 +1,25 @@
import { computed } from '@ember/object';
import attr from 'ember-data/attr';
import { belongsTo } from 'ember-data/relationships';
import Fragment from 'ember-data-model-fragments/fragment';
import { fragmentOwner } from 'ember-data-model-fragments/attributes';
import PromiseObject from 'nomad-ui/utils/classes/promise-object';

export default Fragment.extend({
plugin: fragmentOwner(),

node: belongsTo('node'),
allocation: belongsTo('allocation'),
allocID: attr('string'),

// Model fragments don't support relationships, but with an allocation ID
// a "belongsTo" can be sufficiently mocked.
allocation: computed('allocID', function() {
if (!this.allocID) return null;
return PromiseObject.create({
promise: this.store.findRecord('allocation', this.allocID),
reload: () => this.store.findRecord('allocation', this.allocID),
});
}),

provider: attr('string'),
version: attr('string'),
Expand Down
4 changes: 4 additions & 0 deletions ui/app/router.js
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,10 @@ Router.map(function() {
this.route('volumes', function() {
this.route('volume', { path: '/:volume_name' });
});

this.route('plugins', function() {
this.route('plugin', { path: '/:plugin_name' });
});
});

this.route('allocations', function() {
Expand Down
19 changes: 19 additions & 0 deletions ui/app/routes/csi/plugins.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { inject as service } from '@ember/service';
import Route from '@ember/routing/route';
import WithForbiddenState from 'nomad-ui/mixins/with-forbidden-state';
import notifyForbidden from 'nomad-ui/utils/notify-forbidden';

export default Route.extend(WithForbiddenState, {
store: service(),

breadcrumbs: [
{
label: 'Storage',
args: ['csi.index'],
},
],

model() {
return this.store.query('plugin', { type: 'csi' }).catch(notifyForbidden(this));
},
});
13 changes: 13 additions & 0 deletions ui/app/routes/csi/plugins/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import Route from '@ember/routing/route';
import { collect } from '@ember/object/computed';
import { watchQuery } from 'nomad-ui/utils/properties/watch';
import WithWatchers from 'nomad-ui/mixins/with-watchers';

export default Route.extend(WithWatchers, {
startWatchers(controller) {
controller.set('modelWatch', this.watch.perform({ type: 'csi' }));
},

watch: watchQuery('plugin'),
watchers: collect('watch'),
});
41 changes: 41 additions & 0 deletions ui/app/routes/csi/plugins/plugin.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import { inject as service } from '@ember/service';
import Route from '@ember/routing/route';
import { collect } from '@ember/object/computed';
import notifyError from 'nomad-ui/utils/notify-error';
import { watchRecord } from 'nomad-ui/utils/properties/watch';
import WithWatchers from 'nomad-ui/mixins/with-watchers';

export default Route.extend(WithWatchers, {
store: service(),
system: service(),

breadcrumbs: plugin => [
{
label: 'Plugins',
args: ['csi.plugins'],
},
{
label: plugin.plainId,
args: ['csi.plugins.plugin', plugin.plainId],
},
],

startWatchers(controller, model) {
if (!model) return;

controller.set('watchers', {
model: this.watch.perform(model),
});
},

serialize(model) {
return { plugin_name: model.get('plainId') };
},

model(params) {
return this.store.findRecord('plugin', `csi/${params.plugin_name}`).catch(notifyError(this));
},

watch: watchRecord('plugin'),
watchers: collect('watch'),
});
Loading

0 comments on commit 3393869

Please sign in to comment.