]> git.apps.os.sepia.ceph.com Git - ceph.git/commitdiff
add graphs to rgw overview dashboard 52405/head
authorAashish Sharma <aasharma@li-e74156cc-2f67-11b2-a85c-e98659a63c5c.ibm.com>
Thu, 20 Jul 2023 09:08:59 +0000 (14:38 +0530)
committerAashish Sharma <aasharma@li-e74156cc-2f67-11b2-a85c-e98659a63c5c.ibm.com>
Thu, 3 Aug 2023 05:45:23 +0000 (11:15 +0530)
Signed-off-by: Aashish Sharma <aasharma@redhat.com>
(cherry picked from commit a63026348cb4fcb287046c6dc5432d5709d5dff8)

src/pybind/mgr/dashboard/frontend/src/app/ceph/dashboard-v3/dashboard-area-chart/dashboard-area-chart.component.ts
src/pybind/mgr/dashboard/frontend/src/app/ceph/dashboard-v3/dashboard-v3.module.ts
src/pybind/mgr/dashboard/frontend/src/app/ceph/dashboard-v3/dashboard/dashboard-v3.component.ts
src/pybind/mgr/dashboard/frontend/src/app/ceph/rgw/rgw-overview-dashboard/rgw-overview-dashboard.component.html
src/pybind/mgr/dashboard/frontend/src/app/ceph/rgw/rgw-overview-dashboard/rgw-overview-dashboard.component.scss
src/pybind/mgr/dashboard/frontend/src/app/ceph/rgw/rgw-overview-dashboard/rgw-overview-dashboard.component.spec.ts
src/pybind/mgr/dashboard/frontend/src/app/ceph/rgw/rgw-overview-dashboard/rgw-overview-dashboard.component.ts
src/pybind/mgr/dashboard/frontend/src/app/shared/api/prometheus.service.ts
src/pybind/mgr/dashboard/frontend/src/app/shared/enum/dashboard-promqls.enum.ts
src/pybind/mgr/dashboard/frontend/src/app/shared/services/formatter.service.ts
src/pybind/mgr/dashboard/frontend/src/app/shared/services/number-formatter.service.ts

index e06746bf9b1feb3ce06bf02a8d94911e09088d0c..35d8e571068cb61898dc68dc4cff1c0ab43b98e2 100644 (file)
@@ -73,6 +73,10 @@ export class DashboardAreaChartComponent implements OnChanges, AfterViewInit {
       display: false
     },
     tooltips: {
+      custom: function (tooltipModel: { x: number; y: number }) {
+        tooltipModel.x = 10;
+        tooltipModel.y = 0;
+      }.bind(this),
       intersect: false,
       displayColors: true,
       backgroundColor: this.cssHelper.propertyValue('chart-color-tooltip-background'),
@@ -120,7 +124,7 @@ export class DashboardAreaChartComponent implements OnChanges, AfterViewInit {
       borderArea: true,
       chartAreaBorder: {
         borderColor: this.cssHelper.propertyValue('chart-color-slight-dark-gray'),
-        borderWidth: 2
+        borderWidth: 1
       }
     }
   };
@@ -195,7 +199,7 @@ export class DashboardAreaChartComponent implements OnChanges, AfterViewInit {
 
   private convertToChartDataUnits(data: any): any {
     let dataWithUnits: string = '';
-    if (this.chartDataUnits) {
+    if (this.chartDataUnits !== null) {
       if (this.dataUnits === 'B') {
         dataWithUnits = this.numberFormatter.formatBytesFromTo(
           data,
index 6e55f98531dd1f50965bc07a732fa8a500cb9120..9e1529c3e3794042468d698f030457532cf2c372 100644 (file)
@@ -42,6 +42,12 @@ import { PgSummaryPipe } from './pg-summary.pipe';
     DashboardTimeSelectorComponent
   ],
 
-  exports: [DashboardV3Component, CardComponent, CardRowComponent]
+  exports: [
+    DashboardV3Component,
+    CardComponent,
+    CardRowComponent,
+    DashboardAreaChartComponent,
+    DashboardTimeSelectorComponent
+  ]
 })
 export class DashboardV3Module {}
index a3bd264c6843507d1766d802b1c1d293372ae11c..a682d7e5d8e7210211be1cfe48137069e2666184 100644 (file)
@@ -1,7 +1,7 @@
 import { Component, OnDestroy, OnInit } from '@angular/core';
 
 import _ from 'lodash';
-import { Observable, Subscription, timer } from 'rxjs';
+import { Observable, Subscription } from 'rxjs';
 import { take } from 'rxjs/operators';
 import moment from 'moment';
 
@@ -165,54 +165,11 @@ export class DashboardV3Component extends PrometheusListHelper implements OnInit
     });
   }
 
-  getPrometheusData(selectedTime: any) {
-    this.prometheusService.ifPrometheusConfigured(() => {
-      if (this.timerGetPrometheusDataSub) {
-        this.timerGetPrometheusDataSub.unsubscribe();
-      }
-      this.timerGetPrometheusDataSub = timer(0, this.timerTime).subscribe(() => {
-        selectedTime = this.updateTimeStamp(selectedTime);
-
-        for (const queryName in queries) {
-          if (queries.hasOwnProperty(queryName)) {
-            const query = queries[queryName];
-            let interval = selectedTime.step;
-
-            if (query.includes('rate') && selectedTime.step < 20) {
-              interval = 20;
-            } else if (query.includes('rate')) {
-              interval = selectedTime.step * 2;
-            }
-
-            const intervalAdjustedQuery = query.replace(/\[(.*?)\]/g, `[${interval}s]`);
-
-            this.prometheusService
-              .getPrometheusData({
-                params: intervalAdjustedQuery,
-                start: selectedTime['start'],
-                end: selectedTime['end'],
-                step: selectedTime['step']
-              })
-              .subscribe((data: any) => {
-                if (data.result.length) {
-                  this.queriesResults[queryName] = data.result[0].values;
-                }
-              });
-          }
-        }
-      });
-    });
-  }
-
-  private updateTimeStamp(selectedTime: any): any {
-    let formattedDate = {};
-    const date: number = selectedTime['start'] + this.timerTime / 1000;
-    const dateNow: number = selectedTime['end'] + this.timerTime / 1000;
-    formattedDate = {
-      start: date,
-      end: dateNow,
-      step: selectedTime['step']
-    };
-    return formattedDate;
+  public getPrometheusData(selectedTime: any) {
+    this.queriesResults = this.prometheusService.getPrometheusQueriesData(
+      selectedTime,
+      queries,
+      this.queriesResults
+    );
   }
 }
index 5b11eb4293411ea366a482c84a02c28e3c04e34b..41efb5d65ca11162c50bee2968d4044faa6b8caa 100644 (file)
@@ -1,71 +1,97 @@
 <div class="container-fluid">
   <div class="row">
-    <div class="col-lg-3">
-      <cd-card cardTitle="Inventory"
-               i18n-title
-               class="col-sm-3 px-3"
-               aria-label="Inventory card">
-        <hr>
-        <li class="list-group-item">
-          <cd-card-row [data]="rgwDaemonCount"
-                       link="/rgw/daemon"
-                       title="Gateway"
-                       summaryType="simplified"
-                       *ngIf="rgwDaemonCount != null"></cd-card-row>
-        </li>
-        <hr>
-        <li class="list-group-item">
-          <cd-card-row [data]="rgwRealmCount"
-                       link="/rgw/multisite"
-                       title="Realm"
-                       summaryType="simplified"
-                       *ngIf="rgwRealmCount != null"></cd-card-row>
-        </li>
-        <hr>
-        <li class="list-group-item">
-          <cd-card-row [data]="rgwZonegroupCount"
-                       link="/rgw/multisite"
-                       title="Zonegroup"
-                       summaryType="simplified"
-                       *ngIf="rgwZonegroupCount != null"></cd-card-row>
-        </li>
-        <hr>
-        <li class="list-group-item">
-          <cd-card-row [data]="rgwZoneCount"
-                       link="/rgw/multisite"
-                       title="Zone"
-                       summaryType="simplified"
-                       *ngIf="rgwZoneCount != null"></cd-card-row>
-        </li>
-        <hr>
-        <li class="list-group-item">
-          <cd-card-row [data]="rgwBucketCount"
-                       link="/rgw/bucket"
-                       title="Bucket"
-                       summaryType="simplified"
-                       *ngIf="rgwBucketCount != null"></cd-card-row>
-        </li>
-        <hr>
-        <li class="list-group-item">
-          <cd-card-row [data]="UserCount"
-                       link="/rgw/user"
-                       title="User"
-                       summaryType="simplified"
-                       *ngIf="UserCount != null"></cd-card-row>
-        </li>
-        <hr>
-        <li class="list-group-item">
-          <cd-card-row [data]="objectCount"
-                       title="Object"
-                       summaryType="simplified"
-                       *ngIf="objectCount != null"></cd-card-row>
-        </li>
-      </cd-card>
-    </div>
+    <cd-card cardTitle="Inventory"
+             i18n-title
+             class="col-sm-3 px-3 d-flex"
+             aria-label="Inventory card">
+      <hr>
+      <li class="list-group-item">
+        <cd-card-row [data]="rgwDaemonCount"
+                     link="/rgw/daemon"
+                     title="Daemon"
+                     summaryType="simplified"
+                     *ngIf="rgwDaemonCount != null"></cd-card-row>
+      </li>
+      <hr>
+      <li class="list-group-item">
+        <cd-card-row [data]="rgwRealmCount"
+                     link="/rgw/multisite"
+                     title="Realm"
+                     summaryType="simplified"
+                     *ngIf="rgwRealmCount != null"></cd-card-row>
+      </li>
+      <hr>
+      <li class="list-group-item">
+        <cd-card-row [data]="rgwZonegroupCount"
+                     link="/rgw/multisite"
+                     title="Zonegroup"
+                     summaryType="simplified"
+                     *ngIf="rgwZonegroupCount != null"></cd-card-row>
+      </li>
+      <hr>
+      <li class="list-group-item">
+        <cd-card-row [data]="rgwZoneCount"
+                     link="/rgw/multisite"
+                     title="Zone"
+                     summaryType="simplified"
+                     *ngIf="rgwZoneCount != null"></cd-card-row>
+      </li>
+      <hr>
+      <li class="list-group-item">
+        <cd-card-row [data]="rgwBucketCount"
+                     link="/rgw/bucket"
+                     title="Bucket"
+                     summaryType="simplified"
+                     *ngIf="rgwBucketCount != null"></cd-card-row>
+      </li>
+      <hr>
+      <li class="list-group-item">
+        <cd-card-row [data]="UserCount"
+                     link="/rgw/user"
+                     title="User"
+                     summaryType="simplified"
+                     *ngIf="UserCount != null"></cd-card-row>
+      </li>
+      <hr>
+      <li class="list-group-item">
+        <cd-card-row [data]="objectCount"
+                     title="Object"
+                     summaryType="simplified"
+                     *ngIf="objectCount != null"></cd-card-row>
+      </li>
+    </cd-card>
+    <cd-card cardTitle="Performance Statistics"
+             i18n-title
+             class="col-sm-6 d-flex"
+             ria-label="Performance Statistics card">
+      <div class="ms-4 me-4 mt-0">
+        <cd-dashboard-time-selector (selectedTime)="getPrometheusData($event)">
+        </cd-dashboard-time-selector>
+        <cd-dashboard-area-chart chartTitle="Requests/sec"
+                                 dataUnits=""
+                                 label="Requests/sec"
+                                 [data]="queriesResults.RGW_REQUEST_PER_SECOND">
+        </cd-dashboard-area-chart>
+        <cd-dashboard-area-chart chartTitle="Latency"
+                                 dataUnits="ms"
+                                 label="GET"
+                                 label2="PUT"
+                                 [data]="queriesResults.AVG_GET_LATENCY"
+                                 [data2]="queriesResults.AVG_PUT_LATENCY">
+        </cd-dashboard-area-chart>
+        <cd-dashboard-area-chart chartTitle="Bandwidth"
+                                 dataUnits="B"
+                                 label="GET"
+                                 label2="PUT"
+                                 [data]="queriesResults.GET_BANDWIDTH"
+                                 [data2]="queriesResults.PUT_BANDWIDTH">
+        </cd-dashboard-area-chart>
+      </div>
+    </cd-card>
     <div class="col-lg-3">
       <cd-card cardTitle="Used Capacity"
                i18n-title
-               class="col-sm-2 px-3 d-flex w-100 h-50 mt-4 pb-5"
+               class="col-sm-2 d-flex w-100 h-50 pb-3"
                aria-label="Details card">
         <span  class="ms-4 me-4 text-center">
           <h1>{{ totalPoolUsedBytes | dimlessBinary}}</h1>
@@ -73,7 +99,7 @@
       </cd-card>
       <cd-card cardTitle="Avg Object Size"
                i18n-title
-               class="col-sm-2 px-3 d-flex w-100 h-50 mt-2 pb-5"
+               class="col-sm-2 d-flex w-100 h-50 pt-3"
                aria-label="Details card">
         <span class="ms-4 me-4 text-center">
           <h1>{{ averageObjectSize | dimlessBinary}}</h1>
     </div>
   </div>
 </div>
+
index 482fa0f0245da6a907ce9f57da2dc55b4f4976c2..f887bcd8224f726a2cf6417526f240996c19e42b 100644 (file)
@@ -168,7 +168,7 @@ describe('RgwOverviewDashboardComponent', () => {
   it('should render all cards', () => {
     fixture.detectChanges();
     const dashboardCards = fixture.debugElement.nativeElement.querySelectorAll('cd-card');
-    expect(dashboardCards.length).toBe(3);
+    expect(dashboardCards.length).toBe(4);
   });
 
   it('should get corresponding data into Daemons', () => {
index a3116834b5963a5dff892db95b54be951c74e01b..81634fe950344a1a73169c44eb929122540cda18 100644 (file)
@@ -3,8 +3,8 @@ import { Component, OnDestroy, OnInit } from '@angular/core';
 import _ from 'lodash';
 import { Subscription } from 'rxjs';
 
-import { HealthService } from '~/app/shared/api/health.service';
 import { Permissions } from '~/app/shared/models/permissions';
+import { AuthStorageService } from '~/app/shared/services/auth-storage.service';
 import { RefreshIntervalService } from '~/app/shared/services/refresh-interval.service';
 import { RgwDaemonService } from '~/app/shared/api/rgw-daemon.service';
 import { RgwRealmService } from '~/app/shared/api/rgw-realm.service';
@@ -12,11 +12,9 @@ import { RgwZoneService } from '~/app/shared/api/rgw-zone.service';
 import { RgwZonegroupService } from '~/app/shared/api/rgw-zonegroup.service';
 import { RgwBucketService } from '~/app/shared/api/rgw-bucket.service';
 import { RgwUserService } from '~/app/shared/api/rgw-user.service';
-import { AuthStorageService } from '~/app/shared/services/auth-storage.service';
-import {
-  FeatureTogglesMap$,
-  FeatureTogglesService
-} from '~/app/shared/services/feature-toggles.service';
+import { PrometheusService } from '~/app/shared/api/prometheus.service';
+import { RgwPromqls as queries } from '~/app/shared/enum/dashboard-promqls.enum';
+import { HealthService } from '~/app/shared/api/health.service';
 
 @Component({
   selector: 'cd-rgw-overview-dashboard',
@@ -26,7 +24,6 @@ import {
 export class RgwOverviewDashboardComponent implements OnInit, OnDestroy {
   interval = new Subscription();
   permissions: Permissions;
-  enabledFeature$: FeatureTogglesMap$;
   rgwDaemonCount = 0;
   rgwRealmCount = 0;
   rgwZonegroupCount = 0;
@@ -44,10 +41,16 @@ export class RgwOverviewDashboardComponent implements OnInit, OnDestroy {
   UserSub: Subscription;
   HealthSub: Subscription;
   BucketSub: Subscription;
+  queriesResults: any = {
+    RGW_REQUEST_PER_SECOND: '',
+    BANDWIDTH: '',
+    AVG_GET_LATENCY: '',
+    AVG_PUT_LATENCY: ''
+  };
+  timerGetPrometheusDataSub: Subscription;
 
   constructor(
     private authStorageService: AuthStorageService,
-    private featureToggles: FeatureTogglesService,
     private healthService: HealthService,
     private refreshIntervalService: RefreshIntervalService,
     private rgwDaemonService: RgwDaemonService,
@@ -55,10 +58,10 @@ export class RgwOverviewDashboardComponent implements OnInit, OnDestroy {
     private rgwZonegroupService: RgwZonegroupService,
     private rgwZoneService: RgwZoneService,
     private rgwBucketService: RgwBucketService,
-    private rgwUserService: RgwUserService
+    private rgwUserService: RgwUserService,
+    private prometheusService: PrometheusService
   ) {
     this.permissions = this.authStorageService.getPermissions();
-    this.enabledFeature$ = this.featureToggles.get();
   }
 
   ngOnInit() {
@@ -87,6 +90,7 @@ export class RgwOverviewDashboardComponent implements OnInit, OnDestroy {
     this.ZoneSUb = this.rgwZoneService.list().subscribe((data: any) => {
       this.rgwZoneCount = data['zones'].length;
     });
+    this.getPrometheusData(this.prometheusService.lastHourDateObject);
   }
 
   ngOnDestroy() {
@@ -98,5 +102,17 @@ export class RgwOverviewDashboardComponent implements OnInit, OnDestroy {
     this.BucketSub.unsubscribe();
     this.UserSub.unsubscribe();
     this.HealthSub.unsubscribe();
+    if (this.timerGetPrometheusDataSub) {
+      this.timerGetPrometheusDataSub.unsubscribe();
+    }
+  }
+
+  getPrometheusData(selectedTime: any) {
+    this.queriesResults = this.prometheusService.getPrometheusQueriesData(
+      selectedTime,
+      queries,
+      this.queriesResults,
+      true
+    );
   }
 }
index 340f89ca38f8ff9f9d7d5d83ac5de8f18ce8f4a5..21420d7629fb5c6e8395d4018299c658086a780e 100644 (file)
@@ -1,7 +1,7 @@
 import { HttpClient } from '@angular/common/http';
 import { Injectable } from '@angular/core';
 
-import { Observable } from 'rxjs';
+import { Observable, Subscription, timer } from 'rxjs';
 import { map } from 'rxjs/operators';
 
 import { AlertmanagerSilence } from '../models/alertmanager-silence';
@@ -10,11 +10,19 @@ import {
   AlertmanagerNotification,
   PrometheusRuleGroup
 } from '../models/prometheus-alerts';
+import moment from 'moment';
 
 @Injectable({
   providedIn: 'root'
 })
 export class PrometheusService {
+  timerGetPrometheusDataSub: Subscription;
+  timerTime = 30000;
+  readonly lastHourDateObject = {
+    start: moment().unix() - 3600,
+    end: moment().unix(),
+    step: 14
+  };
   private baseURL = 'api/prometheus';
   private settingsKey = {
     alertmanager: 'ui-api/prometheus/alertmanager-api-host',
@@ -115,4 +123,64 @@ export class PrometheusService {
   private getSettingsValue(data: any): string {
     return data.value || data.instance || '';
   }
+
+  getPrometheusQueriesData(
+    selectedTime: any,
+    queries: any,
+    queriesResults: any,
+    checkNan?: boolean
+  ) {
+    this.ifPrometheusConfigured(() => {
+      if (this.timerGetPrometheusDataSub) {
+        this.timerGetPrometheusDataSub.unsubscribe();
+      }
+      this.timerGetPrometheusDataSub = timer(0, this.timerTime).subscribe(() => {
+        selectedTime = this.updateTimeStamp(selectedTime);
+
+        for (const queryName in queries) {
+          if (queries.hasOwnProperty(queryName)) {
+            const query = queries[queryName];
+            this.getPrometheusData({
+              params: encodeURIComponent(query),
+              start: selectedTime['start'],
+              end: selectedTime['end'],
+              step: selectedTime['step']
+            }).subscribe((data: any) => {
+              if (data.result.length) {
+                queriesResults[queryName] = data.result[0].values;
+              }
+              if (
+                queriesResults[queryName] !== undefined &&
+                queriesResults[queryName] !== '' &&
+                checkNan
+              ) {
+                queriesResults[queryName].forEach((valueArray: string[]) => {
+                  if (valueArray.includes('NaN')) {
+                    const index = valueArray.indexOf('NaN');
+                    if (index !== -1) {
+                      valueArray[index] = '0';
+                    }
+                  }
+                });
+              }
+            });
+          }
+        }
+      });
+    });
+    return queriesResults;
+  }
+
+  private updateTimeStamp(selectedTime: any): any {
+    let formattedDate = {};
+    let secondsAgo = selectedTime['end'] - selectedTime['start'];
+    const date: number = moment().unix() - secondsAgo;
+    const dateNow: number = moment().unix();
+    formattedDate = {
+      start: date,
+      end: dateNow,
+      step: selectedTime['step']
+    };
+    return formattedDate;
+  }
 }
index 7afd069978d6a36ab75541ba040020a393ed7063..3f76fa828f57e978b44d401b7f49317d5786c68a 100644 (file)
@@ -1,10 +1,18 @@
 export enum Promqls {
   USEDCAPACITY = 'ceph_cluster_total_used_bytes',
-  IPS = 'sum(rate(ceph_osd_op_w_in_bytes[$interval]))',
-  OPS = 'sum(rate(ceph_osd_op_r_out_bytes[$interval]))',
-  READLATENCY = 'avg_over_time(ceph_osd_apply_latency_ms[$interval])',
-  WRITELATENCY = 'avg_over_time(ceph_osd_commit_latency_ms[$interval])',
-  READCLIENTTHROUGHPUT = 'sum(rate(ceph_pool_rd_bytes[$interval]))',
-  WRITECLIENTTHROUGHPUT = 'sum(rate(ceph_pool_wr_bytes[$interval]))',
-  RECOVERYBYTES = 'sum(rate(ceph_osd_recovery_bytes[$interval]))'
+  IPS = 'sum(rate(ceph_osd_op_w_in_bytes[1m]))',
+  OPS = 'sum(rate(ceph_osd_op_r_out_bytes[1m]))',
+  READLATENCY = 'avg_over_time(ceph_osd_apply_latency_ms[1m])',
+  WRITELATENCY = 'avg_over_time(ceph_osd_commit_latency_ms[1m])',
+  READCLIENTTHROUGHPUT = 'sum(rate(ceph_pool_rd_bytes[1m]))',
+  WRITECLIENTTHROUGHPUT = 'sum(rate(ceph_pool_wr_bytes[1m]))',
+  RECOVERYBYTES = 'sum(rate(ceph_osd_recovery_bytes[1m]))'
+}
+
+export enum RgwPromqls {
+  RGW_REQUEST_PER_SECOND = 'sum(rate(ceph_rgw_req[1m]))',
+  AVG_GET_LATENCY = 'sum(rate(ceph_rgw_get_initial_lat_sum[1m])) / sum(rate(ceph_rgw_get_initial_lat_count[1m]))',
+  AVG_PUT_LATENCY = 'sum(rate(ceph_rgw_put_initial_lat_sum[1m])) / sum(rate(ceph_rgw_put_initial_lat_count[1m]))',
+  GET_BANDWIDTH = 'sum(rate(ceph_rgw_get_b[1m]))',
+  PUT_BANDWIDTH = 'sum(rate(ceph_rgw_put_b[1m]))'
 }
index b212004d2b1bd2fec8764e52a628e1fed1792f2a..eacba3cf16dccddce9bf0529fef024d216d86845 100644 (file)
@@ -41,7 +41,7 @@ export class FormatterService {
   formatNumberFromTo(
     n: any,
     units: any,
-    targetedUnits: string,
+    targetedUnits: string = '',
     conversionFactor: number,
     unitsArray: string[],
     decimals: number = 1
index 9643f19211452a649f46b82609d36e478b3f012f..16b9587e23abae828b7383383e606907394fb57f 100644 (file)
@@ -25,7 +25,7 @@ export class NumberFormatterService {
   formatFromTo(
     value: any,
     units: string,
-    targetedUnits: string,
+    targetedUnits: string = '',
     factor: number,
     labels: string[],
     decimals: number = 1
@@ -60,7 +60,7 @@ export class NumberFormatterService {
   formatUnitlessFromTo(
     value: any,
     units: string,
-    targetedUnits: string,
+    targetedUnits: string = '',
     decimals: number = 1
   ): any {
     return this.formatFromTo(value, units, targetedUnits, 1000, this.unitlessLabels, decimals);