Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

sysbatch UI luiz #11094

Merged
merged 6 commits into from
Aug 30, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
91 changes: 34 additions & 57 deletions ui/app/components/client-status-bar.js
Original file line number Diff line number Diff line change
@@ -1,81 +1,58 @@
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;
jobClientStatus = null;

@computed('nodes')
@computed('jobClientStatus')
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,
};
const formattedNodes = this.nodes.map(node => {
const [[_, allocs]] = Object.entries(node);
return allocs.map(alloc => alloc.clientStatus);
});
for (const node of formattedNodes) {
const statusCount = countBy(node, status => status);
const hasOnly1Status = Object.keys(statusCount).length === 1;

if (hasOnly1Status) {
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 (!hasOnly1Status && !!statusCount.running) {
if (!!statusCount.failed || !!statusCount.lost) {
statuses.degraded++;
} else if (statusCount.pending) {
statuses.starting++;
}
} else {
// if no allocations then queued -- job registered, hasn't been assigned clients to run -- no allocations
// may only have this state for a few milliseconds
statuses.queued++;
}
}

return [
{ label: 'Not Scheduled', value: statuses['not scheduled'], className: 'not-scheduled' },
{ label: 'Queued', value: statuses.queued, className: 'queued' },
{
label: 'Queued',
value: this.jobClientStatus.byStatus.queued.length,
className: 'queued',
},
{
label: 'Starting',
value: statuses.starting,
value: this.jobClientStatus.byStatus.starting.length,
className: 'starting',
layers: 2,
},
{ label: 'Running', value: statuses.running, className: 'running' },
{
label: 'Running',
value: this.jobClientStatus.byStatus.running.length,
className: 'running',
},
{
label: 'Complete',
value: statuses.complete,
value: this.jobClientStatus.byStatus.complete.length,
className: 'complete',
},
{ label: 'Degraded', value: statuses.degraded, className: 'degraded' },
{ label: 'Failed', value: statuses.failed, className: 'failed' },
{ label: 'Lost', value: statuses.lost, className: 'lost' },
{
label: 'Degraded',
value: this.jobClientStatus.byStatus.degraded.length,
className: 'degraded',
},
{
label: 'Failed',
value: this.jobClientStatus.byStatus.failed.length,
className: 'failed',
},
{
label: 'Lost',
value: this.jobClientStatus.byStatus.lost.length,
className: 'lost',
},
{
label: 'Not Scheduled',
value: this.jobClientStatus.byStatus.notScheduled.length,
className: 'not-scheduled',
},
];
}
}
25 changes: 25 additions & 0 deletions ui/app/components/job-page/parts/job-client-summary.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import Component from '@ember/component';
import { computed } from '@ember/object';
import { inject as service } from '@ember/service';
import { classNames } from '@ember-decorators/component';
import classic from 'ember-classic-decorator';

@classic
@classNames('boxed-section')
export default class JobClientSummary extends Component {
@service store;

job = null;
jobClientStatus = null;

@computed
get isExpanded() {
const storageValue = window.localStorage.nomadExpandJobClientSummary;
return storageValue != null ? JSON.parse(storageValue) : true;
}

persist(item, isOpen) {
window.localStorage.nomadExpandJobClientSummary = isOpen;
this.notifyPropertyChange('isExpanded');
}
}
3 changes: 3 additions & 0 deletions ui/app/components/job-page/parts/summary.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,12 @@ import classic from 'ember-classic-decorator';
@classNames('boxed-section')
export default class Summary extends Component {
job = null;
forceCollapsed = false;

@computed
get isExpanded() {
if (this.forceCollapsed) return false;

const storageValue = window.localStorage.nomadExpandJobSummary;
return storageValue != null ? JSON.parse(storageValue) : true;
}
Expand Down
15 changes: 15 additions & 0 deletions ui/app/components/job-page/sysbatch.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import AbstractJobPage from './abstract';
import classic from 'ember-classic-decorator';
import { inject as service } from '@ember/service';
import jobClientStatus from 'nomad-ui/utils/properties/job-client-status';

@classic
export default class Sysbatch extends AbstractJobPage {
@service store;

@jobClientStatus('nodes', 'job') jobClientStatus;

get nodes() {
return this.store.peekAll('node');
}
}
12 changes: 11 additions & 1 deletion ui/app/components/job-page/system.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,15 @@
import AbstractJobPage from './abstract';
import classic from 'ember-classic-decorator';
import { inject as service } from '@ember/service';
import jobClientStatus from 'nomad-ui/utils/properties/job-client-status';

@classic
export default class System extends AbstractJobPage {}
export default class System extends AbstractJobPage {
@service store;

@jobClientStatus('nodes', 'job') jobClientStatus;

get nodes() {
return this.store.peekAll('node');
}
}
6 changes: 5 additions & 1 deletion ui/app/controllers/jobs/job/clients.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ export default class ClientsController extends Controller.extend(
sortDescending = true;

@alias('model') job;
@jobClientStatus('nodes', 'job.status', 'job.allocations') jobClientStatus;
@jobClientStatus('nodes', 'job') jobClientStatus;

@alias('uniqueNodes') listToSort;
@alias('listSorted') listToSearch;
Expand All @@ -59,6 +59,10 @@ export default class ClientsController extends Controller.extend(
return result;
}

get nodes() {
return this.store.peekAll('node');
}

@action
gotoClient(client) {
console.log('goToClient', client);
Expand Down
31 changes: 0 additions & 31 deletions ui/app/controllers/jobs/job/index.js
Original file line number Diff line number Diff line change
@@ -1,44 +1,13 @@
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';
import classic from 'ember-classic-decorator';
import jobClientStatus from 'nomad-ui/utils/properties/job-client-status';

@classic
export default class IndexController extends Controller.extend(WithNamespaceResetting) {
@service system;
@service store;

@jobClientStatus('nodes', 'job.status', 'job.allocations') jobClientStatus;

@computed('job')
get uniqueNodes() {
// add datacenter filter
const allocs = this.job.allocations;
const nodes = allocs.mapBy('node');
const uniqueNodes = nodes.uniqBy('id').toArray();
const result = uniqueNodes.map(nodeId => {
return {
[nodeId.get('id')]: allocs
.toArray()
.filter(alloc => nodeId.get('id') === alloc.get('node.id'))
.map(alloc => alloc.getProperties('clientStatus', 'name', 'createTime', 'modifyTime')),
};
});
return result;
}

@computed('node')
get totalNodes() {
return this.store.peekAll('node').toArray().length;
}

get nodes() {
return this.store.peekAll('node');
}

queryParams = [
{
Expand Down
2 changes: 1 addition & 1 deletion ui/app/models/job.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import RSVP from 'rsvp';
import { assert } from '@ember/debug';
import classic from 'ember-classic-decorator';

const JOB_TYPES = ['service', 'batch', 'system'];
const JOB_TYPES = ['service', 'batch', 'system', 'sysbatch'];

@classic
export default class Job extends Model {
Expand Down
4 changes: 3 additions & 1 deletion ui/app/routes/jobs/job/clients.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import Route from '@ember/routing/route';

export default class ClientsRoute extends Route {}
export default class ClientsRoute extends Route {
// TODO: add watcher for nodes.
}
6 changes: 3 additions & 3 deletions ui/app/styles/charts/colors.scss
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ $running: $primary;
$complete: $nomad-green-dark;
$failed: $danger;
$lost: $dark;
$not-scheduled: $grey-darker;
$not-scheduled: $blue-200;
$degraded: $warning;

.chart {
Expand Down Expand Up @@ -113,11 +113,11 @@ $degraded: $warning;
}

&.not-scheduled {
fill: $not-scheduled;
background: $not-scheduled;
}

&.degraded {
fill: $degraded;
background: $degraded;
}

@each $name, $pair in $colors {
Expand Down
35 changes: 35 additions & 0 deletions ui/app/templates/components/job-page/parts/job-client-summary.hbs
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
<ListAccordion data-test-job-summary @source={{array this.job}} @key="id" @startExpanded={{this.isExpanded}} @onToggle={{action this.persist}} as |a|>
<a.head @buttonLabel={{if a.isOpen "collapse" "expand"}}>
<div class="columns">
<div class="column is-minimum nowrap">
Job Status in Client
<span class="badge {{if a.isOpen "is-white" "is-light"}}">
{{this.jobClientStatus.totalNodes}}
</span>
</div>

{{#unless a.isOpen}}
<div class="column">
<div class="inline-chart bumper-left">
<ClientStatusBar @jobClientStatus={{this.jobClientStatus}} @isNarrow={{true}} />
</div>
</div>
{{/unless}}
</div>
</a.head>
<a.body>
<ClientStatusBar @jobClientStatus={{this.jobClientStatus}} class="split-view" as |chart|>
<ol data-test-legend class="legend">
{{#each chart.data as |datum index|}}
<li class="{{datum.className}} {{if (eq datum.label chart.activeDatum.label) "is-active"}} {{if (eq datum.value 0) "is-empty"}}">
<span class="color-swatch {{if datum.className datum.className (concat "swatch-" index)}}" />
<span class="value" data-test-legend-value="{{datum.className}}">{{datum.value}}</span>
<span class="label">
{{datum.label}}
</span>
</li>
{{/each}}
</ol>
</ClientStatusBar>
</a.body>
</ListAccordion>
59 changes: 0 additions & 59 deletions ui/app/templates/components/job-page/parts/summary.hbs
Original file line number Diff line number Diff line change
@@ -1,63 +1,4 @@
<ListAccordion data-test-job-summary @source={{array this.job}} @key="id" @startExpanded={{this.isExpanded}} @onToggle={{action this.persist}} as |a|>
<a.head @buttonLabel={{if a.isOpen "collapse" "expand"}}>
<div class="columns">
<div class="column is-minimum nowrap">
{{#if a.item.hasChildren}}
Children Status
<span class="badge {{if a.isOpen "is-white" "is-light"}}">
{{a.item.summary.totalChildren}}
</span>
{{else}}
Client Status
<span class="badge {{if a.isOpen "is-white" "is-light"}}">
{{a.item.summary.totalAllocs}}
</span>
{{/if}}
</div>

{{#unless a.isOpen}}
<div class="column">
<div class="inline-chart bumper-left">
{{#if a.item.hasChildren}}
{{#if (gt a.item.totalChildren 0)}}
<ChildrenStatusBar @job={{a.item}} @isNarrow={{true}} />
{{else}}
<em class="is-faded">No Children</em>
{{/if}}
{{else}}
<ClientStatusBar @jobClientStatus={{this.jobClientStatus}} @nodes={{this.nodes}} @allocationContainer={{a.item}} @isNarrow={{true}} />
{{/if}}
</div>
</div>
{{/unless}}
</div>
</a.head>
<a.body>
{{#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
jobClientStatus=this.jobClientStatus
class="split-view" as |chart|}}
<ol data-test-legend class="legend">
{{#each chart.data as |datum index|}}
<li class="{{datum.className}} {{if (eq datum.label chart.activeDatum.label) "is-active"}} {{if (eq datum.value 0) "is-empty"}}">
<span class="color-swatch {{if datum.className datum.className (concat "swatch-" index)}}" />
<span class="value" data-test-legend-value="{{datum.className}}">{{datum.value}}</span>
<span class="label">
{{datum.label}}
</span>
</li>
{{/each}}
</ol>
{{/component}}
</a.body>
</ListAccordion>

<br/>

<ListAccordion data-test-job-summary @source={{array this.job}} @key="id" @startExpanded={{false}} @onToggle={{action this.persist}} as |a|>
<a.head @buttonLabel={{if a.isOpen "collapse" "expand"}}>
<div class="columns">
<div class="column is-minimum nowrap">
Expand Down
Loading