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}}&ndash;{{p.endsAt}} of {{this.sortedAllocations.length}}
   335              </div>
   336              <p.prev @class="pagination-previous"> &lt; </p.prev>
   337              <p.next @class="pagination-next"> &gt; </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>