]> git.proxmox.com Git - ceph.git/blobdiff - ceph/src/pybind/mgr/dashboard/frontend/src/app/ceph/dashboard/health/health.component.ts
import ceph nautilus 14.2.2
[ceph.git] / ceph / src / pybind / mgr / dashboard / frontend / src / app / ceph / dashboard / health / health.component.ts
index c5748642d357c18c4fe925c30244ae3f61e65edc..6cca3b5cac495862d4e0960c1deca8eda5e91fd8 100644 (file)
@@ -6,6 +6,8 @@ import { Subscription } from 'rxjs/Subscription';
 
 import { HealthService } from '../../../shared/api/health.service';
 import { Permissions } from '../../../shared/models/permissions';
+import { DimlessBinaryPipe } from '../../../shared/pipes/dimless-binary.pipe';
+import { DimlessPipe } from '../../../shared/pipes/dimless.pipe';
 import { AuthStorageService } from '../../../shared/services/auth-storage.service';
 import {
   FeatureTogglesMap$,
@@ -26,13 +28,48 @@ export class HealthComponent implements OnInit, OnDestroy {
   permissions: Permissions;
   enabledFeature$: FeatureTogglesMap$;
 
+  rawCapacityChartConfig = {
+    options: {
+      title: { display: true, position: 'bottom' }
+    }
+  };
+  objectsChartConfig = {
+    options: {
+      title: { display: true, position: 'bottom' }
+    },
+    colors: [
+      {
+        backgroundColor: [
+          HealthPieColor.DEFAULT_GREEN,
+          HealthPieColor.DEFAULT_MAGENTA,
+          HealthPieColor.DEFAULT_ORANGE,
+          HealthPieColor.DEFAULT_RED
+        ]
+      }
+    ]
+  };
+  pgStatusChartConfig = {
+    colors: [
+      {
+        backgroundColor: [
+          HealthPieColor.DEFAULT_GREEN,
+          HealthPieColor.DEFAULT_BLUE,
+          HealthPieColor.DEFAULT_ORANGE,
+          HealthPieColor.DEFAULT_RED
+        ]
+      }
+    ]
+  };
+
   constructor(
     private healthService: HealthService,
     private i18n: I18n,
     private authStorageService: AuthStorageService,
     private pgCategoryService: PgCategoryService,
     private featureToggles: FeatureTogglesService,
-    private refreshIntervalService: RefreshIntervalService
+    private refreshIntervalService: RefreshIntervalService,
+    private dimlessBinary: DimlessBinaryPipe,
+    private dimless: DimlessPipe
   ) {
     this.permissions = this.authStorageService.getPermissions();
     this.enabledFeature$ = this.featureToggles.get();
@@ -59,9 +96,22 @@ export class HealthComponent implements OnInit, OnDestroy {
     const ratioLabels = [];
     const ratioData = [];
 
-    ratioLabels.push(this.i18n('Writes'));
+    const total =
+      this.healthData.client_perf.write_op_per_sec + this.healthData.client_perf.read_op_per_sec;
+
+    ratioLabels.push(
+      `${this.i18n('Writes')} (${this.calcPercentage(
+        this.healthData.client_perf.write_op_per_sec,
+        total
+      )}%)`
+    );
     ratioData.push(this.healthData.client_perf.write_op_per_sec);
-    ratioLabels.push(this.i18n('Reads'));
+    ratioLabels.push(
+      `${this.i18n('Reads')} (${this.calcPercentage(
+        this.healthData.client_perf.read_op_per_sec,
+        total
+      )}%)`
+    );
     ratioData.push(this.healthData.client_perf.read_op_per_sec);
 
     chart.dataset[0].data = ratioData;
@@ -69,44 +119,33 @@ export class HealthComponent implements OnInit, OnDestroy {
   }
 
   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 percentAvailable = this.calcPercentage(
+      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)
+    const percentUsed = this.calcPercentage(
+      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}%)`
+      `${this.dimlessBinary.transform(data.df.stats.total_used_raw_bytes)} ${this.i18n(
+        'Used'
+      )} (${percentUsed}%)`,
+      `${this.dimlessBinary.transform(
+        data.df.stats.total_bytes - data.df.stats.total_used_raw_bytes
+      )} ${this.i18n('Avail.')} (${percentAvailable}%)`
     ];
+
+    chart.options.title.text = `${this.dimlessBinary.transform(
+      data.df.stats.total_bytes
+    )} ${this.i18n('total')}`;
   }
 
   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);
@@ -120,6 +159,63 @@ export class HealthComponent implements OnInit, OnDestroy {
     chart.dataset[0].data = this.pgCategoryService
       .getAllTypes()
       .map((categoryType) => categoryPgAmount[categoryType]);
+
+    chart.labels = [
+      `${this.i18n('Clean')} (${this.calcPercentage(
+        categoryPgAmount['clean'],
+        data.pg_info.pgs_per_osd
+      )}%)`,
+      `${this.i18n('Working')} (${this.calcPercentage(
+        categoryPgAmount['working'],
+        data.pg_info.pgs_per_osd
+      )}%)`,
+      `${this.i18n('Warning')} (${this.calcPercentage(
+        categoryPgAmount['warning'],
+        data.pg_info.pgs_per_osd
+      )}%)`,
+      `${this.i18n('Unknown')} (${this.calcPercentage(
+        categoryPgAmount['unknown'],
+        data.pg_info.pgs_per_osd
+      )}%)`
+    ];
+  }
+
+  prepareObjects(chart, data) {
+    const totalReplicas = data.pg_info.object_stats.num_object_copies;
+    const healthy =
+      totalReplicas -
+      data.pg_info.object_stats.num_objects_misplaced -
+      data.pg_info.object_stats.num_objects_degraded -
+      data.pg_info.object_stats.num_objects_unfound;
+
+    chart.labels = [
+      `${this.i18n('Healthy')} (${this.calcPercentage(healthy, totalReplicas)}%)`,
+      `${this.i18n('Misplaced')} (${this.calcPercentage(
+        data.pg_info.object_stats.num_objects_misplaced,
+        totalReplicas
+      )}%)`,
+      `${this.i18n('Degraded')} (${this.calcPercentage(
+        data.pg_info.object_stats.num_objects_degraded,
+        totalReplicas
+      )}%)`,
+      `${this.i18n('Unfound')} (${this.calcPercentage(
+        data.pg_info.object_stats.num_objects_unfound,
+        totalReplicas
+      )}%)`
+    ];
+
+    chart.dataset[0].data = [
+      healthy,
+      data.pg_info.object_stats.num_objects_misplaced,
+      data.pg_info.object_stats.num_objects_degraded,
+      data.pg_info.object_stats.num_objects_unfound
+    ];
+
+    chart.options.title.text = `${this.dimless.transform(
+      data.pg_info.object_stats.num_objects
+    )} ${this.i18n('total')} (${this.dimless.transform(totalReplicas)} ${this.i18n('replicas')})`;
+
+    chart.options.maintainAspectRatio = window.innerWidth >= 375;
   }
 
   isClientReadWriteChartShowable() {
@@ -128,4 +224,12 @@ export class HealthComponent implements OnInit, OnDestroy {
 
     return readOps + writeOps > 0;
   }
+
+  private calcPercentage(dividend: number, divisor: number) {
+    if (!_.isNumber(dividend) || !_.isNumber(divisor) || divisor === 0) {
+      return 0;
+    }
+
+    return Math.round((dividend / divisor) * 100);
+  }
 }