File

src/app/ceph/dashboard/health/health.component.ts

Implements

OnInit OnDestroy

Metadata

selector cd-health
styleUrls ./health.component.scss
templateUrl ./health.component.html

Index

Properties
Methods

Constructor

constructor(healthService: HealthService, i18n: I18n, authStorageService: AuthStorageService, pgCategoryService: PgCategoryService, featureToggles: FeatureTogglesService, refreshIntervalService: RefreshIntervalService)
Parameters :
Name Type Optional
healthService HealthService No
i18n I18n No
authStorageService AuthStorageService No
pgCategoryService PgCategoryService No
featureToggles FeatureTogglesService No
refreshIntervalService RefreshIntervalService No

Methods

getHealth
getHealth()
Returns : void
isClientReadWriteChartShowable
isClientReadWriteChartShowable()
Returns : boolean
ngOnDestroy
ngOnDestroy()
Returns : void
ngOnInit
ngOnInit()
Returns : void
preparePgStatus
preparePgStatus(chart, data)
Parameters :
Name Optional
chart No
data No
Returns : void
prepareRawUsage
prepareRawUsage(chart, data)
Parameters :
Name Optional
chart No
data No
Returns : void
prepareReadWriteRatio
prepareReadWriteRatio(chart)
Parameters :
Name Optional
chart No
Returns : void

Properties

enabledFeature$
Type : FeatureTogglesMap$
healthData
Type : any
interval
Default value : new Subscription()
permissions
Type : Permissions
import { Component, OnDestroy, OnInit } from '@angular/core';

import { I18n } from '@ngx-translate/i18n-polyfill';
import * as _ from 'lodash';
import { Subscription } from 'rxjs/Subscription';

import { HealthService } from '../../../shared/api/health.service';
import { Permissions } from '../../../shared/models/permissions';
import { AuthStorageService } from '../../../shared/services/auth-storage.service';
import {
  FeatureTogglesMap$,
  FeatureTogglesService
} from '../../../shared/services/feature-toggles.service';
import { RefreshIntervalService } from '../../../shared/services/refresh-interval.service';
import { PgCategoryService } from '../../shared/pg-category.service';
import { HealthPieColor } from '../health-pie/health-pie-color.enum';

@Component({
  selector: 'cd-health',
  templateUrl: './health.component.html',
  styleUrls: ['./health.component.scss']
})
export class HealthComponent implements OnInit, OnDestroy {
  healthData: any;
  interval = new Subscription();
  permissions: Permissions;
  enabledFeature$: FeatureTogglesMap$;

  constructor(
    private healthService: HealthService,
    private i18n: I18n,
    private authStorageService: AuthStorageService,
    private pgCategoryService: PgCategoryService,
    private featureToggles: FeatureTogglesService,
    private refreshIntervalService: RefreshIntervalService
  ) {
    this.permissions = this.authStorageService.getPermissions();
    this.enabledFeature$ = this.featureToggles.get();
  }

  ngOnInit() {
    this.getHealth();
    this.interval = this.refreshIntervalService.intervalData$.subscribe(() => {
      this.getHealth();
    });
  }

  ngOnDestroy() {
    this.interval.unsubscribe();
  }

  getHealth() {
    this.healthService.getMinimalHealth().subscribe((data: any) => {
      this.healthData = data;
    });
  }

  prepareReadWriteRatio(chart) {
    const ratioLabels = [];
    const ratioData = [];

    ratioLabels.push(this.i18n('Writes'));
    ratioData.push(this.healthData.client_perf.write_op_per_sec);
    ratioLabels.push(this.i18n('Reads'));
    ratioData.push(this.healthData.client_perf.read_op_per_sec);

    chart.dataset[0].data = ratioData;
    chart.labels = ratioLabels;
  }

  prepareRawUsage(chart, data) {
    const percentAvailable = Math.round(
      100 *
        ((data.df.stats.total_bytes - data.df.stats.total_used_raw_bytes) /
          data.df.stats.total_bytes)
    );

    const percentUsed = Math.round(
      100 * (data.df.stats.total_used_raw_bytes / data.df.stats.total_bytes)
    );

    chart.dataset[0].data = [data.df.stats.total_used_raw_bytes, data.df.stats.total_avail_bytes];
    if (chart === 'doughnut') {
      chart.options.cutoutPercentage = 65;
    }
    chart.labels = [
      `${this.i18n('Used')} (${percentUsed}%)`,
      `${this.i18n('Avail.')} (${percentAvailable}%)`
    ];
  }

  preparePgStatus(chart, data) {
    const categoryPgAmount = {};
    chart.labels = [
      this.i18n('Clean'),
      this.i18n('Working'),
      this.i18n('Warning'),
      this.i18n('Unknown')
    ];
    chart.colors = [
      {
        backgroundColor: [
          HealthPieColor.DEFAULT_GREEN,
          HealthPieColor.DEFAULT_BLUE,
          HealthPieColor.DEFAULT_ORANGE,
          HealthPieColor.DEFAULT_RED
        ]
      }
    ];

    _.forEach(data.pg_info.statuses, (pgAmount, pgStatesText) => {
      const categoryType = this.pgCategoryService.getTypeByStates(pgStatesText);

      if (_.isUndefined(categoryPgAmount[categoryType])) {
        categoryPgAmount[categoryType] = 0;
      }
      categoryPgAmount[categoryType] += pgAmount;
    });

    chart.dataset[0].data = this.pgCategoryService
      .getAllTypes()
      .map((categoryType) => categoryPgAmount[categoryType]);
  }

  isClientReadWriteChartShowable() {
    const readOps = this.healthData.client_perf.read_op_per_sec || 0;
    const writeOps = this.healthData.client_perf.write_op_per_sec || 0;

    return readOps + writeOps > 0;
  }
}
<div *ngIf="healthData && enabledFeature$ | async as enabledFeature"
     class="container-fluid">
  <cd-info-group groupTitle="Status"
                 i18n-groupTitle
                 class="row info-group"
                 *ngIf="healthData.health?.status
                 || healthData.mon_status
                 || healthData.osd_map
                 || healthData.mgr_map
                 || healthData.hosts != null
                 || healthData.rgw != null
                 || healthData.fs_map
                 || healthData.iscsi_daemons != null">

    <cd-info-card cardTitle="Cluster Status"
                  i18n-cardTitle
                  class="col-sm-6 col-md-4 col-lg-3"
                  [contentClass]="healthData.health?.checks?.length > 0 ? 'content-highlight text-area-size-2' : 'content-highlight'"
                  *ngIf="healthData.health?.status">
      <ng-container *ngIf="healthData.health?.checks?.length > 0">
        <ng-template #healthChecks>
          <ng-container *ngTemplateOutlet="logsLink"></ng-container>
          <ul>
            <li *ngFor="let check of healthData.health.checks">
              <span [ngStyle]="check.severity | healthColor">{{ check.type }}</span>: {{ check.summary.message }}
            </li>
          </ul>
        </ng-template>
        <div class="info-card-content-clickable"
             [ngStyle]="healthData.health.status | healthColor"
             [popover]="healthChecks"
             triggers=""
             #healthChecksTarget="bs-popover"
             placement="bottom"
             container="body"
             containerClass="info-card-popover-cluster-status"
             (click)="healthChecksTarget.toggle()">
          {{ healthData.health.status }}
        </div>
      </ng-container>
      <ng-container *ngIf="!healthData.health?.checks?.length">
        <div [ngStyle]="healthData.health.status | healthColor">
          {{ healthData.health.status }}
        </div>
      </ng-container>
    </cd-info-card>

    <cd-info-card cardTitle="Monitors"
                  i18n-cardTitle
                  link="/monitor"
                  class="col-sm-6 col-md-4 col-lg-3"
                  contentClass="content-highlight"
                  *ngIf="healthData.mon_status">
      {{ healthData.mon_status | monSummary }}
    </cd-info-card>

    <cd-info-card cardTitle="OSDs"
                  i18n-cardTitle
                  link="/osd"
                  class="col-sm-6 col-md-4 col-lg-3"
                  *ngIf="(healthData.osd_map | osdSummary) as transformedResult"
                  [contentClass]="(transformedResult.length == 5 ? 'text-area-size-3' : 'text-area-size-2') + ' content-highlight'">
      <span *ngFor="let result of transformedResult"
            [ngClass]="result.class">
        {{ result.content }}
      </span>
    </cd-info-card>

    <cd-info-card cardTitle="Manager Daemons"
                  i18n-cardTitle
                  class="col-sm-6 col-md-4 col-lg-3"
                  contentClass="content-highlight text-area-size-2"
                  *ngIf="healthData.mgr_map">
      <span *ngFor="let result of (healthData.mgr_map | mgrSummary)"
            [ngClass]="result.class"
            [title]="result.titleText != null ? result.titleText : ''">
        {{ result.content }}
      </span>
    </cd-info-card>

    <cd-info-card cardTitle="Hosts"
                  i18n-cardTitle
                  link="/hosts"
                  class="col-sm-6 col-md-4 col-lg-3"
                  contentClass="content-medium content-highlight"
                  *ngIf="healthData.hosts != null">
      {{ healthData.hosts }} total
    </cd-info-card>

    <cd-info-card cardTitle="Object Gateways"
                  i18n-cardTitle
                  link="/rgw/daemon"
                  class="col-sm-6 col-md-4 col-lg-3"
                  contentClass="content-medium content-highlight"
                  *ngIf="enabledFeature.rgw && healthData.rgw != null">
      {{ healthData.rgw }} total
    </cd-info-card>
    <cd-info-card cardTitle="Metadata Servers"
                  i18n-cardTitle
                  class="col-sm-6 col-md-4 col-lg-3"
                  *ngIf="(enabledFeature.cephfs && healthData.fs_map | mdsSummary) as transformedResult"
                  [contentClass]="(transformedResult.length > 1 ? 'text-area-size-2' : '') + ' content-highlight'">
      <span *ngFor="let result of transformedResult"
            [ngClass]="result.class">
        {{ result.content }}
      </span>
    </cd-info-card>

    <cd-info-card cardTitle="iSCSI Gateways"
                  i18n-cardTitle
                  link="/block/iscsi"
                  class="col-sm-6 col-md-4 col-lg-3"
                  contentClass="content-medium content-highlight"
                  *ngIf="enabledFeature.iscsi && healthData.iscsi_daemons != null">
      {{ healthData.iscsi_daemons }} total
    </cd-info-card>
  </cd-info-group>

  <cd-info-group groupTitle="Performance"
                 i18n-groupTitle
                 class="row info-group"
                 *ngIf="healthData.client_perf || healthData.scrub_status">

    <div class="cd-container-flex">
      <cd-info-card cardTitle="Client IOPS"
                    i18n-cardTitle
                    class="cd-col-5"
                    cardClass="card-medium"
                    contentClass="content-medium content-highlight"
                    *ngIf="healthData.client_perf">
        {{ (healthData.client_perf.read_op_per_sec + healthData.client_perf.write_op_per_sec) | round:1 }}
      </cd-info-card>

      <cd-info-card cardTitle="Client Throughput"
                    i18n-cardTitle
                    class="cd-col-5"
                    cardClass="card-medium"
                    contentClass="content-medium content-highlight"
                    *ngIf="healthData.client_perf">
        {{ ((healthData.client_perf.read_bytes_sec + healthData.client_perf.write_bytes_sec) | dimlessBinary) + '/s' }}
      </cd-info-card>

      <cd-info-card cardTitle="Client Read/Write"
                    i18n-cardTitle
                    class="cd-col-5"
                    cardClass="card-medium"
                    [contentClass]="isClientReadWriteChartShowable() ? 'content-chart': 'content-medium content-highlight'"
                    *ngIf="healthData.client_perf">
        <cd-health-pie *ngIf="isClientReadWriteChartShowable()"
                       [data]="healthData"
                       [isBytesData]="false"
                       chartType="pie"
                       [displayLegend]="true"
                       (prepareFn)="prepareReadWriteRatio($event[0], $event[1])">
        </cd-health-pie>
        <span *ngIf="!isClientReadWriteChartShowable()">
          N/A
        </span>
      </cd-info-card>

      <cd-info-card cardTitle="Recovery Throughput"
                    i18n-cardTitle
                    class="cd-col-5"
                    cardClass="card-medium"
                    contentClass="content-medium content-highlight"
                    *ngIf="healthData.client_perf">
        {{ (healthData.client_perf.recovering_bytes_per_sec | dimlessBinary) + '/s' }}
      </cd-info-card>

      <cd-info-card cardTitle="Scrub"
                    i18n-cardTitle
                    class="cd-col-5"
                    cardClass="card-medium"
                    contentClass="content-medium content-highlight"
                    *ngIf="healthData.scrub_status">
        {{ healthData.scrub_status }}
      </cd-info-card>
    </div>
  </cd-info-group>

  <cd-info-group groupTitle="Capacity"
                 i18n-groupTitle
                 class="row info-group"
                 *ngIf="healthData.pools
                 || healthData.df
                 || healthData.df?.stats?.total_objects != null
                 || healthData.pg_info">

    <div class="cd-container-flex">
      <cd-info-card cardTitle="Pools"
                    i18n-cardTitle
                    link="/pool"
                    class="cd-col-5"
                    cardClass="card-medium"
                    contentClass="content-medium content-highlight"
                    *ngIf="healthData.pools">
        {{ healthData.pools.length }}
      </cd-info-card>

      <cd-info-card cardTitle="Raw Capacity"
                    i18n-cardTitle
                    class="cd-col-5"
                    cardClass="card-medium"
                    contentClass="content-chart"
                    *ngIf="healthData.df">
        <cd-health-pie [data]="healthData"
                       [isBytesData]="true"
                       [displayLegend]="true"
                       (prepareFn)="prepareRawUsage($event[0], $event[1])">
        </cd-health-pie>
      </cd-info-card>

      <cd-info-card cardTitle="Objects"
                    i18n-cardTitle
                    class="cd-col-5"
                    cardClass="card-medium"
                    contentClass="content-medium content-highlight"
                    *ngIf="healthData.df?.stats?.total_objects != null">
        {{ healthData.df?.stats?.total_objects }}
      </cd-info-card>

      <cd-info-card cardTitle="PGs per OSD"
                    i18n-cardTitle
                    class="cd-col-5"
                    cardClass="card-medium"
                    contentClass="content-medium content-highlight"
                    *ngIf="healthData.pg_info">
        {{ healthData.pg_info.pgs_per_osd | dimless }}
      </cd-info-card>

      <cd-info-card cardTitle="PG Status"
                    i18n-cardTitle
                    class="cd-col-5"
                    cardClass="card-medium"
                    contentClass="content-chart"
                    (click)="pgStatusTarget.toggle()"
                    *ngIf="healthData.pg_info">
        <ng-template #pgStatus>
          <ng-container *ngTemplateOutlet="logsLink"></ng-container>
          <ul>
            <li *ngFor="let pgStatesText of healthData.pg_info.statuses | keyvalue">
              {{ pgStatesText.key }}: {{ pgStatesText.value }}
            </li>
          </ul>
        </ng-template>
        <div class="pg-status-popover-wrapper">
          <div [popover]="pgStatus"
               triggers=""
               #pgStatusTarget="bs-popover"
               placement="bottom">
            <cd-health-pie [data]="healthData"
                           chartType="pie"
                           [displayLegend]="true"
                           (prepareFn)="preparePgStatus($event[0], $event[1])">
            </cd-health-pie>
          </div>
        </div>
      </cd-info-card>
    </div>
  </cd-info-group>

  <ng-template #logsLink>
    <ng-container *ngIf="permissions.log.read">
      <p class="logs-link"
         i18n><i class="fa fa-info-circle"></i> See <a routerLink="/logs">Logs</a> for more details.</p>
    </ng-container>
  </ng-template>
</div>

./health.component.scss

@import '../../../../defaults';

cd-info-card {
  padding: 0 0.5vw 0 0.5vw;
}

.cd-container-flex {
  margin: 0;
  padding: 0;
  display: -webkit-box;
  display: -webkit-flex;
  display: -ms-flexbox;
  display: flex;
  flex-flow: row wrap;
  justify-content: space-between;
}

.cd-col-5 {
  width: 20%;
}

@media (max-width: 1599px) {
  .cd-col-5 {
    width: 25%;
  }
}

@media (max-width: $screen-md-max) {
  .cd-col-5 {
    width: 33%;
  }
}

@media (max-width: $screen-sm-max) {
  .cd-col-5 {
    width: 50%;
  }
}

@media (max-width: $screen-xs-max) {
  cd-info-card {
    padding: 0;
  }

  .cd-col-5 {
    width: 100%;
  }
}

.info-group {
  margin: 0;
  padding: 0;
}

::ng-deep .pg-status-popover-wrapper {
  position: relative;

  .popover {
    position: absolute;
    width: 116%;
    max-height: 20vh;
    min-width: unset !important;
    max-width: unset !important;

    .popover-body {
      max-width: 100%;
      max-height: 19vh;
      font-size: 12px;
    }
  }
}

.logs-link {
  text-align: center;
}

.card-text-error {
  display: inline;
  color: $color-solid-red;
}

.card-text-line-break:after {
  content: '\A';
  white-space: pre;
}

.mgr-active-name:hover {
  cursor: pointer;
}
Legend
Html element
Component
Html element with directive

result-matching ""

    No results matching ""