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}}&ndash;{{p.endsAt}} of {{sortedAllocations.length}}
   324              </div>
   325              {{#p.prev class="pagination-previous"}} &lt; {{/p.prev}}
   326              {{#p.next class="pagination-next"}} &gt; {{/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>