From 16c27b34e35c08e30aa2da753c2b97af14db4003 Mon Sep 17 00:00:00 2001 From: Aashish Sharma Date: Thu, 20 Jul 2023 14:38:59 +0530 Subject: [PATCH] add graphs to rgw overview dashboard Signed-off-by: Aashish Sharma (cherry picked from commit a63026348cb4fcb287046c6dc5432d5709d5dff8) --- .../dashboard-area-chart.component.ts | 8 +- .../ceph/dashboard-v3/dashboard-v3.module.ts | 8 +- .../dashboard/dashboard-v3.component.ts | 57 +------ .../rgw-overview-dashboard.component.html | 155 ++++++++++-------- .../rgw-overview-dashboard.component.scss | 4 - .../rgw-overview-dashboard.component.spec.ts | 2 +- .../rgw-overview-dashboard.component.ts | 36 ++-- .../src/app/shared/api/prometheus.service.ts | 70 +++++++- .../app/shared/enum/dashboard-promqls.enum.ts | 22 ++- .../app/shared/services/formatter.service.ts | 2 +- .../services/number-formatter.service.ts | 4 +- 11 files changed, 225 insertions(+), 143 deletions(-) diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/dashboard-v3/dashboard-area-chart/dashboard-area-chart.component.ts b/src/pybind/mgr/dashboard/frontend/src/app/ceph/dashboard-v3/dashboard-area-chart/dashboard-area-chart.component.ts index e06746bf9b1fe..35d8e571068cb 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/ceph/dashboard-v3/dashboard-area-chart/dashboard-area-chart.component.ts +++ b/src/pybind/mgr/dashboard/frontend/src/app/ceph/dashboard-v3/dashboard-area-chart/dashboard-area-chart.component.ts @@ -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, diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/dashboard-v3/dashboard-v3.module.ts b/src/pybind/mgr/dashboard/frontend/src/app/ceph/dashboard-v3/dashboard-v3.module.ts index 6e55f98531dd1..9e1529c3e3794 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/ceph/dashboard-v3/dashboard-v3.module.ts +++ b/src/pybind/mgr/dashboard/frontend/src/app/ceph/dashboard-v3/dashboard-v3.module.ts @@ -42,6 +42,12 @@ import { PgSummaryPipe } from './pg-summary.pipe'; DashboardTimeSelectorComponent ], - exports: [DashboardV3Component, CardComponent, CardRowComponent] + exports: [ + DashboardV3Component, + CardComponent, + CardRowComponent, + DashboardAreaChartComponent, + DashboardTimeSelectorComponent + ] }) export class DashboardV3Module {} diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/dashboard-v3/dashboard/dashboard-v3.component.ts b/src/pybind/mgr/dashboard/frontend/src/app/ceph/dashboard-v3/dashboard/dashboard-v3.component.ts index a3bd264c68435..a682d7e5d8e72 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/ceph/dashboard-v3/dashboard/dashboard-v3.component.ts +++ b/src/pybind/mgr/dashboard/frontend/src/app/ceph/dashboard-v3/dashboard/dashboard-v3.component.ts @@ -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 + ); } } diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/rgw/rgw-overview-dashboard/rgw-overview-dashboard.component.html b/src/pybind/mgr/dashboard/frontend/src/app/ceph/rgw/rgw-overview-dashboard/rgw-overview-dashboard.component.html index 5b11eb4293411..41efb5d65ca11 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/ceph/rgw/rgw-overview-dashboard/rgw-overview-dashboard.component.html +++ b/src/pybind/mgr/dashboard/frontend/src/app/ceph/rgw/rgw-overview-dashboard/rgw-overview-dashboard.component.html @@ -1,71 +1,97 @@
-
- -
-
  • - -
  • -
    -
  • - -
  • -
    -
  • - -
  • -
    -
  • - -
  • -
    -
  • - -
  • -
    -
  • - -
  • -
    -
  • - -
  • -
    -
    + +
    +
  • + +
  • +
    +
  • + +
  • +
    +
  • + +
  • +
    +
  • + +
  • +
    +
  • + +
  • +
    +
  • + +
  • +
    +
  • + +
  • +
    + +
    + + + + + + + + +
    +

    {{ totalPoolUsedBytes | dimlessBinary}}

    @@ -73,7 +99,7 @@

    {{ averageObjectSize | dimlessBinary}}

    @@ -82,3 +108,4 @@
    + diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/rgw/rgw-overview-dashboard/rgw-overview-dashboard.component.scss b/src/pybind/mgr/dashboard/frontend/src/app/ceph/rgw/rgw-overview-dashboard/rgw-overview-dashboard.component.scss index bda47a5274f01..39a87125f78c1 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/ceph/rgw/rgw-overview-dashboard/rgw-overview-dashboard.component.scss +++ b/src/pybind/mgr/dashboard/frontend/src/app/ceph/rgw/rgw-overview-dashboard/rgw-overview-dashboard.component.scss @@ -6,7 +6,3 @@ hr { .list-group-item { border: 0; } - -.pb-5 { - padding-bottom: 3.5rem !important; -} diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/rgw/rgw-overview-dashboard/rgw-overview-dashboard.component.spec.ts b/src/pybind/mgr/dashboard/frontend/src/app/ceph/rgw/rgw-overview-dashboard/rgw-overview-dashboard.component.spec.ts index 482fa0f0245da..f887bcd8224f7 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/ceph/rgw/rgw-overview-dashboard/rgw-overview-dashboard.component.spec.ts +++ b/src/pybind/mgr/dashboard/frontend/src/app/ceph/rgw/rgw-overview-dashboard/rgw-overview-dashboard.component.spec.ts @@ -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', () => { diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/rgw/rgw-overview-dashboard/rgw-overview-dashboard.component.ts b/src/pybind/mgr/dashboard/frontend/src/app/ceph/rgw/rgw-overview-dashboard/rgw-overview-dashboard.component.ts index a3116834b5963..81634fe950344 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/ceph/rgw/rgw-overview-dashboard/rgw-overview-dashboard.component.ts +++ b/src/pybind/mgr/dashboard/frontend/src/app/ceph/rgw/rgw-overview-dashboard/rgw-overview-dashboard.component.ts @@ -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 + ); } } diff --git a/src/pybind/mgr/dashboard/frontend/src/app/shared/api/prometheus.service.ts b/src/pybind/mgr/dashboard/frontend/src/app/shared/api/prometheus.service.ts index 340f89ca38f8f..21420d7629fb5 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/shared/api/prometheus.service.ts +++ b/src/pybind/mgr/dashboard/frontend/src/app/shared/api/prometheus.service.ts @@ -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; + } } diff --git a/src/pybind/mgr/dashboard/frontend/src/app/shared/enum/dashboard-promqls.enum.ts b/src/pybind/mgr/dashboard/frontend/src/app/shared/enum/dashboard-promqls.enum.ts index 7afd069978d6a..3f76fa828f57e 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/shared/enum/dashboard-promqls.enum.ts +++ b/src/pybind/mgr/dashboard/frontend/src/app/shared/enum/dashboard-promqls.enum.ts @@ -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]))' } diff --git a/src/pybind/mgr/dashboard/frontend/src/app/shared/services/formatter.service.ts b/src/pybind/mgr/dashboard/frontend/src/app/shared/services/formatter.service.ts index b212004d2b1bd..eacba3cf16dcc 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/shared/services/formatter.service.ts +++ b/src/pybind/mgr/dashboard/frontend/src/app/shared/services/formatter.service.ts @@ -41,7 +41,7 @@ export class FormatterService { formatNumberFromTo( n: any, units: any, - targetedUnits: string, + targetedUnits: string = '', conversionFactor: number, unitsArray: string[], decimals: number = 1 diff --git a/src/pybind/mgr/dashboard/frontend/src/app/shared/services/number-formatter.service.ts b/src/pybind/mgr/dashboard/frontend/src/app/shared/services/number-formatter.service.ts index 9643f19211452..16b9587e23aba 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/shared/services/number-formatter.service.ts +++ b/src/pybind/mgr/dashboard/frontend/src/app/shared/services/number-formatter.service.ts @@ -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); -- 2.39.5