54eeb6ebe8
* Adds searching and filtering for nodes on topology view * Lintfix and changelog * Acceptance tests for topology search and filter * Search terms also apply to class and dc on topo page * Initialize queryparam values so as to not break history state
524 lines
20 KiB
Handlebars
524 lines
20 KiB
Handlebars
<Breadcrumb @crumb={{hash label="Topology" args=(array "topology")}} />
|
|
{{page-title "Cluster Topology"}}
|
|
<PageLayout>
|
|
<section class="section is-full-width">
|
|
{{#if this.isForbidden}}
|
|
<ForbiddenMessage />
|
|
{{else}}
|
|
{{#if this.pre09Nodes}}
|
|
<div class="notification is-warning">
|
|
<div data-test-filtered-nodes-warning class="columns">
|
|
<div class="column">
|
|
<h3 data-test-title class="title is-4">
|
|
Some Clients Were Filtered
|
|
</h3>
|
|
<p data-test-message>
|
|
{{this.pre09Nodes.length}}
|
|
{{if (eq this.pre09Nodes.length 1) "client was" "clients were"}}
|
|
filtered from the topology visualization. This is most likely due to the
|
|
{{pluralize "client" this.pre09Nodes.length}}
|
|
running a version of Nomad
|
|
</p>
|
|
</div>
|
|
<div class="column is-centered is-minimum">
|
|
<button
|
|
data-test-dismiss
|
|
class="button is-warning"
|
|
onclick={{action (mut this.pre09Nodes) null}}
|
|
type="button"
|
|
>
|
|
Okay
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
{{/if}}
|
|
<div class="columns">
|
|
<div class="column is-narrow is-400">
|
|
{{#if this.showPollingNotice}}
|
|
<div class="notification is-warning">
|
|
<div class="columns">
|
|
<div class="column">
|
|
<h3 class="title is-4">
|
|
No Live Updating
|
|
</h3>
|
|
<p>
|
|
The topology visualization depends on too much data to continuously poll.
|
|
</p>
|
|
</div>
|
|
<div class="column is-centered is-minimum">
|
|
<button
|
|
data-test-polling-notice-dismiss
|
|
class="button is-warning"
|
|
type="button"
|
|
onclick={{toggle-action "showPollingNotice" this}}
|
|
>
|
|
Okay
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
{{/if}}
|
|
<div class="boxed-section">
|
|
<div class="boxed-section-head">
|
|
Legend
|
|
{{#if (cannot "list all jobs")}}
|
|
<span
|
|
aria-label="Your ACL token may limit your ability to list all allocations"
|
|
class="tag is-warning pull-right tooltip multiline"
|
|
>
|
|
Partial View
|
|
</span>
|
|
{{/if}}
|
|
</div>
|
|
<div class="boxed-section-body">
|
|
<div class="legend">
|
|
<h3 class="legend-label">
|
|
Metrics
|
|
</h3>
|
|
<dl class="legend-terms">
|
|
<dt>
|
|
M:
|
|
</dt>
|
|
<dd>
|
|
Memory
|
|
</dd>
|
|
<dt>
|
|
C:
|
|
</dt>
|
|
<dd>
|
|
CPU
|
|
</dd>
|
|
</dl>
|
|
</div>
|
|
<div class="legend">
|
|
<h3 class="legend-label">
|
|
Allocation Status
|
|
</h3>
|
|
<dl class="legend-terms">
|
|
<div class="legend-term">
|
|
<dt>
|
|
<span class="color-swatch is-wide running" title="Running"></span>
|
|
</dt>
|
|
<dd>
|
|
Running
|
|
</dd>
|
|
</div>
|
|
<div class="legend-term">
|
|
<dt>
|
|
<span class="color-swatch is-wide pending" title="Starting"></span>
|
|
</dt>
|
|
<dd>
|
|
Starting
|
|
</dd>
|
|
</div>
|
|
</dl>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="boxed-section">
|
|
<div data-test-info-panel-title class="boxed-section-head">
|
|
{{#if this.activeNode}}
|
|
Client
|
|
{{else if this.activeAllocation}}
|
|
Allocation
|
|
{{else}}
|
|
Cluster
|
|
{{/if}}
|
|
Details
|
|
</div>
|
|
<div data-test-info-panel class="boxed-section-body">
|
|
{{#if this.activeNode}}
|
|
{{#let this.activeNode.node as |node|}}
|
|
<div class="dashboard-metric">
|
|
<p data-test-allocations class="metric">
|
|
{{this.activeNode.allocations.length}}
|
|
<span class="metric-label">
|
|
Allocations
|
|
</span>
|
|
</p>
|
|
</div>
|
|
<div class="dashboard-metric">
|
|
<h3 class="pair">
|
|
<strong>
|
|
Client:
|
|
</strong>
|
|
<LinkTo data-test-client-link @route="clients.client" @model={{node}}>
|
|
{{node.shortId}}
|
|
</LinkTo>
|
|
</h3>
|
|
<p data-test-name class="minor-pair">
|
|
<strong>
|
|
Name:
|
|
</strong>
|
|
{{node.name}}
|
|
</p>
|
|
<p data-test-address class="minor-pair">
|
|
<strong>
|
|
Address:
|
|
</strong>
|
|
{{node.httpAddr}}
|
|
</p>
|
|
<p data-test-status class="minor-pair">
|
|
<strong>
|
|
Status:
|
|
</strong>
|
|
{{node.status}}
|
|
</p>
|
|
</div>
|
|
<div class="dashboard-metric">
|
|
<h3 class="pair">
|
|
<strong>
|
|
Draining?
|
|
</strong>
|
|
<span data-test-draining class="{{if node.isDraining "status-text is-info"}}">
|
|
{{if node.isDraining "Yes" "No"}}
|
|
</span>
|
|
</h3>
|
|
<h3 class="pair">
|
|
<strong>
|
|
Eligible?
|
|
</strong>
|
|
<span
|
|
data-test-eligible
|
|
class="{{unless node.isEligible "status-text is-warning"}}"
|
|
>
|
|
{{if node.isEligible "Yes" "No"}}
|
|
</span>
|
|
</h3>
|
|
</div>
|
|
<div class="dashboard-metric with-divider">
|
|
<p class="metric">
|
|
{{this.nodeUtilization.totalMemoryFormatted}}
|
|
<span class="metric-units">
|
|
{{this.nodeUtilization.totalMemoryUnits}}
|
|
</span>
|
|
<span class="metric-label">
|
|
of memory
|
|
</span>
|
|
</p>
|
|
<div class="columns graphic">
|
|
<div class="column">
|
|
<div class="inline-chart">
|
|
<progress
|
|
data-test-memory-progress-bar
|
|
class="progress is-danger is-small"
|
|
value="{{this.nodeUtilization.reservedMemoryPercent}}"
|
|
max="1"
|
|
>
|
|
{{this.nodeUtilization.reservedMemoryPercent}}
|
|
</progress>
|
|
</div>
|
|
</div>
|
|
<div class="column is-minimum">
|
|
<span class="nowrap" data-test-percentage>
|
|
{{format-percentage this.nodeUtilization.reservedMemoryPercent total=1}}
|
|
</span>
|
|
</div>
|
|
</div>
|
|
<div class="annotation" data-test-memory-absolute-value>
|
|
<strong>
|
|
{{format-scheduled-bytes this.nodeUtilization.totalReservedMemory}}
|
|
</strong>
|
|
/
|
|
{{format-scheduled-bytes this.nodeUtilization.totalMemory}}
|
|
reserved
|
|
</div>
|
|
</div>
|
|
<div class="dashboard-metric">
|
|
<p class="metric">
|
|
{{this.nodeUtilization.totalCPU}}
|
|
<span class="metric-units">
|
|
MHz
|
|
</span>
|
|
<span class="metric-label">
|
|
of CPU
|
|
</span>
|
|
</p>
|
|
<div class="columns graphic">
|
|
<div class="column">
|
|
<div class="inline-chart" data-test-percentage-bar>
|
|
<progress
|
|
data-test-cpu-progress-bar
|
|
class="progress is-info is-small"
|
|
value="{{this.nodeUtilization.reservedCPUPercent}}"
|
|
max="1"
|
|
>
|
|
{{this.nodeUtilization.reservedCPUPercent}}
|
|
</progress>
|
|
</div>
|
|
</div>
|
|
<div class="column is-minimum">
|
|
<span class="nowrap" data-test-percentage>
|
|
{{format-percentage this.nodeUtilization.reservedCPUPercent total=1}}
|
|
</span>
|
|
</div>
|
|
</div>
|
|
<div class="annotation" data-test-cpu-absolute-value>
|
|
<strong>
|
|
{{format-scheduled-hertz this.nodeUtilization.totalReservedCPU}}
|
|
</strong>
|
|
/
|
|
{{format-scheduled-hertz this.nodeUtilization.totalCPU}}
|
|
reserved
|
|
</div>
|
|
</div>
|
|
{{/let}}
|
|
{{else if this.activeAllocation}}
|
|
<div class="dashboard-metric">
|
|
<h3 class="pair">
|
|
<strong>
|
|
Allocation:
|
|
</strong>
|
|
<LinkTo
|
|
data-test-id
|
|
@route="allocations.allocation"
|
|
@model={{this.activeAllocation}}
|
|
class="is-primary"
|
|
>
|
|
{{this.activeAllocation.shortId}}
|
|
</LinkTo>
|
|
</h3>
|
|
<p data-test-sibling-allocs class="minor-pair">
|
|
<strong>
|
|
Sibling Allocations:
|
|
</strong>
|
|
{{this.siblingAllocations.length}}
|
|
</p>
|
|
<p data-test-unique-placements class="minor-pair">
|
|
<strong>
|
|
Unique Client Placements:
|
|
</strong>
|
|
{{this.uniqueActiveAllocationNodes.length}}
|
|
</p>
|
|
</div>
|
|
<div class="dashboard-metric with-divider">
|
|
<h3 class="pair">
|
|
<strong>
|
|
Job:
|
|
</strong>
|
|
<LinkTo
|
|
data-test-job
|
|
@route="jobs.job"
|
|
@model={{this.activeAllocation.job}}
|
|
>
|
|
{{this.activeAllocation.job.name}}
|
|
</LinkTo>
|
|
<span class="is-faded" data-test-task-group>
|
|
/
|
|
{{this.activeAllocation.taskGroupName}}
|
|
</span>
|
|
</h3>
|
|
<p class="minor-pair">
|
|
<strong>
|
|
Type:
|
|
</strong>
|
|
{{this.activeAllocation.job.type}}
|
|
</p>
|
|
<p class="minor-pair">
|
|
<strong>
|
|
Priority:
|
|
</strong>
|
|
{{this.activeAllocation.job.priority}}
|
|
</p>
|
|
</div>
|
|
<div class="dashboard-metric with-divider">
|
|
<h3 class="pair">
|
|
<strong>
|
|
Client:
|
|
</strong>
|
|
<LinkTo
|
|
data-test-client
|
|
@route="clients.client"
|
|
@model={{this.activeAllocation.node}}
|
|
>
|
|
{{this.activeAllocation.node.shortId}}
|
|
</LinkTo>
|
|
</h3>
|
|
<p class="minor-pair">
|
|
<strong>
|
|
Name:
|
|
</strong>
|
|
{{this.activeAllocation.node.name}}
|
|
</p>
|
|
<p class="minor-pair">
|
|
<strong>
|
|
Address:
|
|
</strong>
|
|
{{this.activeAllocation.node.httpAddr}}
|
|
</p>
|
|
</div>
|
|
<div class="dashboard-metric with-divider">
|
|
<PrimaryMetric::Allocation
|
|
@allocation={{this.activeAllocation}}
|
|
@metric="memory"
|
|
class="is-short"
|
|
/>
|
|
</div>
|
|
<div class="dashboard-metric">
|
|
<PrimaryMetric::Allocation
|
|
@allocation={{this.activeAllocation}}
|
|
@metric="cpu"
|
|
class="is-short"
|
|
/>
|
|
</div>
|
|
{{else}}
|
|
<div class="columns is-flush">
|
|
<div class="dashboard-metric column">
|
|
<p data-test-node-count class="metric">
|
|
{{this.model.nodes.length}}
|
|
<span class="metric-label">
|
|
Clients
|
|
</span>
|
|
</p>
|
|
</div>
|
|
<div class="dashboard-metric column">
|
|
<p data-test-alloc-count class="metric">
|
|
{{this.scheduledAllocations.length}}
|
|
<span class="metric-label">
|
|
Allocations
|
|
</span>
|
|
</p>
|
|
</div>
|
|
</div>
|
|
<div class="dashboard-metric with-divider">
|
|
<p class="metric">
|
|
{{this.totalMemoryFormatted}}
|
|
<span class="metric-units">
|
|
{{this.totalMemoryUnits}}
|
|
</span>
|
|
<span class="metric-label">
|
|
of memory
|
|
</span>
|
|
</p>
|
|
<div class="columns graphic">
|
|
<div class="column">
|
|
<div class="inline-chart" data-test-percentage-bar>
|
|
<progress
|
|
data-test-memory-progress-bar
|
|
class="progress is-danger is-small"
|
|
value="{{this.reservedMemoryPercent}}"
|
|
max="1"
|
|
>
|
|
{{this.reservedMemoryPercent}}
|
|
</progress>
|
|
</div>
|
|
</div>
|
|
<div class="column is-minimum">
|
|
<span class="nowrap" data-test-memory-percentage>
|
|
{{format-percentage this.reservedMemoryPercent total=1}}
|
|
</span>
|
|
</div>
|
|
</div>
|
|
<div class="annotation" data-test-memory-absolute-value>
|
|
<strong>
|
|
{{format-bytes this.totalReservedMemory}}
|
|
</strong>
|
|
/
|
|
{{format-bytes this.totalMemory}}
|
|
reserved
|
|
</div>
|
|
</div>
|
|
<div class="dashboard-metric">
|
|
<p class="metric">
|
|
{{this.totalCPUFormatted}}
|
|
<span class="metric-units">
|
|
{{this.totalCPUUnits}}
|
|
</span>
|
|
<span class="metric-label">
|
|
of CPU
|
|
</span>
|
|
</p>
|
|
<div class="columns graphic">
|
|
<div class="column">
|
|
<div class="inline-chart" data-test-percentage-bar>
|
|
<progress
|
|
data-test-cpu-progress-bar
|
|
class="progress is-info is-small"
|
|
value="{{this.reservedCPUPercent}}"
|
|
max="1"
|
|
>
|
|
{{this.reservedCPUPercent}}
|
|
</progress>
|
|
</div>
|
|
</div>
|
|
<div class="column is-minimum">
|
|
<span class="nowrap" data-test-cpu-percentage>
|
|
{{format-percentage this.reservedCPUPercent total=1}}
|
|
</span>
|
|
</div>
|
|
</div>
|
|
<div class="annotation" data-test-cpu-absolute-value>
|
|
<strong>
|
|
{{format-hertz this.totalReservedCPU}}
|
|
</strong>
|
|
/
|
|
{{format-hertz this.totalCPU}}
|
|
reserved
|
|
</div>
|
|
</div>
|
|
{{/if}}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="column">
|
|
<div class="toolbar">
|
|
<div class="toolbar-item">
|
|
{{#if this.model.nodes.length}}
|
|
<SearchBox
|
|
@inputClass="node-search"
|
|
@searchTerm={{mut this.searchTerm}}
|
|
@placeholder="Search clients..."
|
|
/>
|
|
{{/if}}
|
|
</div>
|
|
<div class="toolbar-item is-right-aligned is-mobile-full-width">
|
|
<div class="button-bar">
|
|
<MultiSelectDropdown
|
|
data-test-datacenter-facet
|
|
@label="Datacenter"
|
|
@options={{this.optionsDatacenter}}
|
|
@selection={{this.selectionDatacenter}}
|
|
@onSelect={{action this.setFacetQueryParam "qpDatacenter"}}
|
|
/>
|
|
<MultiSelectDropdown
|
|
data-test-class-facet
|
|
@label="Class"
|
|
@options={{this.optionsClass}}
|
|
@selection={{this.selectionClass}}
|
|
@onSelect={{action this.setFacetQueryParam "qpClass"}}
|
|
/>
|
|
<MultiSelectDropdown
|
|
data-test-state-facet
|
|
@label="State"
|
|
@options={{this.optionsState}}
|
|
@selection={{this.selectionState}}
|
|
@onSelect={{action this.setFacetQueryParam "qpState"}}
|
|
/>
|
|
<MultiSelectDropdown
|
|
data-test-version-facet
|
|
@label="Version"
|
|
@options={{this.optionsVersion}}
|
|
@selection={{this.selectionVersion}}
|
|
@onSelect={{action this.setFacetQueryParam "qpVersion"}}
|
|
/>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<TopoViz
|
|
@nodes={{this.filteredNodes}}
|
|
@allocations={{this.model.allocations}}
|
|
@onAllocationSelect={{action this.setAllocation}}
|
|
@onNodeSelect={{action this.setNode}}
|
|
@onDataError={{action this.handleTopoVizDataError}}
|
|
@filters={{hash
|
|
search=this.searchTerm
|
|
clientState=this.selectionState
|
|
clientVersion=this.selectionVersion
|
|
}}
|
|
/>
|
|
</div>
|
|
</div>
|
|
{{/if}}
|
|
</section>
|
|
</PageLayout> |