From 1f95a674f30a9c327061fb336e1c0b9de42f95b4 Mon Sep 17 00:00:00 2001 From: Jai Bhagat Date: Tue, 24 Aug 2021 07:34:07 -0400 Subject: [PATCH] initial logic for computing client status bar --- ui/app/components/client-status-bar.js | 80 +++++++++++++++++++ ui/app/controllers/jobs/job/index.js | 24 ++++++ ui/app/routes/jobs/job/index.js | 9 +++ ui/app/styles/charts/colors.scss | 18 +++++ .../components/job-page/parts/summary.hbs | 58 ++++++++++++++ .../templates/components/job-page/service.hbs | 2 +- .../templates/components/job-page/system.hbs | 2 +- ui/app/templates/jobs/job/index.hbs | 2 + 8 files changed, 193 insertions(+), 2 deletions(-) create mode 100644 ui/app/components/client-status-bar.js diff --git a/ui/app/components/client-status-bar.js b/ui/app/components/client-status-bar.js new file mode 100644 index 00000000000..08c3d40cd2b --- /dev/null +++ b/ui/app/components/client-status-bar.js @@ -0,0 +1,80 @@ +import { computed } from '@ember/object'; +import DistributionBar from './distribution-bar'; +import classic from 'ember-classic-decorator'; +import { countBy } from 'lodash'; + +@classic +export default class ClientStatusBar extends DistributionBar { + layoutName = 'components/distribution-bar'; + + 'data-test-client-status-bar' = true; + allocationContainer = null; + nodes = null; + totalNodes = null; + + @computed('nodes') + get data() { + const statuses = { + queued: 0, + 'not scheduled': this.totalNodes - this.nodes.length, + starting: 0, + running: 0, + complete: 0, + degraded: 0, + failed: 0, + lost: 0, + }; + for (const node of this.nodes) { + const concatenatedAllocationStatuses = [].concat(...Object.values(node)); + console.log(concatenatedAllocationStatuses); + // there is a bug that counts nodes multiple times in this part of the loop + for (const status of concatenatedAllocationStatuses) { + const val = str => str; + const statusCount = countBy(concatenatedAllocationStatuses, val); + if (Object.keys(statusCount).length === 1) { + if (statusCount.running > 0) { + statuses.running++; + } + if (statusCount.failed > 0) { + statuses.failed++; + } + if (statusCount.lost > 0) { + statuses.lost++; + } + if (statusCount.complete > 0) { + statuses.complete++; + } + } else if (Object.keys(statusCount).length !== 1 && !!statusCount.running) { + if (!!statusCount.failed || !!statusCount.lost) { + statuses.degraded++; + } + } else if (Object.keys(statusCount).length !== 1 && !!statusCount.pending) { + statuses.starting++; + } else { + statuses.queued++; + } + } + } + + console.log('statuses\n\n', statuses); + return [ + { label: 'Not Scheduled', value: statuses['not scheduled'], className: 'not-scheduled' }, + { label: 'Queued', value: statuses.queued, className: 'queued' }, + { + label: 'Starting', + value: statuses.starting, + className: 'starting', + layers: 2, + }, + { label: 'Running', value: statuses.running, className: 'running' }, + { + label: 'Complete', + value: statuses.complete, + className: 'complete', + }, + { label: 'Degraded', value: statuses.degraded, className: 'degraded' }, + { label: 'Failed', value: statuses.failed, className: 'failed' }, + { label: 'Lost', value: statuses.lost, className: 'lost' }, + ]; + } +} diff --git a/ui/app/controllers/jobs/job/index.js b/ui/app/controllers/jobs/job/index.js index eae91858f7a..18a9b685d65 100644 --- a/ui/app/controllers/jobs/job/index.js +++ b/ui/app/controllers/jobs/job/index.js @@ -1,5 +1,6 @@ import { inject as service } from '@ember/service'; import { alias } from '@ember/object/computed'; +import { computed } from '@ember/object'; import Controller from '@ember/controller'; import WithNamespaceResetting from 'nomad-ui/mixins/with-namespace-resetting'; import { action } from '@ember/object'; @@ -8,6 +9,29 @@ import classic from 'ember-classic-decorator'; @classic export default class IndexController extends Controller.extend(WithNamespaceResetting) { @service system; + @service store; + + @computed('job') + get uniqueNodes() { + // add datacenter filter + const allocs = this.job.allocations; + const nodes = allocs.mapBy('node'); + const uniqueNodes = nodes.uniqBy('id').toArray(); + return uniqueNodes.map(nodeId => { + return { + [nodeId.get('id')]: allocs + .toArray() + .filter(alloc => nodeId.get('id') === alloc.get('node.id')) + .map(alloc => alloc.getProperties('clientStatus')) + .map(alloc => alloc.clientStatus), + }; + }); + } + + @computed('node') + get totalNodes() { + return this.store.peekAll('node').toArray().length; + } queryParams = [ { diff --git a/ui/app/routes/jobs/job/index.js b/ui/app/routes/jobs/job/index.js index 4bea45f1f2d..933fcf95ba9 100644 --- a/ui/app/routes/jobs/job/index.js +++ b/ui/app/routes/jobs/job/index.js @@ -4,6 +4,12 @@ import { watchRecord, watchRelationship, watchAll } from 'nomad-ui/utils/propert import WithWatchers from 'nomad-ui/mixins/with-watchers'; export default class IndexRoute extends Route.extend(WithWatchers) { + async model() { + // Optimizing future node look ups by preemptively loading everything + await this.store.findAll('node'); + return this.modelFor('jobs.job'); + } + startWatchers(controller, model) { if (!model) { return; @@ -16,6 +22,7 @@ export default class IndexRoute extends Route.extend(WithWatchers) { latestDeployment: model.get('supportsDeployments') && this.watchLatestDeployment.perform(model), list: model.get('hasChildren') && this.watchAll.perform(), + nodes: /*model.type === 'sysbatch' && */ this.watchNodes.perform(), }); } @@ -33,6 +40,7 @@ export default class IndexRoute extends Route.extend(WithWatchers) { @watchRecord('job') watch; @watchAll('job') watchAll; + @watchAll('node') watchNodes; @watchRecord('job-summary') watchSummary; @watchRelationship('allocations') watchAllocations; @watchRelationship('evaluations') watchEvaluations; @@ -41,6 +49,7 @@ export default class IndexRoute extends Route.extend(WithWatchers) { @collect( 'watch', 'watchAll', + 'watchNodes', 'watchSummary', 'watchAllocations', 'watchEvaluations', diff --git a/ui/app/styles/charts/colors.scss b/ui/app/styles/charts/colors.scss index 50f370607f6..25292206177 100644 --- a/ui/app/styles/charts/colors.scss +++ b/ui/app/styles/charts/colors.scss @@ -4,6 +4,8 @@ $running: $primary; $complete: $nomad-green-dark; $failed: $danger; $lost: $dark; +$not-scheduled: $grey-darker; +$degraded: $warning; .chart { .queued { @@ -37,6 +39,14 @@ $lost: $dark; .lost { fill: $lost; } + + .not-scheduled { + fill: $not-scheduled; + } + + .degraded { + fill: $degraded; + } } .color-swatch { @@ -102,6 +112,14 @@ $lost: $dark; background: $lost; } + &.not-scheduled { + fill: $not-scheduled; + } + + &.degraded { + fill: $degraded; + } + @each $name, $pair in $colors { $color: nth($pair, 1); diff --git a/ui/app/templates/components/job-page/parts/summary.hbs b/ui/app/templates/components/job-page/parts/summary.hbs index e90cdff5375..11130804d60 100644 --- a/ui/app/templates/components/job-page/parts/summary.hbs +++ b/ui/app/templates/components/job-page/parts/summary.hbs @@ -1,4 +1,62 @@ + +
+
+ {{#if a.item.hasChildren}} + Children Status + + {{a.item.summary.totalChildren}} + + {{else}} + Client Status + + {{a.item.summary.totalAllocs}} + + {{/if}} +
+ + {{#unless a.isOpen}} +
+
+ {{#if a.item.hasChildren}} + {{#if (gt a.item.totalChildren 0)}} + + {{else}} + No Children + {{/if}} + {{else}} + + {{/if}} +
+
+ {{/unless}} +
+
+ + {{#component (if a.item.hasChildren "children-status-bar" "client-status-bar") + allocationContainer=a.item.summary + job=a.item.summary + nodes=this.nodes + totalNodes=this.totalNodes + class="split-view" as |chart|}} +
    + {{#each chart.data as |datum index|}} +
  1. + + {{datum.value}} + + {{datum.label}} + +
  2. + {{/each}} +
+ {{/component}} +
+
+ +
+ +
diff --git a/ui/app/templates/components/job-page/service.hbs b/ui/app/templates/components/job-page/service.hbs index d8180f244a8..915e88bab96 100644 --- a/ui/app/templates/components/job-page/service.hbs +++ b/ui/app/templates/components/job-page/service.hbs @@ -19,7 +19,7 @@ {{/each}} {{/if}} - + diff --git a/ui/app/templates/components/job-page/system.hbs b/ui/app/templates/components/job-page/system.hbs index 5e0abbea25f..a7c8520b2ae 100644 --- a/ui/app/templates/components/job-page/system.hbs +++ b/ui/app/templates/components/job-page/system.hbs @@ -19,7 +19,7 @@ {{/each}} {{/if}} - + diff --git a/ui/app/templates/jobs/job/index.hbs b/ui/app/templates/jobs/job/index.hbs index 56d18ec2e57..82d97770fbe 100644 --- a/ui/app/templates/jobs/job/index.hbs +++ b/ui/app/templates/jobs/job/index.hbs @@ -1,6 +1,8 @@ {{page-title "Job " this.model.name}} {{component (concat "job-page/" this.model.templateType) job=this.model + nodes=this.uniqueNodes + totalNodes=this.totalNodes sortProperty=this.sortProperty sortDescending=this.sortDescending currentPage=this.currentPage