From 2c96d75ccd8ef9690784cb9a96c44a831298242d Mon Sep 17 00:00:00 2001 From: Jai Bhagat Date: Fri, 27 Aug 2021 10:41:41 -0400 Subject: [PATCH] create client tab view --- ui/app/components/client-row.hbs | 27 ++++++++ ui/app/components/client-row.js | 73 ++++++++++++++++++++ ui/app/controllers/jobs/job/clients.js | 67 ++++++++++++++++++ ui/app/controllers/jobs/job/index.js | 1 - ui/app/router.js | 1 + ui/app/routes/jobs/job/clients.js | 3 + ui/app/templates/components/job-subnav.hbs | 1 + ui/app/templates/jobs/job/clients.hbs | 71 +++++++++++++++++++ ui/app/utils/properties/job-client-status.js | 8 +++ 9 files changed, 251 insertions(+), 1 deletion(-) create mode 100644 ui/app/components/client-row.hbs create mode 100644 ui/app/components/client-row.js create mode 100644 ui/app/controllers/jobs/job/clients.js create mode 100644 ui/app/routes/jobs/job/clients.js create mode 100644 ui/app/templates/jobs/job/clients.hbs diff --git a/ui/app/components/client-row.hbs b/ui/app/components/client-row.hbs new file mode 100644 index 00000000000..0b2c2defe07 --- /dev/null +++ b/ui/app/components/client-row.hbs @@ -0,0 +1,27 @@ + + + + + + {{this.id}} + + + + {{this.name}} + +{{moment-from-now this.eldestCreateTime}} + + + {{moment-from-now this.allocation.modifyTime}} + + + + {{this.status}} + + +
+ +
+ + + diff --git a/ui/app/components/client-row.js b/ui/app/components/client-row.js new file mode 100644 index 00000000000..5c84a42fb5c --- /dev/null +++ b/ui/app/components/client-row.js @@ -0,0 +1,73 @@ +import { classNames, tagName } from '@ember-decorators/component'; +import EmberObject from '@ember/object'; +import Component from '@glimmer/component'; + +@tagName('tr') +@classNames('client-row', 'is-interactive') +export default class ClientRowComponent extends Component { + get id() { + console.log('node arg', this.args.node); + return Object.keys(this.args.node.model)[0]; + } + + get name() { + return this.args.node.model[this.id][0].name; + } + + get eldestCreateTime() { + let eldest = null; + for (const allocation of this.args.node.model[this.id]) { + if (!eldest || allocation.createTime < eldest) { + eldest = allocation.createTime; + } + } + return eldest; + } + + get mostRecentModifyTime() { + let mostRecent = null; + for (const allocation of this.args.node.model[this.id]) { + if (!mostRecent || allocation.modifyTime > mostRecent) { + mostRecent = allocation.createTime; + } + } + return mostRecent; + } + + get status() { + return this.args.jobClientStatus.byNode[this.id]; + } + + get allocationContainer() { + const statusSummary = { + queuedAllocs: 0, + completeAllocs: 0, + failedAllocs: 0, + runningAllocs: 0, + startingAllocs: 0, + lostAllocs: 0, + }; + for (const allocation of this.args.node.model[this.id]) { + const { clientStatus } = allocation; + switch (clientStatus) { + // add missing statuses + case 'running': + statusSummary.runningAllocs++; + break; + case 'lost': + statusSummary.lostAllocs++; + break; + case 'failed': + statusSummary.failedAllocs++; + break; + case 'completed': + statusSummary.completeAllocs++; + break; + } + } + const Allocations = EmberObject.extend({ + ...statusSummary, + }); + return Allocations.create(); + } +} diff --git a/ui/app/controllers/jobs/job/clients.js b/ui/app/controllers/jobs/job/clients.js new file mode 100644 index 00000000000..d25905f98f1 --- /dev/null +++ b/ui/app/controllers/jobs/job/clients.js @@ -0,0 +1,67 @@ +import Controller from '@ember/controller'; +import { action, computed } from '@ember/object'; +import { alias } from '@ember/object/computed'; +import Sortable from 'nomad-ui/mixins/sortable'; +import Searchable from 'nomad-ui/mixins/searchable'; +import WithNamespaceResetting from 'nomad-ui/mixins/with-namespace-resetting'; +import jobClientStatus from 'nomad-ui/utils/properties/job-client-status'; + +export default class ClientsController extends Controller.extend( + Sortable, + Searchable, + WithNamespaceResetting +) { + queryParams = [ + { + currentPage: 'page', + }, + { + searchTerm: 'search', + }, + { + sortProperty: 'sort', + }, + { + sortDescending: 'desc', + }, + ]; + + currentPage = 1; + pageSize = 25; + + sortProperty = 'modifyIndex'; + sortDescending = true; + + @alias('model') job; + @jobClientStatus('nodes', 'job.status', 'job.allocations') jobClientStatus; + + @alias('uniqueNodes') listToSort; + @alias('listSorted') listToSearch; + @alias('listSearched') sortedClients; + + @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 => ({ + nodeId, + ...alloc.getProperties('clientStatus', 'name', 'createTime', 'modifyTime'), + })), + }; + }); + return result; + } + + @action + gotoClient(client) { + console.log('goToClient', client); + this.transitionToRoute('clients.client', client); + } +} diff --git a/ui/app/controllers/jobs/job/index.js b/ui/app/controllers/jobs/job/index.js index 25b37f4aeeb..0db12bd24c7 100644 --- a/ui/app/controllers/jobs/job/index.js +++ b/ui/app/controllers/jobs/job/index.js @@ -28,7 +28,6 @@ export default class IndexController extends Controller.extend(WithNamespaceRese .map(alloc => alloc.getProperties('clientStatus', 'name', 'createTime', 'modifyTime')), }; }); - console.log('result\n\n', result); return result; } diff --git a/ui/app/router.js b/ui/app/router.js index 072291fe673..8e5c2070895 100644 --- a/ui/app/router.js +++ b/ui/app/router.js @@ -23,6 +23,7 @@ Router.map(function() { this.route('dispatch'); this.route('evaluations'); this.route('allocations'); + this.route('clients'); }); }); diff --git a/ui/app/routes/jobs/job/clients.js b/ui/app/routes/jobs/job/clients.js new file mode 100644 index 00000000000..0558daf6b48 --- /dev/null +++ b/ui/app/routes/jobs/job/clients.js @@ -0,0 +1,3 @@ +import Route from '@ember/routing/route'; + +export default class ClientsRoute extends Route {} diff --git a/ui/app/templates/components/job-subnav.hbs b/ui/app/templates/components/job-subnav.hbs index a655756d862..fd4fc055cfa 100644 --- a/ui/app/templates/components/job-subnav.hbs +++ b/ui/app/templates/components/job-subnav.hbs @@ -8,5 +8,6 @@ {{/if}}
  • Allocations
  • Evaluations
  • +
  • Clients
  • diff --git a/ui/app/templates/jobs/job/clients.hbs b/ui/app/templates/jobs/job/clients.hbs new file mode 100644 index 00000000000..8d91a7d930a --- /dev/null +++ b/ui/app/templates/jobs/job/clients.hbs @@ -0,0 +1,71 @@ +{{page-title "Job " this.job.name " clients"}} + +
    + {{#if this.uniqueNodes.length}} +
    +
    + +
    +
    + {{#if this.sortedClients}} + + + + + ID + Name + Created + Modified + Status + Allocation Summary + + + {{!-- {{log this.sortedClients.length}} --}} + + + +
    + +
    +
    + {{else}} +
    +
    +

    No Matches

    +

    No clients match the term {{this.searchTerm}}

    +
    +
    + {{/if}} + {{else}} +
    +
    +

    No Clients

    +

    No clients have been placed.

    +
    +
    + {{/if}} +
    \ No newline at end of file diff --git a/ui/app/utils/properties/job-client-status.js b/ui/app/utils/properties/job-client-status.js index f050335489d..ca1ebec57fe 100644 --- a/ui/app/utils/properties/job-client-status.js +++ b/ui/app/utils/properties/job-client-status.js @@ -14,6 +14,14 @@ export default function jobClientStatus(nodesKey, jobStatusKey, jobAllocsKey) { return { byNode: { '123': 'running', + '14b8ff31-8310-4877-b3e9-ba6ebcbf37a2': 'running', + '193538ac-30d7-48eb-9a31-b89a83abae1d': 'degraded', + '0cdce07c-6c50-4364-9ffd-e08c419f93aa': 'lost', + 'a07563e7-fdc6-4df4-bfd7-82b97a3605ab': 'failed', + '6c32bb4d-6fda-4c0d-a7be-008f857d9f0e': 'queued', + '75930e1c-15f2-4411-8dbd-1016e3d54c12': 'running', + '8f834f8f-75cc-4da7-96f2-3665bd9bf774': 'running', + '386a8b5f-68ab-4baf-a5ca-597d1bed8589': 'running', }, byStatus: { running: ['123'],