github.com/hernad/nomad@v1.6.112/ui/app/templates/topology.hbs (about) 1 {{! 2 Copyright (c) HashiCorp, Inc. 3 SPDX-License-Identifier: MPL-2.0 4 ~}} 5 6 <Breadcrumb @crumb={{hash label="Topology" args=(array "topology")}} /> 7 {{page-title "Cluster Topology"}} 8 <PageLayout> 9 <section class="section is-full-width"> 10 {{#if this.isForbidden}} 11 <ForbiddenMessage /> 12 {{else}} 13 {{#if this.pre09Nodes}} 14 <div class="notification is-warning"> 15 <div data-test-filtered-nodes-warning class="columns"> 16 <div class="column"> 17 <h3 data-test-title class="title is-4"> 18 Some Clients Were Filtered 19 </h3> 20 <p data-test-message> 21 {{this.pre09Nodes.length}} 22 {{if (eq this.pre09Nodes.length 1) "client was" "clients were"}} 23 filtered from the topology visualization. This is most likely due to the 24 {{pluralize "client" this.pre09Nodes.length}} 25 running a version of Nomad 26 </p> 27 </div> 28 <div class="column is-centered is-minimum"> 29 <button 30 data-test-dismiss 31 class="button is-warning" 32 onclick={{action (mut this.pre09Nodes) null}} 33 type="button" 34 > 35 Okay 36 </button> 37 </div> 38 </div> 39 </div> 40 {{/if}} 41 <div class="columns"> 42 <div class="column is-narrow is-400"> 43 {{#if this.showPollingNotice}} 44 <div class="notification is-warning"> 45 <div class="columns"> 46 <div class="column"> 47 <h3 class="title is-4"> 48 No Live Updating 49 </h3> 50 <p> 51 The topology visualization depends on too much data to continuously poll. 52 </p> 53 </div> 54 <div class="column is-centered is-minimum"> 55 <button 56 data-test-polling-notice-dismiss 57 class="button is-warning" 58 type="button" 59 onclick={{toggle-action "showPollingNotice" this}} 60 > 61 Okay 62 </button> 63 </div> 64 </div> 65 </div> 66 {{/if}} 67 <div class="boxed-section"> 68 <div class="boxed-section-head"> 69 Legend 70 {{#if (cannot "list all jobs")}} 71 <span 72 aria-label="Your ACL token may limit your ability to list all allocations" 73 class="tag is-warning pull-right tooltip multiline" 74 > 75 Partial View 76 </span> 77 {{/if}} 78 </div> 79 <div class="boxed-section-body"> 80 <div class="legend"> 81 <h3 class="legend-label"> 82 Metrics 83 </h3> 84 <dl class="legend-terms"> 85 <dt> 86 M: 87 </dt> 88 <dd> 89 Memory 90 </dd> 91 <dt> 92 C: 93 </dt> 94 <dd> 95 CPU 96 </dd> 97 </dl> 98 </div> 99 <div class="legend"> 100 <h3 class="legend-label"> 101 Allocation Status 102 </h3> 103 <dl class="legend-terms"> 104 <div class="legend-term"> 105 <dt> 106 <span class="color-swatch is-wide running" title="Running"></span> 107 </dt> 108 <dd> 109 Running 110 </dd> 111 </div> 112 <div class="legend-term"> 113 <dt> 114 <span class="color-swatch is-wide pending" title="Starting"></span> 115 </dt> 116 <dd> 117 Starting 118 </dd> 119 </div> 120 </dl> 121 </div> 122 </div> 123 </div> 124 <div class="boxed-section"> 125 <div data-test-info-panel-title class="boxed-section-head"> 126 {{#if this.activeNode}} 127 Client 128 {{else if this.activeAllocation}} 129 Allocation 130 {{else}} 131 Cluster 132 {{/if}} 133 Details 134 </div> 135 <div data-test-info-panel class="boxed-section-body"> 136 {{#if this.activeNode}} 137 {{#let this.activeNode.node as |node|}} 138 <div class="dashboard-metric"> 139 <p data-test-allocations class="metric"> 140 {{this.activeNode.allocations.length}} 141 <span class="metric-label"> 142 Allocations 143 </span> 144 </p> 145 </div> 146 <div class="dashboard-metric"> 147 <h3 class="pair"> 148 <strong> 149 Client: 150 </strong> 151 <LinkTo data-test-client-link @route="clients.client" @model={{node}}> 152 {{node.shortId}} 153 </LinkTo> 154 </h3> 155 <p data-test-name class="minor-pair"> 156 <strong> 157 Name: 158 </strong> 159 {{node.name}} 160 </p> 161 <p data-test-address class="minor-pair"> 162 <strong> 163 Address: 164 </strong> 165 {{node.httpAddr}} 166 </p> 167 <p data-test-status class="minor-pair"> 168 <strong> 169 Status: 170 </strong> 171 {{node.status}} 172 </p> 173 </div> 174 <div class="dashboard-metric"> 175 <h3 class="pair"> 176 <strong> 177 Draining? 178 </strong> 179 <span data-test-draining class="{{if node.isDraining "status-text is-info"}}"> 180 {{if node.isDraining "Yes" "No"}} 181 </span> 182 </h3> 183 <h3 class="pair"> 184 <strong> 185 Eligible? 186 </strong> 187 <span 188 data-test-eligible 189 class="{{unless node.isEligible "status-text is-warning"}}" 190 > 191 {{if node.isEligible "Yes" "No"}} 192 </span> 193 </h3> 194 </div> 195 <div class="dashboard-metric with-divider"> 196 <p class="metric"> 197 {{this.nodeUtilization.totalMemoryFormatted}} 198 <span class="metric-units"> 199 {{this.nodeUtilization.totalMemoryUnits}} 200 </span> 201 <span class="metric-label"> 202 of memory 203 </span> 204 </p> 205 <div class="columns graphic"> 206 <div class="column"> 207 <div class="inline-chart"> 208 <progress 209 data-test-memory-progress-bar 210 class="progress is-danger is-small" 211 value="{{this.nodeUtilization.reservedMemoryPercent}}" 212 max="1" 213 > 214 {{this.nodeUtilization.reservedMemoryPercent}} 215 </progress> 216 </div> 217 </div> 218 <div class="column is-minimum"> 219 <span class="nowrap" data-test-percentage> 220 {{format-percentage this.nodeUtilization.reservedMemoryPercent total=1}} 221 </span> 222 </div> 223 </div> 224 <div class="annotation" data-test-memory-absolute-value> 225 <strong> 226 {{format-scheduled-bytes this.nodeUtilization.totalReservedMemory}} 227 </strong> 228 / 229 {{format-scheduled-bytes this.nodeUtilization.totalMemory}} 230 reserved 231 </div> 232 </div> 233 <div class="dashboard-metric"> 234 <p class="metric"> 235 {{this.nodeUtilization.totalCPU}} 236 <span class="metric-units"> 237 MHz 238 </span> 239 <span class="metric-label"> 240 of CPU 241 </span> 242 </p> 243 <div class="columns graphic"> 244 <div class="column"> 245 <div class="inline-chart" data-test-percentage-bar> 246 <progress 247 data-test-cpu-progress-bar 248 class="progress is-info is-small" 249 value="{{this.nodeUtilization.reservedCPUPercent}}" 250 max="1" 251 > 252 {{this.nodeUtilization.reservedCPUPercent}} 253 </progress> 254 </div> 255 </div> 256 <div class="column is-minimum"> 257 <span class="nowrap" data-test-percentage> 258 {{format-percentage this.nodeUtilization.reservedCPUPercent total=1}} 259 </span> 260 </div> 261 </div> 262 <div class="annotation" data-test-cpu-absolute-value> 263 <strong> 264 {{format-scheduled-hertz this.nodeUtilization.totalReservedCPU}} 265 </strong> 266 / 267 {{format-scheduled-hertz this.nodeUtilization.totalCPU}} 268 reserved 269 </div> 270 </div> 271 {{/let}} 272 {{else if this.activeAllocation}} 273 <div class="dashboard-metric"> 274 <h3 class="pair"> 275 <strong> 276 Allocation: 277 </strong> 278 <LinkTo 279 data-test-id 280 @route="allocations.allocation" 281 @model={{this.activeAllocation}} 282 class="is-primary" 283 > 284 {{this.activeAllocation.shortId}} 285 </LinkTo> 286 </h3> 287 <p data-test-sibling-allocs class="minor-pair"> 288 <strong> 289 Sibling Allocations: 290 </strong> 291 {{this.siblingAllocations.length}} 292 </p> 293 <p data-test-unique-placements class="minor-pair"> 294 <strong> 295 Unique Client Placements: 296 </strong> 297 {{this.uniqueActiveAllocationNodes.length}} 298 </p> 299 </div> 300 <div class="dashboard-metric with-divider"> 301 <h3 class="pair"> 302 <strong> 303 Job: 304 </strong> 305 <LinkTo 306 data-test-job 307 @route="jobs.job" 308 @model={{this.activeAllocation.job}} 309 > 310 {{this.activeAllocation.job.name}} 311 </LinkTo> 312 <span class="is-faded" data-test-task-group> 313 / 314 {{this.activeAllocation.taskGroupName}} 315 </span> 316 </h3> 317 <p class="minor-pair"> 318 <strong> 319 Type: 320 </strong> 321 {{this.activeAllocation.job.type}} 322 </p> 323 <p class="minor-pair"> 324 <strong> 325 Priority: 326 </strong> 327 {{this.activeAllocation.job.priority}} 328 </p> 329 </div> 330 <div class="dashboard-metric with-divider"> 331 <h3 class="pair"> 332 <strong> 333 Client: 334 </strong> 335 <LinkTo 336 data-test-client 337 @route="clients.client" 338 @model={{this.activeAllocation.node}} 339 > 340 {{this.activeAllocation.node.shortId}} 341 </LinkTo> 342 </h3> 343 <p class="minor-pair"> 344 <strong> 345 Name: 346 </strong> 347 {{this.activeAllocation.node.name}} 348 </p> 349 <p class="minor-pair"> 350 <strong> 351 Address: 352 </strong> 353 {{this.activeAllocation.node.httpAddr}} 354 </p> 355 </div> 356 <div class="dashboard-metric with-divider"> 357 <PrimaryMetric::Allocation 358 @allocation={{this.activeAllocation}} 359 @metric="memory" 360 class="is-short" 361 /> 362 </div> 363 <div class="dashboard-metric"> 364 <PrimaryMetric::Allocation 365 @allocation={{this.activeAllocation}} 366 @metric="cpu" 367 class="is-short" 368 /> 369 </div> 370 {{else}} 371 <div class="columns is-flush"> 372 <div class="dashboard-metric column"> 373 <p data-test-node-count class="metric justify"> 374 {{this.model.nodes.length}} 375 <span class="metric-label"> 376 Clients 377 </span> 378 </p> 379 </div> 380 <div class="dashboard-metric column"> 381 <p data-test-alloc-count class="metric justify"> 382 {{this.scheduledAllocations.length}} 383 <span class="metric-label"> 384 Allocations 385 </span> 386 </p> 387 </div> 388 <div class="dashboard-metric column"> 389 <p data-test-node-pool-count class="metric justify"> 390 {{this.model.nodePools.length}} 391 <span class="metric-label"> 392 Node Pools 393 </span> 394 </p> 395 </div> 396 </div> 397 <div class="dashboard-metric with-divider"> 398 <p class="metric"> 399 {{this.totalMemoryFormatted}} 400 <span class="metric-units"> 401 {{this.totalMemoryUnits}} 402 </span> 403 <span class="metric-label"> 404 of memory 405 </span> 406 </p> 407 <div class="columns graphic"> 408 <div class="column"> 409 <div class="inline-chart" data-test-percentage-bar> 410 <progress 411 data-test-memory-progress-bar 412 class="progress is-danger is-small" 413 value="{{this.reservedMemoryPercent}}" 414 max="1" 415 > 416 {{this.reservedMemoryPercent}} 417 </progress> 418 </div> 419 </div> 420 <div class="column is-minimum"> 421 <span class="nowrap" data-test-memory-percentage> 422 {{format-percentage this.reservedMemoryPercent total=1}} 423 </span> 424 </div> 425 </div> 426 <div class="annotation" data-test-memory-absolute-value> 427 <strong> 428 {{format-bytes this.totalReservedMemory}} 429 </strong> 430 / 431 {{format-bytes this.totalMemory}} 432 reserved 433 </div> 434 </div> 435 <div class="dashboard-metric"> 436 <p class="metric"> 437 {{this.totalCPUFormatted}} 438 <span class="metric-units"> 439 {{this.totalCPUUnits}} 440 </span> 441 <span class="metric-label"> 442 of CPU 443 </span> 444 </p> 445 <div class="columns graphic"> 446 <div class="column"> 447 <div class="inline-chart" data-test-percentage-bar> 448 <progress 449 data-test-cpu-progress-bar 450 class="progress is-info is-small" 451 value="{{this.reservedCPUPercent}}" 452 max="1" 453 > 454 {{this.reservedCPUPercent}} 455 </progress> 456 </div> 457 </div> 458 <div class="column is-minimum"> 459 <span class="nowrap" data-test-cpu-percentage> 460 {{format-percentage this.reservedCPUPercent total=1}} 461 </span> 462 </div> 463 </div> 464 <div class="annotation" data-test-cpu-absolute-value> 465 <strong> 466 {{format-hertz this.totalReservedCPU}} 467 </strong> 468 / 469 {{format-hertz this.totalCPU}} 470 reserved 471 </div> 472 </div> 473 {{/if}} 474 </div> 475 </div> 476 </div> 477 <div class="column"> 478 <div class="toolbar"> 479 <div class="toolbar-item"> 480 {{#if this.model.nodes.length}} 481 <SearchBox 482 @inputClass="node-search" 483 @searchTerm={{mut this.searchTerm}} 484 @placeholder="Search clients..." 485 /> 486 {{/if}} 487 </div> 488 <div class="toolbar-item is-right-aligned is-mobile-full-width"> 489 <div class="button-bar"> 490 <MultiSelectDropdown 491 data-test-node-pool-facet 492 @label="Node Pool" 493 @options={{this.optionsNodePool}} 494 @selection={{this.selectionNodePool}} 495 @onSelect={{action this.setFacetQueryParam "qpNodePool"}} 496 /> 497 <MultiSelectDropdown 498 data-test-datacenter-facet 499 @label="Datacenter" 500 @options={{this.optionsDatacenter}} 501 @selection={{this.selectionDatacenter}} 502 @onSelect={{action this.setFacetQueryParam "qpDatacenter"}} 503 /> 504 <MultiSelectDropdown 505 data-test-class-facet 506 @label="Class" 507 @options={{this.optionsClass}} 508 @selection={{this.selectionClass}} 509 @onSelect={{action this.setFacetQueryParam "qpClass"}} 510 /> 511 <MultiSelectDropdown 512 data-test-state-facet 513 @label="State" 514 @options={{this.optionsState}} 515 @selection={{this.selectionState}} 516 @onSelect={{action this.setFacetQueryParam "qpState"}} 517 /> 518 <MultiSelectDropdown 519 data-test-version-facet 520 @label="Version" 521 @options={{this.optionsVersion}} 522 @selection={{this.selectionVersion}} 523 @onSelect={{action this.setFacetQueryParam "qpVersion"}} 524 /> 525 </div> 526 </div> 527 </div> 528 <TopoViz 529 @nodes={{this.filteredNodes}} 530 @allocations={{this.model.allocations}} 531 @onAllocationSelect={{action this.setAllocation}} 532 @onNodeSelect={{action this.setNode}} 533 @onDataError={{action this.handleTopoVizDataError}} 534 @filters={{hash 535 search=this.searchTerm 536 clientState=this.selectionState 537 clientVersion=this.selectionVersion 538 }} 539 /> 540 </div> 541 </div> 542 {{/if}} 543 </section> 544 </PageLayout>