From 7a08bdd264ba81beec74be08d8041167300e49a5 Mon Sep 17 00:00:00 2001 From: Aashish Sharma Date: Mon, 26 Feb 2024 09:47:34 +0530 Subject: [PATCH] mgr/dashboards: multi-cluster improvements and bug fixes Fixes: https://tracker.ceph.com/issues/64880 Signed-off-by: Aashish Sharma --- src/pybind/mgr/dashboard/controllers/auth.py | 87 ++++++------ .../multi-cluster-form.component.html | 39 +++++- .../multi-cluster-form.component.ts | 12 +- .../multi-cluster-list.component.ts | 34 ++--- .../multi-cluster.component.html | 24 +++- .../multi-cluster/multi-cluster.component.ts | 129 ++++++++++++++++-- .../dashboard-area-chart.component.html | 2 +- .../dashboard-area-chart.component.spec.ts | 10 +- .../dashboard-area-chart.component.ts | 7 + .../dashboard-pie/dashboard-pie.component.ts | 6 +- .../navigation/navigation.component.ts | 14 +- .../app/shared/api/multi-cluster.service.ts | 47 ++++++- .../src/app/shared/api/prometheus.service.ts | 87 +++++------- ...critical-confirmation-modal.component.html | 6 + .../critical-confirmation-modal.component.ts | 1 + .../app/shared/enum/dashboard-promqls.enum.ts | 20 ++- .../services/api-interceptor.service.ts | 53 +------ 17 files changed, 370 insertions(+), 208 deletions(-) diff --git a/src/pybind/mgr/dashboard/controllers/auth.py b/src/pybind/mgr/dashboard/controllers/auth.py index eca87a9a84edb..c2287ef51a80f 100644 --- a/src/pybind/mgr/dashboard/controllers/auth.py +++ b/src/pybind/mgr/dashboard/controllers/auth.py @@ -54,50 +54,6 @@ class Auth(RESTController, ControllerAuthMixin): pwd_expiration_date = user_data.get('pwdExpirationDate', None) pwd_update_required = user_data.get('pwdUpdateRequired', False) - if isinstance(Settings.MULTICLUSTER_CONFIG, str): - try: - item_to_dict = json.loads(Settings.MULTICLUSTER_CONFIG) - except json.JSONDecodeError: - item_to_dict = {} - multicluster_config = item_to_dict.copy() - else: - multicluster_config = Settings.MULTICLUSTER_CONFIG.copy() - try: - if fsid in multicluster_config['config']: - existing_entries = multicluster_config['config'][fsid] - if not any(entry['user'] == username for entry in existing_entries): - existing_entries.append({ - "name": fsid, - "url": origin, - "cluster_alias": "local-cluster", - "user": username - }) - else: - multicluster_config['config'][fsid] = [{ - "name": fsid, - "url": origin, - "cluster_alias": "local-cluster", - "user": username - }] - - except KeyError: - multicluster_config = { - 'current_url': origin, - 'current_user': username, - 'hub_url': origin, - 'config': { - fsid: [ - { - "name": fsid, - "url": origin, - "cluster_alias": "local-cluster", - "user": username - } - ] - } - } - Settings.MULTICLUSTER_CONFIG = multicluster_config - if user_perms is not None: url_prefix = 'https' if mgr.get_localized_module_option('ssl') else 'http' @@ -110,6 +66,49 @@ class Auth(RESTController, ControllerAuthMixin): token = token.decode('utf-8') if isinstance(token, bytes) else token self._set_token_cookie(url_prefix, token) + if isinstance(Settings.MULTICLUSTER_CONFIG, str): + try: + item_to_dict = json.loads(Settings.MULTICLUSTER_CONFIG) + except json.JSONDecodeError: + item_to_dict = {} + multicluster_config = item_to_dict.copy() + else: + multicluster_config = Settings.MULTICLUSTER_CONFIG.copy() + try: + if fsid in multicluster_config['config']: + existing_entries = multicluster_config['config'][fsid] + if not any((entry['user'] == username or entry['cluster_alias'] == 'local-cluster') for entry in existing_entries): # noqa E501 #pylint: disable=line-too-long + existing_entries.append({ + "name": fsid, + "url": origin, + "cluster_alias": "local-cluster", + "user": username + }) + else: + multicluster_config['config'][fsid] = [{ + "name": fsid, + "url": origin, + "cluster_alias": "local-cluster", + "user": username + }] + + except KeyError: + multicluster_config = { + 'current_url': origin, + 'current_user': username, + 'hub_url': origin, + 'config': { + fsid: [ + { + "name": fsid, + "url": origin, + "cluster_alias": "local-cluster", + "user": username + } + ] + } + } + Settings.MULTICLUSTER_CONFIG = multicluster_config return { 'token': token, 'username': username, diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/multi-cluster/multi-cluster-form/multi-cluster-form.component.html b/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/multi-cluster/multi-cluster-form/multi-cluster-form.component.html index a2d36e4232aa4..68af8c1672dc2 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/multi-cluster/multi-cluster-form/multi-cluster-form.component.html +++ b/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/multi-cluster/multi-cluster-form/multi-cluster-form.component.html @@ -20,7 +20,7 @@ + *ngIf="connectionVerified !== undefined && !connectionVerified && connectionMessage === 'Connection refused' || remoteClusterForm.getValue('showToken')">

You need to set this cluster's url as the cross origin url in the remote cluster you are trying to connect. You can do it by running this CLI command in your remote cluster and proceed with authentication via token.

@@ -29,7 +29,14 @@
+
+ +
+ + This field is required. + +
+
+ *ngIf="!showCrossOriginError && action !== 'edit' && !remoteClusterForm.getValue('showToken') && !connectionVerified">
@@ -50,6 +52,16 @@
+ +

Please note that the data for the disconnected cluster will be visible for a duration of ~ 5 minutes. After this period, it will be automatically removed.

+
@@ -112,7 +124,7 @@ i18n-title class="col-sm-6 m-0 p-0 ps-2 pe-2" aria-label="Total number of hosts" - *ngIf="queriesResults['TOTAL_HOSTS'][0][1] !== '0'"> + *ngIf="queriesResults['TOTAL_HOSTS'] && queriesResults['TOTAL_HOSTS'][0]">

{{ queriesResults['TOTAL_HOSTS'][0][1] }}

@@ -142,10 +154,11 @@ aria-label="Cluster Utilization card" *ngIf="clusters">
- + @@ -163,6 +177,7 @@ [labelsArray]="throughputLabels" dataUnits="B/s" decimals="2" + isMultiCluster="true" [dataArray]="throughputValues" [truncateLabel]="true" *ngIf="throughputLabels && throughputLabels"> @@ -190,11 +205,12 @@ aria-label="Pools Utilization card" *ngIf="clusters">
- + @@ -203,6 +219,7 @@ [labelsArray]="poolIOPSLabels" dataUnits="" decimals="0" + isMultiCluster="true" [dataArray]="poolIOPSValues" *ngIf="poolIOPSLabels && poolIOPSValues" [truncateLabel]="true"> @@ -211,6 +228,7 @@ [labelsArray]="poolThroughputLabels" dataUnits="B/s" decimals="2" + isMultiCluster="true" [dataArray]="poolThroughputValues" *ngIf="poolThroughputLabels && poolThroughputValues" [truncateLabel]="true"> diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/multi-cluster/multi-cluster.component.ts b/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/multi-cluster/multi-cluster.component.ts index ab8b413e73623..18dc80406169e 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/multi-cluster/multi-cluster.component.ts +++ b/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/multi-cluster/multi-cluster.component.ts @@ -1,4 +1,4 @@ -import { Component, OnInit, ViewChild } from '@angular/core'; +import { Component, OnDestroy, OnInit, ViewChild } from '@angular/core'; import { NgbModalRef } from '@ng-bootstrap/ng-bootstrap'; import { Subscription } from 'rxjs'; import { MultiClusterService } from '~/app/shared/api/multi-cluster.service'; @@ -6,17 +6,23 @@ import { Icons } from '~/app/shared/enum/icons.enum'; import { ModalService } from '~/app/shared/services/modal.service'; import { MultiClusterFormComponent } from './multi-cluster-form/multi-cluster-form.component'; import { PrometheusService } from '~/app/shared/api/prometheus.service'; -import { MultiClusterPromqls as queries } from '~/app/shared/enum/dashboard-promqls.enum'; import { CdTableColumn } from '~/app/shared/models/cd-table-column'; import { CellTemplate } from '~/app/shared/enum/cell-template.enum'; import { DimlessBinaryPipe } from '~/app/shared/pipes/dimless-binary.pipe'; +import { Router } from '@angular/router'; + +import { + MultiClusterPromqls as allQueries, + MultiClusterPromqlsForClusterUtilization as ClusterUltilizationQueries, + MultiClusterPromqlsForPoolUtilization as PoolUltilizationQueries +} from '~/app/shared/enum/dashboard-promqls.enum'; @Component({ selector: 'cd-multi-cluster', templateUrl: './multi-cluster.component.html', styleUrls: ['./multi-cluster.component.scss'] }) -export class MultiClusterComponent implements OnInit { +export class MultiClusterComponent implements OnInit, OnDestroy { COUNT_OF_UTILIZATION_CHARTS = 5; @ViewChild('nameTpl', { static: true }) @@ -56,7 +62,7 @@ export class MultiClusterComponent implements OnInit { isMultiCluster = true; clusterTokenStatus: object = {}; localClusterName: string; - clusters: any; + clusters: any = []; connectionErrorsCount = 0; capacityLabels: string[] = []; @@ -72,13 +78,38 @@ export class MultiClusterComponent implements OnInit { poolIOPSValues: string[] = []; poolCapacityValues: string[] = []; poolThroughputValues: string[] = []; + showDeletionMessage = false; + isClusterAdded = false; + selectedQueries: any; + PROMETHEUS_DELAY = 20000; + LOAD_DELAY = 5000; + CLUSTERS_REFRESH_INTERVAL = 30000; + interval: NodeJS.Timer; + selectedTime: any; + multiClusterQueries: any = {}; constructor( private multiClusterService: MultiClusterService, private modalService: ModalService, + private router: Router, private prometheusService: PrometheusService, private dimlessBinaryPipe: DimlessBinaryPipe - ) {} + ) { + this.multiClusterQueries = { + cluster: { + queries: ClusterUltilizationQueries, + selectedTime: this.prometheusService.lastHourDateObject + }, + pool: { + queries: PoolUltilizationQueries, + selectedTime: this.prometheusService.lastHourDateObject + }, + all: { + queries: allQueries, + selectedTime: this.prometheusService.lastHourDateObject + } + }; + } ngOnInit(): void { this.columns = [ @@ -160,7 +191,24 @@ export class MultiClusterComponent implements OnInit { this.clusterTokenStatus = resp; }) ); - this.getPrometheusData(this.prometheusService.lastHourDateObject); + + this.isClusterAdded = this.multiClusterService.isClusterAdded(); + + if (this.isClusterAdded) { + setTimeout(() => { + this.getPrometheusData(this.prometheusService.lastHourDateObject); + this.multiClusterService.isClusterAdded(false); + }, this.PROMETHEUS_DELAY); + } else { + this.showDeletionMessage = this.multiClusterService.showPrometheusDelayMessage(); + if (this.showDeletionMessage) { + setTimeout(() => { + this.getPrometheusData(this.prometheusService.lastHourDateObject); + }, this.LOAD_DELAY); + } else { + this.getPrometheusData(this.prometheusService.lastHourDateObject); + } + } } openRemoteClusterInfoModal() { @@ -170,17 +218,69 @@ export class MultiClusterComponent implements OnInit { this.bsModalRef = this.modalService.show(MultiClusterFormComponent, initialState, { size: 'lg' }); + this.bsModalRef.componentInstance.submitAction.subscribe(() => { + this.loading = true; + setTimeout(() => { + const currentRoute = this.router.url.split('?')[0]; + this.multiClusterService.refreshMultiCluster(currentRoute); + this.getPrometheusData(this.prometheusService.lastHourDateObject); + }, this.PROMETHEUS_DELAY); + }); } - getPrometheusData(selectedTime: any) { + getPrometheusData(selectedTime: any, selectedQueries?: string) { + const validRangeQueries = [ + 'CLUSTER_CAPACITY_UTILIZATION', + 'CLUSTER_IOPS_UTILIZATION', + 'CLUSTER_THROUGHPUT_UTILIZATION', + 'POOL_CAPACITY_UTILIZATION', + 'POOL_IOPS_UTILIZATION', + 'POOL_THROUGHPUT_UTILIZATION' + ]; + const validQueries = [ + 'ALERTS', + 'MGR_METADATA', + 'HEALTH_STATUS', + 'TOTAL_CAPACITY', + 'USED_CAPACITY', + 'POOLS', + 'OSDS', + 'CLUSTER_CAPACITY_UTILIZATION', + 'CLUSTER_IOPS_UTILIZATION', + 'CLUSTER_THROUGHPUT_UTILIZATION', + 'POOL_CAPACITY_UTILIZATION', + 'POOL_IOPS_UTILIZATION', + 'POOL_THROUGHPUT_UTILIZATION', + 'HOSTS', + 'CLUSTER_ALERTS' + ]; + + if (selectedQueries) { + if (selectedQueries === 'poolUtilization') { + this.multiClusterQueries.pool['selectedTime'] = selectedTime; + } + + if (selectedQueries === 'clusterUtilization') { + this.multiClusterQueries.cluster.selectedTime = selectedTime; + } + } + this.prometheusService - .getMultiClusterQueriesData(selectedTime, queries, this.queriesResults) + .getMultiClusterQueriesData( + this.queriesResults, + validQueries, + validRangeQueries, + this.multiClusterQueries + ) .subscribe((data: any) => { this.queriesResults = data; this.loading = false; this.alerts = this.queriesResults.ALERTS; this.getAlertsInfo(); this.getClustersInfo(); + this.interval = setInterval(() => { + this.getClustersInfo(); + }, this.CLUSTERS_REFRESH_INTERVAL); }); } @@ -224,7 +324,6 @@ export class MultiClusterComponent implements OnInit { } const clusters: ClusterInfo[] = []; - this.queriesResults.TOTAL_CAPACITY?.forEach((totalCapacityMetric: any) => { const clusterName = totalCapacityMetric.metric.cluster; const totalCapacity = parseInt(totalCapacityMetric.value[1]); @@ -240,7 +339,7 @@ export class MultiClusterComponent implements OnInit { const available_capacity = totalCapacity - usedCapacity; clusters.push({ - cluster: clusterName, + cluster: clusterName.trim(), status, alert, total_capacity: totalCapacity, @@ -318,7 +417,6 @@ export class MultiClusterComponent implements OnInit { } labels.push(label); } - // console.log(labels) return labels; } @@ -329,4 +427,13 @@ export class MultiClusterComponent implements OnInit { } return values; } + + onDismissed() { + this.showDeletionMessage = false; + this.multiClusterService.showPrometheusDelayMessage(false); + } + + ngOnDestroy(): void { + clearInterval(this.interval); + } } diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/dashboard-v3/dashboard-area-chart/dashboard-area-chart.component.html b/src/pybind/mgr/dashboard/frontend/src/app/ceph/dashboard-v3/dashboard-area-chart/dashboard-area-chart.component.html index 2b4878e995d2a..99a71181b70e3 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/ceph/dashboard-v3/dashboard-area-chart/dashboard-area-chart.component.html +++ b/src/pybind/mgr/dashboard/frontend/src/app/ceph/dashboard-v3/dashboard-area-chart/dashboard-area-chart.component.html @@ -24,7 +24,7 @@
-
+
{ it('should set label', () => { component.ngOnChanges({ dataArray: new SimpleChange(null, component.dataArray, false) }); - expect(component.chartData.dataset[0].label).toEqual('Read'); + expect(component.chartData.dataset[0].label).toEqual('Total'); expect(component.chartData.dataset[1].label).toEqual('Write'); - expect(component.chartData.dataset[2].label).toEqual('Total'); + expect(component.chartData.dataset[2].label).toEqual('Read'); }); it('should transform and update data', () => { component.ngOnChanges({ dataArray: new SimpleChange(null, component.dataArray, false) }); expect(component.chartData.dataset[0].data).toEqual([ - { x: 1000, y: 110 }, - { x: 3000, y: 130 } + { x: 5000, y: 150 }, + { x: 6000, y: 160 } ]); }); it('should set currentData to last value', () => { component.ngOnChanges({ dataArray: new SimpleChange(null, component.dataArray, false) }); - expect(component.currentChartData.dataset[0].currentData).toBe('130'); + expect(component.currentChartData.dataset[0].currentData).toBe('160'); }); it('should keep data units consistency', () => { 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 607a3b7d51ad7..8a267b4782aa8 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 @@ -31,6 +31,8 @@ export class DashboardAreaChartComponent implements OnChanges { decimals?: number = 1; @Input() truncateLabel = false; + @Input() + isMultiCluster?: boolean = false; currentDataUnits: string; currentData: number; @@ -214,8 +216,13 @@ export class DashboardAreaChartComponent implements OnChanges { [this.maxConvertedValue, this.maxConvertedValueUnits] = this.convertUnits( this.maxValue ).split(' '); + this.currentChartData.dataset[index]['currentDataValue'] = currentDataValue; } } + this.currentChartData.dataset.sort( + (a: { currentDataValue: string }, b: { currentDataValue: string }) => + parseFloat(b['currentDataValue']) - parseFloat(a['currentDataValue']) + ); } if (this.chart) { diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/dashboard-v3/dashboard-pie/dashboard-pie.component.ts b/src/pybind/mgr/dashboard/frontend/src/app/ceph/dashboard-v3/dashboard-pie/dashboard-pie.component.ts index b0c253c33e9d8..fa194024db9ee 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/ceph/dashboard-v3/dashboard-pie/dashboard-pie.component.ts +++ b/src/pybind/mgr/dashboard/frontend/src/app/ceph/dashboard-v3/dashboard-pie/dashboard-pie.component.ts @@ -157,8 +157,10 @@ export class DashboardPieComponent implements OnChanges, OnInit { private prepareRawUsage(chart: Record, data: Record) { const nearFullRatioPercent = this.lowThreshold * 100; const fullRatioPercent = this.highThreshold * 100; - const percentAvailable = this.calcPercentage(data.max - data.current, data.max); - const percentUsed = this.calcPercentage(data.current, data.max); + const max = typeof data.max === 'string' ? parseFloat(data.max) : data.max; + const current = typeof data.current === 'string' ? parseFloat(data.current) : data.current; + const percentAvailable = this.calcPercentage(max - current, max); + const percentUsed = this.calcPercentage(current, max); if (fullRatioPercent >= 0 && percentUsed >= fullRatioPercent) { this.color = 'chart-color-red'; diff --git a/src/pybind/mgr/dashboard/frontend/src/app/core/navigation/navigation/navigation.component.ts b/src/pybind/mgr/dashboard/frontend/src/app/core/navigation/navigation/navigation.component.ts index de8b0a267db82..6f52dc6cf3391 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/core/navigation/navigation/navigation.component.ts +++ b/src/pybind/mgr/dashboard/frontend/src/app/core/navigation/navigation/navigation.component.ts @@ -71,6 +71,7 @@ export class NavigationComponent implements OnInit, OnDestroy { this.multiClusterService.subscribe((resp: object) => { const clustersConfig = resp['config']; if (clustersConfig) { + this.clustersMap.clear(); Object.keys(clustersConfig).forEach((clusterKey: string) => { const clusterDetailsList = clustersConfig[clusterKey]; clusterDetailsList.forEach((clusterDetails: MultiCluster) => { @@ -212,9 +213,6 @@ export class NavigationComponent implements OnInit, OnDestroy { }, () => {}, () => { - this.multiClusterService.refresh(); - this.summaryService.refresh(); - // force refresh grafana api url to get the correct url for the selected cluster this.settingsService.ifSettingConfigured( 'api/grafana/url', @@ -223,15 +221,7 @@ export class NavigationComponent implements OnInit, OnDestroy { true ); const currentRoute = this.router.url.split('?')[0]; - if (currentRoute.includes('dashboard')) { - this.router.navigateByUrl('/pool', { skipLocationChange: true }).then(() => { - this.router.navigate([currentRoute]); - }); - } else { - this.router.navigateByUrl('/', { skipLocationChange: true }).then(() => { - this.router.navigate([currentRoute]); - }); - } + this.multiClusterService.refreshMultiCluster(currentRoute); } ); } diff --git a/src/pybind/mgr/dashboard/frontend/src/app/shared/api/multi-cluster.service.ts b/src/pybind/mgr/dashboard/frontend/src/app/shared/api/multi-cluster.service.ts index ffb312de4d9d7..5e6ab6e3606f9 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/shared/api/multi-cluster.service.ts +++ b/src/pybind/mgr/dashboard/frontend/src/app/shared/api/multi-cluster.service.ts @@ -3,6 +3,8 @@ import { Injectable } from '@angular/core'; import { BehaviorSubject, Observable, Subscription } from 'rxjs'; import { TimerService } from '../services/timer.service'; import { filter } from 'rxjs/operators'; +import { SummaryService } from '../services/summary.service'; +import { Router } from '@angular/router'; @Injectable({ providedIn: 'root' @@ -13,7 +15,14 @@ export class MultiClusterService { msData$ = this.msSource.asObservable(); private tokenStatusSource = new BehaviorSubject(null); tokenStatusSource$ = this.tokenStatusSource.asObservable(); - constructor(private http: HttpClient, private timerService: TimerService) {} + showDeletionMessage = false; + isClusterAddedFlag = false; + constructor( + private http: HttpClient, + private timerService: TimerService, + private summaryService: SummaryService, + private router: Router + ) {} startPolling(): Subscription { return this.timerService @@ -41,7 +50,9 @@ export class MultiClusterService { if (tempMap.size > 0) { clustersTokenMap = tempMap; - dataSubscription.unsubscribe(); + if (dataSubscription) { + dataSubscription.unsubscribe(); + } this.checkAndStartTimer(clustersTokenMap); } } @@ -95,6 +106,7 @@ export class MultiClusterService { token = '', hub_url = '', clusterFsid = '', + prometheusApiUrl = '', ssl = false, cert = '' ) { @@ -106,6 +118,7 @@ export class MultiClusterService { token, hub_url, cluster_fsid: clusterFsid, + prometheus_api_url: prometheusApiUrl, ssl_verify: ssl, ssl_certificate: cert }); @@ -119,7 +132,7 @@ export class MultiClusterService { ssl = false, cert = '' ) { - return this.http.post('api/multi-cluster/reconnect_cluster', { + return this.http.put('api/multi-cluster/reconnect_cluster', { url, username, password, @@ -169,4 +182,32 @@ export class MultiClusterService { return this.http.get('api/multi-cluster/check_token_status', { params }); } + + showPrometheusDelayMessage(showDeletionMessage?: boolean) { + if (showDeletionMessage !== undefined) { + this.showDeletionMessage = showDeletionMessage; + } + return this.showDeletionMessage; + } + + isClusterAdded(isClusterAddedFlag?: boolean) { + if (isClusterAddedFlag !== undefined) { + this.isClusterAddedFlag = isClusterAddedFlag; + } + return this.isClusterAddedFlag; + } + + refreshMultiCluster(currentRoute: string) { + this.refresh(); + this.summaryService.refresh(); + if (currentRoute.includes('dashboard')) { + this.router.navigateByUrl('/pool', { skipLocationChange: true }).then(() => { + this.router.navigate([currentRoute]); + }); + } else { + this.router.navigateByUrl('/', { skipLocationChange: true }).then(() => { + this.router.navigate([currentRoute]); + }); + } + } } 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 b7db0bc2f3cca..eaa1696dc873d 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 @@ -200,7 +200,12 @@ export class PrometheusService { return this.http.get(`${this.baseURL}/data`, { params }); } - getMultiClusterQueriesData(selectedTime: any, queries: any, queriesResults: any) { + getMultiClusterQueriesData( + queriesResults: any, + validQueries: string[], + validRangeQueries: string[], + multiClusterQueries: any + ) { return new Observable((observer) => { this.ifPrometheusConfigured(() => { if (this.timerGetPrometheusDataSub) { @@ -208,63 +213,45 @@ export class PrometheusService { } this.timerGetPrometheusDataSub = timer(0, this.timerTime).subscribe(() => { - selectedTime = this.updateTimeStamp(selectedTime); + let requests: any[] = []; + let queryNames: string[] = []; - const requests = []; - for (const queryName in queries) { - if (queries.hasOwnProperty(queryName)) { - const validRangeQueries1 = [ - 'CLUSTER_CAPACITY_UTILIZATION', - 'CLUSTER_IOPS_UTILIZATION', - 'CLUSTER_THROUGHPUT_UTILIZATION', - 'POOL_CAPACITY_UTILIZATION', - 'POOL_IOPS_UTILIZATION', - 'POOL_THROUGHPUT_UTILIZATION' - ]; - if (validRangeQueries1.includes(queryName)) { - const query = queries[queryName]; - const request = this.getMultiClusterQueryRangeData({ - params: encodeURIComponent(query), - start: selectedTime['start'], - end: selectedTime['end'], - step: selectedTime['step'] - }); - requests.push(request); - } else { - const query = queries[queryName]; - const request = this.getMultiClusterData({ - params: encodeURIComponent(query), - start: selectedTime['start'], - end: selectedTime['end'], - step: selectedTime['step'] - }); - requests.push(request); + Object.entries(multiClusterQueries).forEach(([key, _value]) => { + for (const queryName in multiClusterQueries[key].queries) { + if (multiClusterQueries[key].queries.hasOwnProperty(queryName)) { + const query = multiClusterQueries[key].queries[queryName]; + const start = this.updateTimeStamp(multiClusterQueries[key].selectedTime)['start']; + const end = this.updateTimeStamp(multiClusterQueries[key].selectedTime)['end']; + const step = this.updateTimeStamp(multiClusterQueries[key].selectedTime)['step']; + + if (validRangeQueries.includes(queryName)) { + const request = this.getMultiClusterQueryRangeData({ + params: encodeURIComponent(query), + start, + end, + step + }); + requests.push(request); + queryNames.push(queryName); + } else { + const request = this.getMultiClusterData({ + params: encodeURIComponent(query), + start, + end, + step + }); + requests.push(request); + queryNames.push(queryName); + } } } - } + }); forkJoin(requests).subscribe( (responses: any[]) => { for (let i = 0; i < responses.length; i++) { const data = responses[i]; - const queryName = Object.keys(queries)[i]; - const validQueries = [ - 'ALERTS', - 'MGR_METADATA', - 'HEALTH_STATUS', - 'TOTAL_CAPACITY', - 'USED_CAPACITY', - 'POOLS', - 'OSDS', - 'CLUSTER_CAPACITY_UTILIZATION', - 'CLUSTER_IOPS_UTILIZATION', - 'CLUSTER_THROUGHPUT_UTILIZATION', - 'POOL_CAPACITY_UTILIZATION', - 'POOL_IOPS_UTILIZATION', - 'POOL_THROUGHPUT_UTILIZATION', - 'HOSTS', - 'CLUSTER_ALERTS' - ]; + const queryName = queryNames[i]; if (data.result.length) { if (validQueries.includes(queryName)) { queriesResults[queryName] = data.result; diff --git a/src/pybind/mgr/dashboard/frontend/src/app/shared/components/critical-confirmation-modal/critical-confirmation-modal.component.html b/src/pybind/mgr/dashboard/frontend/src/app/shared/components/critical-confirmation-modal/critical-confirmation-modal.component.html index cc2eded0e3b8f..41b0ed5ddb7bb 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/shared/components/critical-confirmation-modal/critical-confirmation-modal.component.html +++ b/src/pybind/mgr/dashboard/frontend/src/app/shared/components/critical-confirmation-modal/critical-confirmation-modal.component.html @@ -10,6 +10,12 @@ [formGroup]="deletionForm" novalidate>