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