github.com/Ilhicas/nomad@v1.0.4-0.20210304152020-e86851182bc3/ui/app/templates/clients/client/index.hbs (about) 1 {{page-title "Client " (or this.model.name this.model.shortId)}} 2 <ClientSubnav @client={{this.model}} /> 3 <section class="section"> 4 {{#if this.eligibilityError}} 5 <div data-test-eligibility-error class="columns"> 6 <div class="column"> 7 <div class="notification is-danger"> 8 <h3 data-test-title class="title is-4">Eligibility Error</h3> 9 <p data-test-message>{{this.eligibilityError}}</p> 10 </div> 11 </div> 12 <div class="column is-centered is-minimum"> 13 <button data-test-dismiss class="button is-danger" onclick={{action (mut this.eligibilityError) ""}} type="button">Okay</button> 14 </div> 15 </div> 16 {{/if}} 17 {{#if this.stopDrainError}} 18 <div data-test-stop-drain-error class="columns"> 19 <div class="column"> 20 <div class="notification is-danger"> 21 <h3 data-test-title class="title is-4">Stop Drain Error</h3> 22 <p data-test-message>{{this.stopDrainError}}</p> 23 </div> 24 </div> 25 <div class="column is-centered is-minimum"> 26 <button data-test-dismiss class="button is-danger" onclick={{action (mut this.stopDrainError) ""}} type="button">Okay</button> 27 </div> 28 </div> 29 {{/if}} 30 {{#if this.drainError}} 31 <div data-test-drain-error class="columns"> 32 <div class="column"> 33 <div class="notification is-danger"> 34 <h3 data-test-title class="title is-4">Drain Error</h3> 35 <p data-test-message>{{this.drainError}}</p> 36 </div> 37 </div> 38 <div class="column is-centered is-minimum"> 39 <button data-test-dismiss class="button is-danger" onclick={{action (mut this.drainError) ""}} type="button">Okay</button> 40 </div> 41 </div> 42 {{/if}} 43 {{#if this.showDrainStoppedNotification}} 44 <div class="notification is-info"> 45 <div data-test-drain-stopped-notification class="columns"> 46 <div class="column"> 47 <h3 data-test-title class="title is-4">Drain Stopped</h3> 48 <p data-test-message>The drain has been stopped and the node has been set to ineligible.</p> 49 </div> 50 <div class="column is-centered is-minimum"> 51 <button data-test-dismiss class="button is-info" onclick={{action (mut this.showDrainStoppedNotification) false}} type="button">Okay</button> 52 </div> 53 </div> 54 </div> 55 {{/if}} 56 {{#if this.showDrainUpdateNotification}} 57 <div class="notification is-info"> 58 <div data-test-drain-updated-notification class="columns"> 59 <div class="column"> 60 <h3 data-test-title class="title is-4">Drain Updated</h3> 61 <p data-test-message>The new drain specification has been applied.</p> 62 </div> 63 <div class="column is-centered is-minimum"> 64 <button data-test-dismiss class="button is-info" onclick={{action (mut this.showDrainUpdateNotification) false}} type="button">Okay</button> 65 </div> 66 </div> 67 </div> 68 {{/if}} 69 {{#if this.showDrainNotification}} 70 <div class="notification is-info"> 71 <div data-test-drain-complete-notification class="columns"> 72 <div class="column"> 73 <h3 data-test-title class="title is-4">Drain Complete</h3> 74 <p data-test-message>Allocations have been drained and the node has been set to ineligible.</p> 75 </div> 76 <div class="column is-centered is-minimum"> 77 <button data-test-dimiss class="button is-info" onclick={{action (mut this.showDrainNotification) false}} type="button">Okay</button> 78 </div> 79 </div> 80 </div> 81 {{/if}} 82 <div class="toolbar"> 83 <div class="toolbar-item is-top-aligned is-minimum"> 84 <span class="title"> 85 <span data-test-node-status="{{this.model.compositeStatus}}" class="node-status-light {{this.model.compositeStatus}}"> 86 {{x-icon this.model.compositeStatusIcon}} 87 </span> 88 </span> 89 </div> 90 <div class="toolbar-item"> 91 <h1 data-test-title class="title with-subheading"> 92 {{or this.model.name this.model.shortId}} 93 </h1> 94 <p> 95 <label class="is-interactive"> 96 <Toggle 97 data-test-eligibility-toggle 98 @isActive={{this.model.isEligible}} 99 @isDisabled={{or this.setEligibility.isRunning this.model.isDraining (cannot "write client")}} 100 @onToggle={{perform this.setEligibility (not this.model.isEligible)}}> 101 Eligible 102 </Toggle> 103 <span class="tooltip" aria-label="Only eligible clients can receive allocations"> 104 {{x-icon "info-circle-outline" class="is-faded"}} 105 </span> 106 </label> 107 <span data-test-node-id class="tag is-hollow is-small no-text-transform"> 108 {{this.model.id}} 109 <CopyButton @clipboardText={{this.model.id}} /> 110 </span> 111 </p> 112 </div> 113 <div class="toolbar-item is-right-aligned is-top-aligned"> 114 {{#if this.model.isDraining}} 115 <TwoStepButton 116 data-test-drain-stop 117 @idleText="Stop Drain" 118 @cancelText="Cancel" 119 @confirmText="Yes, Stop" 120 @confirmationMessage="Are you sure you want to stop this drain?" 121 @awaitingConfirmation={{this.stopDrain.isRunning}} 122 @onConfirm={{perform this.stopDrain}} /> 123 {{/if}} 124 </div> 125 <div class="toolbar-item is-right-aligned is-top-aligned"> 126 <DrainPopover 127 @client={{this.model}} 128 @isDisabled={{cannot "write client"}} 129 @onDrain={{action "drainNotify"}} 130 @onError={{action "setDrainError"}} /> 131 </div> 132 </div> 133 134 <div class="boxed-section is-small"> 135 <div class="boxed-section-body inline-definitions"> 136 <span class="label">Client Details</span> 137 <span class="pair" data-test-status-definition> 138 <span class="term">Status</span> 139 <span class="status-text node-{{this.model.status}}">{{this.model.status}}</span> 140 </span> 141 <span class="pair" data-test-address-definition> 142 <span class="term">Address</span> 143 {{this.model.httpAddr}} 144 </span> 145 <span class="pair" data-test-datacenter-definition> 146 <span class="term">Datacenter</span> 147 {{this.model.datacenter}} 148 </span> 149 {{#if this.model.nodeClass}} 150 <span class="pair" data-test-node-class> 151 <span class="term">Class</span> 152 {{this.model.nodeClass}} 153 </span> 154 {{/if}} 155 <span class="pair" data-test-driver-health> 156 <span class="term">Drivers</span> 157 {{#if this.model.unhealthyDrivers.length}} 158 {{x-icon "alert-triangle" class="is-text is-warning"}} 159 {{this.model.unhealthyDrivers.length}} of {{this.model.detectedDrivers.length}} {{pluralize "driver" this.model.detectedDrivers.length}} unhealthy 160 {{else}} 161 All healthy 162 {{/if}} 163 </span> 164 </div> 165 </div> 166 167 {{#if this.model.drainStrategy}} 168 <div data-test-drain-details class="boxed-section is-info"> 169 <div class="boxed-section-head"> 170 <div class="boxed-section-row">Drain Strategy</div> 171 <div class="boxed-section-row"> 172 <div class="inline-definitions is-small"> 173 {{#unless this.model.drainStrategy.hasNoDeadline}} 174 <span class="pair"> 175 <span class="term">Duration</span> 176 {{#if this.model.drainStrategy.isForced}} 177 <span data-test-duration>--</span> 178 {{else}} 179 <span data-test-duration class="tooltip" aria-label={{format-duration this.model.drainStrategy.deadline}}> 180 {{format-duration this.model.drainStrategy.deadline}} 181 </span> 182 {{/if}} 183 </span> 184 {{/unless}} 185 <span class="pair"> 186 <span class="term">{{if this.model.drainStrategy.hasNoDeadline "Deadline" "Remaining"}}</span> 187 {{#if this.model.drainStrategy.hasNoDeadline}} 188 <span data-test-deadline>No deadline</span> 189 {{else if this.model.drainStrategy.isForced}} 190 <span data-test-deadline>--</span> 191 {{else}} 192 <span data-test-deadline class="tooltip" aria-label={{format-ts this.model.drainStrategy.forceDeadline}}> 193 {{moment-from-now this.model.drainStrategy.forceDeadline interval=1000 hideAffix=true}} 194 </span> 195 {{/if}} 196 </span> 197 <span data-test-force-drain-text class="pair"> 198 <span class="term">Force Drain</span> 199 {{#if this.model.drainStrategy.isForced}} 200 {{x-icon "alert-triangle" class="is-text is-warning"}} Yes 201 {{else}} 202 No 203 {{/if}} 204 </span> 205 <span data-test-drain-system-jobs-text class="pair"> 206 <span class="term">Drain System Jobs</span> 207 {{if this.model.drainStrategy.ignoreSystemJobs "No" "Yes"}} 208 </span> 209 </div> 210 {{#unless this.model.drainStrategy.isForced}} 211 <div class="pull-right"> 212 <TwoStepButton 213 data-test-force 214 @alignRight={{true}} 215 @classes={{hash 216 idleButton="is-warning" 217 confirmationMessage="inherit-color" 218 cancelButton="is-danger is-important" 219 confirmButton="is-warning"}} 220 @idleText="Force Drain" 221 @cancelText="Cancel" 222 @confirmText="Yes, Force Drain" 223 @confirmationMessage="Are you sure you want to force drain?" 224 @awaitingConfirmation={{this.forceDrain.isRunning}} 225 @onConfirm={{perform this.forceDrain}} /> 226 </div> 227 {{/unless}} 228 </div> 229 </div> 230 <div class="boxed-section-body"> 231 <div class="columns"> 232 <div class="column nowrap is-minimum"> 233 <div class="metric-group"> 234 <div class="metric is-primary"> 235 <h3 class="label">Complete</h3> 236 <p data-test-complete-count class="value">{{this.model.completeAllocations.length}}</p> 237 </div> 238 </div> 239 <div class="metric-group"> 240 <div class="metric"> 241 <h3 class="label">Migrating</h3> 242 <p data-test-migrating-count class="value">{{this.model.migratingAllocations.length}}</p> 243 </div> 244 </div> 245 <div class="metric-group"> 246 <div class="metric"> 247 <h3 class="label">Remaining</h3> 248 <p data-test-remaining-count class="value">{{this.model.runningAllocations.length}}</p> 249 </div> 250 </div> 251 </div> 252 <div class="column"> 253 <h3 class="title is-4">Status</h3> 254 {{#if this.model.lastMigrateTime}} 255 <p data-test-status>{{moment-to-now this.model.lastMigrateTime interval=1000 hideAffix=true}} since an allocation was successfully migrated.</p> 256 {{else}} 257 <p data-test-status>No allocations migrated.</p> 258 {{/if}} 259 </div> 260 </div> 261 </div> 262 </div> 263 {{/if}} 264 265 <div class="boxed-section"> 266 <div class="boxed-section-head is-hollow"> 267 Resource Utilization 268 </div> 269 <div class="boxed-section-body"> 270 <div class="columns"> 271 <div class="column"> 272 <PrimaryMetric @resource={{this.model}} @metric="cpu" /> 273 </div> 274 <div class="column"> 275 <PrimaryMetric @resource={{this.model}} @metric="memory" /> 276 </div> 277 </div> 278 </div> 279 </div> 280 281 <div class="boxed-section"> 282 <div class="boxed-section-head"> 283 <div> 284 Allocations 285 <button role="button" class="badge is-white" onclick={{action "setPreemptionFilter" false}} data-test-filter-all type="button"> 286 {{this.model.allocations.length}} 287 </button> 288 {{#if this.preemptions.length}} 289 <button role="button" class="badge is-warning" onclick={{action "setPreemptionFilter" true}} data-test-filter-preemptions type="button"> 290 {{this.preemptions.length}} {{pluralize "preemption" this.preemptions.length}} 291 </button> 292 {{/if}} 293 </div> 294 <SearchBox 295 @searchTerm={{mut this.searchTerm}} 296 @onChange={{action this.resetPagination}} 297 @placeholder="Search allocations..." 298 @class="is-inline pull-right" 299 @inputClass="is-compact" /> 300 </div> 301 <div class="boxed-section-body is-full-bleed"> 302 <ListPagination 303 @source={{this.sortedAllocations}} 304 @size={{this.pageSize}} 305 @page={{this.currentPage}} as |p|> 306 <ListTable 307 @source={{p.list}} 308 @sortProperty={{this.sortProperty}} 309 @sortDescending={{this.sortDescending}} 310 @class="with-foot" as |t|> 311 <t.head> 312 <th class="is-narrow"></th> 313 <t.sort-by @prop="shortId">ID</t.sort-by> 314 <t.sort-by @prop="createIndex" @title="Create Index">Created</t.sort-by> 315 <t.sort-by @prop="modifyIndex" @title="Modify Index">Modified</t.sort-by> 316 <t.sort-by @prop="statusIndex">Status</t.sort-by> 317 <t.sort-by @prop="job.name">Job</t.sort-by> 318 <t.sort-by @prop="jobVersion">Version</t.sort-by> 319 <th>Volume</th> 320 <th>CPU</th> 321 <th>Memory</th> 322 </t.head> 323 <t.body as |row|> 324 <AllocationRow 325 @allocation={{row.model}} 326 @context="node" 327 @onClick={{action "gotoAllocation" row.model}} 328 @data-test-allocation={{row.model.id}} /> 329 </t.body> 330 </ListTable> 331 <div class="table-foot"> 332 <nav class="pagination"> 333 <div class="pagination-numbers"> 334 {{p.startsAt}}–{{p.endsAt}} of {{this.sortedAllocations.length}} 335 </div> 336 <p.prev @class="pagination-previous"> < </p.prev> 337 <p.next @class="pagination-next"> > </p.next> 338 <ul class="pagination-list"></ul> 339 </nav> 340 </div> 341 </ListPagination> 342 </div> 343 </div> 344 345 <div data-test-client-events class="boxed-section"> 346 <div class="boxed-section-head"> 347 Client Events 348 </div> 349 <div class="boxed-section-body is-full-bleed"> 350 <ListTable @source={{this.sortedEvents}} @class="is-striped" as |t|> 351 <t.head> 352 <th class="is-2">Time</th> 353 <th class="is-2">Subsystem</th> 354 <th>Message</th> 355 </t.head> 356 <t.body as |row|> 357 <tr data-test-client-event> 358 <td data-test-client-event-time>{{format-ts row.model.time}}</td> 359 <td data-test-client-event-subsystem>{{row.model.subsystem}}</td> 360 <td data-test-client-event-message> 361 {{#if row.model.message}} 362 {{#if row.model.driver}} 363 <span class="badge is-secondary is-small">{{row.model.driver}}</span> 364 {{/if}} 365 {{row.model.message}} 366 {{else}} 367 <em>No message</em> 368 {{/if}} 369 </td> 370 </tr> 371 </t.body> 372 </ListTable> 373 </div> 374 </div> 375 376 {{#if this.sortedHostVolumes.length}} 377 <div data-test-client-host-volumes class="boxed-section"> 378 <div class="boxed-section-head"> 379 Host Volumes 380 </div> 381 <div class="boxed-section-body is-full-bleed"> 382 <ListTable @source={{this.sortedHostVolumes}} @class="is-striped" as |t|> 383 <t.head> 384 <th>Name</th> 385 <th>Source</th> 386 <th>Permissions</th> 387 </t.head> 388 <t.body as |row|> 389 <tr data-test-client-host-volume> 390 <td data-test-name>{{row.model.name}}</td> 391 <td data-test-path><code>{{row.model.path}}</code></td> 392 <td data-test-permissions>{{if row.model.readOnly "Read" "Read/Write"}}</td> 393 </tr> 394 </t.body> 395 </ListTable> 396 </div> 397 </div> 398 {{/if}} 399 400 <div data-test-driver-status class="boxed-section"> 401 <div class="boxed-section-head"> 402 Driver Status 403 </div> 404 <div class="boxed-section-body"> 405 <ListAccordion @source={{this.sortedDrivers}} @key="name" as |a|> 406 <a.head @buttonLabel="details" @isExpandable={{a.item.detected}}> 407 <div class="columns inline-definitions {{unless a.item.detected "is-faded"}}"> 408 <div class="column is-1"> 409 <span data-test-name>{{a.item.name}}</span> 410 </div> 411 <div class="column is-2"> 412 {{#if a.item.detected}} 413 <span data-test-health> 414 <span class="color-swatch {{a.item.healthClass}}"></span> 415 {{if a.item.healthy "Healthy" "Unhealthy"}} 416 </span> 417 {{/if}} 418 </div> 419 <div class="column"> 420 <span class="pair"> 421 <span class="term">Detected</span> 422 <span data-test-detected>{{if a.item.detected "Yes" "No"}}</span> 423 </span> 424 <span class="is-pulled-right"> 425 <span class="pair"> 426 <span class="term">Last Updated</span> 427 <span data-test-last-updated class="tooltip" aria-label="{{format-ts a.item.updateTime}}"> 428 {{moment-from-now a.item.updateTime interval=1000}} 429 </span> 430 </span> 431 </span> 432 </div> 433 </div> 434 </a.head> 435 <a.body> 436 <p data-test-health-description class="message">{{a.item.healthDescription}}</p> 437 <div data-test-driver-attributes class="boxed-section"> 438 <div class="boxed-section-head"> 439 {{capitalize a.item.name}} Attributes 440 </div> 441 {{#if a.item.attributes.attributesStructured}} 442 <div class="boxed-section-body is-full-bleed"> 443 <AttributesTable 444 @attributePairs={{a.item.attributesShort}} 445 @class="attributes-table" /> 446 </div> 447 {{else}} 448 <div class="boxed-section-body"> 449 <div class="empty-message"> 450 <h3 class="empty-message-headline">No Driver Attributes</h3> 451 </div> 452 </div> 453 {{/if}} 454 </div> 455 </a.body> 456 </ListAccordion> 457 </div> 458 </div> 459 460 <div class="boxed-section"> 461 <div class="boxed-section-head"> 462 Attributes 463 </div> 464 <div class="boxed-section-body is-full-bleed"> 465 <AttributesTable 466 data-test-attributes 467 @attributePairs={{this.model.attributes.attributesStructured}} 468 @class="attributes-table" /> 469 </div> 470 <div class="boxed-section-head"> 471 Meta 472 </div> 473 {{#if this.model.meta.attributesStructured}} 474 <div class="boxed-section-body is-full-bleed"> 475 <AttributesTable 476 data-test-meta 477 @attributePairs={{this.model.meta.attributesStructured}} 478 @class="attributes-table" /> 479 </div> 480 {{else}} 481 <div class="boxed-section-body"> 482 <div data-test-empty-meta-message class="empty-message"> 483 <h3 class="empty-message-headline">No Meta Attributes</h3> 484 <p class="empty-message-body">This client is configured with no meta attributes.</p> 485 </div> 486 </div> 487 {{/if}} 488 </div> 489 </section>