From 7a65f7541df21f64ca44a32ac111f3b08e44e5fc Mon Sep 17 00:00:00 2001 From: Timothy Asir Jeyasingh Date: Tue, 3 Oct 2023 12:12:06 +0530 Subject: [PATCH] Fix tooltips messages in the graphs at DR Policy dashboard https://bugzilla.redhat.com/show_bug.cgi?id=2239093 https://bugzilla.redhat.com/show_bug.cgi?id=2239096 https://bugzilla.redhat.com/show_bug.cgi?id=2239101 Signed-off-by: Timothy Asir Jeyasingh --- locales/en/plugin__odf-console.json | 22 +++--- .../data-policy/alert-card/alert-card.tsx | 9 +++ .../cluster-app-card/argo-application-set.tsx | 70 ++----------------- .../cluster-app-card/cluster-app-card.tsx | 18 ++--- .../data-policy/cluster-app-card/cluster.tsx | 53 ++------------ .../data-policy/cluster-app-card/common.tsx | 64 ++++++++++++++++- .../data-policy/summary-card/summary-card.tsx | 11 ++- .../mco/components/mco-dashboard/queries.ts | 5 -- packages/mco/constants/dashboard.ts | 2 +- packages/shared/src/alert/AlertsPanel.tsx | 4 ++ 10 files changed, 116 insertions(+), 142 deletions(-) diff --git a/locales/en/plugin__odf-console.json b/locales/en/plugin__odf-console.json index a1c7fb22c..b27bd3a81 100644 --- a/locales/en/plugin__odf-console.json +++ b/locales/en/plugin__odf-console.json @@ -62,11 +62,10 @@ "Loading Empty Page": "Loading Empty Page", "You are not authorized to complete this action. See your cluster administrator for role-based access control information.": "You are not authorized to complete this action. See your cluster administrator for role-based access control information.", "Not Authorized": "Not Authorized", + "Alerts are displayed for both <1>ApplicationSet and <4>Subscription type applications.": "Alerts are displayed for both <1>ApplicationSet and <4>Subscription type applications.", "{{ currentStatus }} to {{ preferredCluster }}": "{{ currentStatus }} to {{ preferredCluster }}", "{{ currentStatus }} to {{ failoverCluster }}": "{{ currentStatus }} to {{ failoverCluster }}", "Unknown": "Unknown", - "Protected PVCs": "Protected PVCs", - "{{ pvcsWithIssueCount }} with issues": "{{ pvcsWithIssueCount }} with issues", "Snapshot": "Snapshot", "Last replicated on: {{ lastSyncTime }}": "Last replicated on: {{ lastSyncTime }}", "Operator health": "Operator health", @@ -80,30 +79,31 @@ "Operators health": "Operators health", "Peer connection": "Peer connection", " {{ peerConnectedCount }} Connected": " {{ peerConnectedCount }} Connected", - "Total applications": "Total applications", + "Total applications (ApplicationSet)": "Total applications (ApplicationSet)", " {{ protectedAppSetsCount }} protected apps": " {{ protectedAppSetsCount }} protected apps", " {{ appsWithIssues }} of {{ protectedAppSetsCount }} apps with issues": " {{ appsWithIssues }} of {{ protectedAppSetsCount }} apps with issues", - "PVCs": "PVCs", - " {{ protectedPVCsCount }} protected": " {{ protectedPVCsCount }} protected", "Current value: ": "Current value: ", "Max value: ": "Max value: ", "Min value: ": "Min value: ", "Utilization": "Utilization", - "Snapshots synced": "Snapshots synced", - "The y-axis shows the number of snapshots taken. It represents the rate of difference in snapshot creation count during a failover.": "The y-axis shows the number of snapshots taken. It represents the rate of difference in snapshot creation count during a failover.", - "Replication throughput": "Replication throughput", - "The y-axis shows the average throughput for syncing snapshot bytes from the primary to the secondary cluster.": "The y-axis shows the average throughput for syncing snapshot bytes from the primary to the secondary cluster.", - "Volume replication health": "Volume replication health", + "Block volumes snapshots synced": "Block volumes snapshots synced", + "The graph displays the total number of block volumes inbound snapshots, by cluster, from all ApplicationSet and Subscription type applications. Applications that use file volumes are excluded in the total snapshot count.": "The graph displays the total number of block volumes inbound snapshots, by cluster, from all ApplicationSet and Subscription type applications. Applications that use file volumes are excluded in the total snapshot count.", + "Block volumes replication throughput": "Block volumes replication throughput", + "The graph displays the average replication throughput inbound, by cluster, from all ApplicationSet and Subscription type applications. Applications that use file volumes are excluded in the replication throughput.": "The graph displays the average replication throughput inbound, by cluster, from all ApplicationSet and Subscription type applications. Applications that use file volumes are excluded in the replication throughput.", + "Volume replication health (ApplicationSet)": "Volume replication health (ApplicationSet)", "Volumes": "Volumes", "Cluster: {{clusterName}}": "Cluster: {{clusterName}}", "Select a cluster": "Select a cluster", "Application:": "Application:", "Namespace: ": "Namespace: ", + "Protected PVCs (ApplicationSet)": "Protected PVCs (ApplicationSet)", + "{{ pvcsWithIssueCount }} with issues": "{{ pvcsWithIssueCount }} with issues", "Operator status": "Operator status", "Cluster operator": "Cluster operator", "{{ healthy }} healthy": "{{ healthy }} healthy", "{{ issues }} with issues": "{{ issues }} with issues", - "{{ protected }} DR protected": "{{ protected }} DR protected", + "The applications count displays the total number of <2>ApplicationSet type applications in all disaster recovery configured clusters.": "The applications count displays the total number of <2>ApplicationSet type applications in all disaster recovery configured clusters.", + "{{ protected }} protected": "{{ protected }} protected", "Systems": "Systems", "Storage System status": "Storage System status", "StorageSystem is responsible for ensuring different types of file and block storage availability, storage capacity management and generic operations on storage.": "StorageSystem is responsible for ensuring different types of file and block storage availability, storage capacity management and generic operations on storage.", diff --git a/packages/mco/components/mco-dashboard/data-policy/alert-card/alert-card.tsx b/packages/mco/components/mco-dashboard/data-policy/alert-card/alert-card.tsx index eb5a2a7b5..ee8854801 100644 --- a/packages/mco/components/mco-dashboard/data-policy/alert-card/alert-card.tsx +++ b/packages/mco/components/mco-dashboard/data-policy/alert-card/alert-card.tsx @@ -2,12 +2,15 @@ import * as React from 'react'; import { filterDRAlerts } from '@odf/mco/utils'; import AlertsPanel from '@odf/shared/alert/AlertsPanel'; import useAlerts from '@odf/shared/monitoring/useAlert'; +import { useCustomTranslation } from '@odf/shared/useCustomTranslationHook'; +import { Trans } from 'react-i18next'; import { Card, CardBody } from '@patternfly/react-core'; import AlertItem from './alert-item'; import './alert-card.scss'; export const AlertsCard: React.FC = () => { const [alerts, loaded, loadError] = useAlerts(); + const { t } = useCustomTranslation(); return ( @@ -16,6 +19,12 @@ export const AlertsCard: React.FC = () => { alerts={alerts} AlertItemComponent={AlertItem} alertsFilter={filterDRAlerts} + titleToolTip={ + + Alerts are displayed for both ApplicationSet and{' '} + Subscription type applications. + + } loaded={loaded} loadError={loadError} /> diff --git a/packages/mco/components/mco-dashboard/data-policy/cluster-app-card/argo-application-set.tsx b/packages/mco/components/mco-dashboard/data-policy/cluster-app-card/argo-application-set.tsx index 468a11c1a..33fc273ad 100644 --- a/packages/mco/components/mco-dashboard/data-policy/cluster-app-card/argo-application-set.tsx +++ b/packages/mco/components/mco-dashboard/data-policy/cluster-app-card/argo-application-set.tsx @@ -3,18 +3,11 @@ */ import * as React from 'react'; -import { VOLUME_REPLICATION_HEALTH, DRPC_STATUS } from '@odf/mco/constants'; -import { - PlacementInfo, - ProtectedAppSetsMap, - ProtectedPVCData, -} from '@odf/mco/types'; -import { getVolumeReplicationHealth, getDRStatus } from '@odf/mco/utils'; +import { DRPC_STATUS } from '@odf/mco/constants'; +import { PlacementInfo, ProtectedAppSetsMap } from '@odf/mco/types'; +import { getDRStatus } from '@odf/mco/utils'; import { utcDateTimeFormatter } from '@odf/shared/details-page/datetime'; -import { - fromNow, - getTimeDifferenceInSeconds, -} from '@odf/shared/details-page/datetime'; +import { fromNow } from '@odf/shared/details-page/datetime'; import { URL_POLL_DEFAULT_DELAY } from '@odf/shared/hooks/custom-prometheus-poll/use-url-poll'; import { useCustomTranslation } from '@odf/shared/useCustomTranslationHook'; import { @@ -22,7 +15,7 @@ import { StatusIconAndText, } from '@openshift-console/dynamic-plugin-sdk'; import { TFunction } from 'i18next'; -import { TextVariants, Text } from '@patternfly/react-core'; +import { Text } from '@patternfly/react-core'; import { StatusText } from './common'; const getCurrentActivity = ( @@ -54,54 +47,6 @@ const getCurrentActivity = ( } }; -export const ProtectedPVCsSection: React.FC = ({ - protectedPVCData, - selectedAppSet, -}) => { - const { t } = useCustomTranslation(); - const clearSetIntervalId = React.useRef(); - const [protectedPVC, setProtectedPVC] = React.useState([0, 0]); - const [protectedPVCsCount, pvcsWithIssueCount] = protectedPVC; - - const updateProtectedPVC = React.useCallback(() => { - const placementInfo = selectedAppSet?.placementInfo?.[0]; - const issueCount = - protectedPVCData?.reduce((acc, protectedPVCItem) => { - if ( - protectedPVCItem?.drpcName === placementInfo?.drpcName && - protectedPVCItem?.drpcNamespace === placementInfo?.drpcNamespace && - getVolumeReplicationHealth( - getTimeDifferenceInSeconds(protectedPVCItem?.lastSyncTime), - protectedPVCItem?.schedulingInterval - )[0] !== VOLUME_REPLICATION_HEALTH.HEALTHY - ) - return acc + 1; - else return acc; - }, 0) || 0; - - setProtectedPVC([protectedPVCData?.length || 0, issueCount]); - }, [selectedAppSet, protectedPVCData, setProtectedPVC]); - - React.useEffect(() => { - updateProtectedPVC(); - clearSetIntervalId.current = setInterval( - updateProtectedPVC, - URL_POLL_DEFAULT_DELAY - ); - return () => clearInterval(clearSetIntervalId.current); - }, [updateProtectedPVC]); - - return ( -
- {protectedPVCsCount} - {t('Protected PVCs')} - - {t('{{ pvcsWithIssueCount }} with issues', { pvcsWithIssueCount })} - -
- ); -}; - export const ActivitySection: React.FC = ({ selectedAppSet }) => { const { t } = useCustomTranslation(); @@ -162,11 +107,6 @@ export const SnapshotSection: React.FC = ({ selectedAppSet }) => { ); }; -type ProtectedPVCsSectionProps = { - protectedPVCData: ProtectedPVCData[]; - selectedAppSet: ProtectedAppSetsMap; -}; - type CommonProps = { selectedAppSet: ProtectedAppSetsMap; lastSyncTimeData?: PrometheusResponse; diff --git a/packages/mco/components/mco-dashboard/data-policy/cluster-app-card/cluster-app-card.tsx b/packages/mco/components/mco-dashboard/data-policy/cluster-app-card/cluster-app-card.tsx index 979820d5d..b5bdddcfa 100644 --- a/packages/mco/components/mco-dashboard/data-policy/cluster-app-card/cluster-app-card.tsx +++ b/packages/mco/components/mco-dashboard/data-policy/cluster-app-card/cluster-app-card.tsx @@ -45,19 +45,18 @@ import { CSVStatusesContext, DRResourcesContext, } from '../dr-dashboard-context'; -import { - ProtectedPVCsSection, - ActivitySection, - SnapshotSection, -} from './argo-application-set'; +import { ActivitySection, SnapshotSection } from './argo-application-set'; import { HealthSection, PeerConnectionSection, ApplicationsSection, - PVCsSection, UtilizationCard, } from './cluster'; -import { ClusterAppDropdown, VolumeSummarySection } from './common'; +import { + ClusterAppDropdown, + ProtectedPVCsSection, + VolumeSummarySection, +} from './common'; import './cluster-app-card.scss'; export const ClusterWiseCard: React.FC = ({ @@ -97,10 +96,7 @@ export const ClusterWiseCard: React.FC = ({ /> - + diff --git a/packages/mco/components/mco-dashboard/data-policy/cluster-app-card/cluster.tsx b/packages/mco/components/mco-dashboard/data-policy/cluster-app-card/cluster.tsx index 765a12daf..3b74c56b4 100644 --- a/packages/mco/components/mco-dashboard/data-policy/cluster-app-card/cluster.tsx +++ b/packages/mco/components/mco-dashboard/data-policy/cluster-app-card/cluster.tsx @@ -6,23 +6,17 @@ import * as React from 'react'; import { DRDashboard, getRBDSnapshotUtilizationQuery, - getTotalPVCCountPerClusterQuery, } from '@odf/mco/components/mco-dashboard/queries'; import { ODR_CLUSTER_OPERATOR, VOL_SYNC, - HUB_CLUSTER_NAME, ACM_ENDPOINT, VOLUME_REPLICATION_HEALTH, OBJECT_NAMESPACE, OBJECT_NAME, MANAGED_CLUSTER_CONDITION_AVAILABLE, } from '@odf/mco/constants'; -import { - DrClusterAppsMap, - PlacementInfo, - ProtectedPVCData, -} from '@odf/mco/types'; +import { DrClusterAppsMap, PlacementInfo } from '@odf/mco/types'; import { getVolumeReplicationHealth, getManagedClusterCondition, @@ -33,7 +27,6 @@ import { healthStateMapping } from '@odf/shared/dashboards/status-card/states'; import { PrometheusUtilizationItem } from '@odf/shared/dashboards/utilization-card/prometheus-utilization-item'; import { CustomUtilizationSummaryProps } from '@odf/shared/dashboards/utilization-card/utilization-item'; import { FieldLevelHelp } from '@odf/shared/generic'; -import { useCustomPrometheusPoll } from '@odf/shared/hooks/custom-prometheus-poll'; import Status, { StatusPopupSection } from '@odf/shared/popup/status-popup'; import { useCustomTranslation } from '@odf/shared/useCustomTranslationHook'; import { humanizeDecimalBytesPerSec, humanizeNumber } from '@odf/shared/utils'; @@ -212,7 +205,7 @@ export const ApplicationsSection: React.FC = ({ return (
{totalAppSetsCount || 0} - {t('Total applications')} + {t('Total applications (ApplicationSet)')} {t(' {{ protectedAppSetsCount }} protected apps', { protectedAppSetsCount, @@ -228,33 +221,6 @@ export const ApplicationsSection: React.FC = ({ ); }; -export const PVCsSection: React.FC = ({ - protectedPVCData, - clusterName, -}) => { - const { t } = useCustomTranslation(); - const [pvcsCount] = useCustomPrometheusPoll({ - endpoint: 'api/v1/query' as any, - query: !!clusterName ? getTotalPVCCountPerClusterQuery(clusterName) : null, - basePath: ACM_ENDPOINT, - cluster: HUB_CLUSTER_NAME, - }); - let totalPVCsCount = pvcsCount?.data?.result?.[0]?.value?.[1] || '0'; - let protectedPVCsCount = protectedPVCData?.length || 0; - - return ( -
- {totalPVCsCount} - {t('PVCs')} - - {t(' {{ protectedPVCsCount }} protected', { - protectedPVCsCount, - })} - -
- ); -}; - const getDescription = (result: PrometheusResult, _index: number) => // Returning cluster name as a description result.metric?.['cluster'] || ''; @@ -354,10 +320,10 @@ export const UtilizationCard: React.FC = ({
@@ -365,11 +331,11 @@ export const UtilizationCard: React.FC = ({
@@ -403,18 +369,13 @@ type ApplicationsSectionProps = { lastSyncTimeData: PrometheusResponse; }; -type PVCsSectionProps = { - protectedPVCData: ProtectedPVCData[]; - clusterName: string; -}; - type SnapshotUtilizationCardProps = { title: string; queryType: DRDashboard; humanizeValue: Humanize; chartLabel?: string; clusters?: string[]; - titleToolTip: string; + titleToolTip: JSX.Element; CustomUtilizationSummary?: React.FC; }; diff --git a/packages/mco/components/mco-dashboard/data-policy/cluster-app-card/common.tsx b/packages/mco/components/mco-dashboard/data-policy/cluster-app-card/common.tsx index c2056e084..4a7b590f7 100644 --- a/packages/mco/components/mco-dashboard/data-policy/cluster-app-card/common.tsx +++ b/packages/mco/components/mco-dashboard/data-policy/cluster-app-card/common.tsx @@ -3,7 +3,11 @@ */ import * as React from 'react'; -import { ALL_APPS, ALL_APPS_ITEM_ID } from '@odf/mco/constants'; +import { + ALL_APPS, + ALL_APPS_ITEM_ID, + VOLUME_REPLICATION_HEALTH, +} from '@odf/mco/constants'; import { DrClusterAppsMap, ProtectedAppSetsMap, @@ -96,7 +100,9 @@ export const VolumeSummarySection: React.FC = ({ return (
- {t('Volume replication health')} + + {t('Volume replication health (ApplicationSet)')} +
= ({ ); }; +export const ProtectedPVCsSection: React.FC = ({ + protectedPVCData, + selectedAppSet, +}) => { + const { t } = useCustomTranslation(); + const clearSetIntervalId = React.useRef(); + const [protectedPVC, setProtectedPVC] = React.useState([0, 0]); + const [protectedPVCsCount, pvcsWithIssueCount] = protectedPVC; + + const updateProtectedPVC = React.useCallback(() => { + const placementInfo = selectedAppSet?.placementInfo?.[0]; + const issueCount = + protectedPVCData?.reduce((acc, protectedPVCItem) => { + const replicationHealth = getVolumeReplicationHealth( + getTimeDifferenceInSeconds(protectedPVCItem?.lastSyncTime), + protectedPVCItem?.schedulingInterval + )[0]; + + (!!selectedAppSet + ? protectedPVCItem?.drpcName === placementInfo?.drpcName && + protectedPVCItem?.drpcNamespace === placementInfo?.drpcNamespace && + replicationHealth !== VOLUME_REPLICATION_HEALTH.HEALTHY + : replicationHealth !== VOLUME_REPLICATION_HEALTH.HEALTHY) && acc++; + + return acc; + }, 0) || 0; + setProtectedPVC([protectedPVCData?.length || 0, issueCount]); + }, [selectedAppSet, protectedPVCData, setProtectedPVC]); + + React.useEffect(() => { + updateProtectedPVC(); + clearSetIntervalId.current = setInterval( + updateProtectedPVC, + URL_POLL_DEFAULT_DELAY + ); + return () => clearInterval(clearSetIntervalId.current); + }, [updateProtectedPVC]); + + return ( +
+ {protectedPVCsCount} + {t('Protected PVCs (ApplicationSet)')} + + {t('{{ pvcsWithIssueCount }} with issues', { pvcsWithIssueCount })} + +
+ ); +}; + +type ProtectedPVCsSectionProps = { + protectedPVCData: ProtectedPVCData[]; + selectedAppSet?: ProtectedAppSetsMap; +}; + type VolumeSummarySectionProps = { protectedPVCData: ProtectedPVCData[]; selectedAppSet?: ProtectedAppSetsMap; diff --git a/packages/mco/components/mco-dashboard/data-policy/summary-card/summary-card.tsx b/packages/mco/components/mco-dashboard/data-policy/summary-card/summary-card.tsx index 5578bf2c5..617375180 100644 --- a/packages/mco/components/mco-dashboard/data-policy/summary-card/summary-card.tsx +++ b/packages/mco/components/mco-dashboard/data-policy/summary-card/summary-card.tsx @@ -3,9 +3,11 @@ import { MANAGED_CLUSTER_CONDITION_AVAILABLE } from '@odf/mco/constants'; import { DrClusterAppsMap } from '@odf/mco/types'; import { getManagedClusterCondition } from '@odf/mco/utils'; import HealthItem from '@odf/shared/dashboards/status-card/HealthItem'; +import { FieldLevelHelp } from '@odf/shared/generic'; import { DataUnavailableError } from '@odf/shared/generic/Error'; import { useCustomTranslation } from '@odf/shared/useCustomTranslationHook'; import { HealthState } from '@openshift-console/dynamic-plugin-sdk'; +import { Trans } from 'react-i18next'; import { Card, CardBody, @@ -105,13 +107,20 @@ export const SummaryCard: React.FC = () => { {t('Applications')} + + + The applications count displays the total number of{' '} + ApplicationSet type applications in all disaster + recovery configured clusters. + + {summaryMap.applications.totalCount} - {t('{{ protected }} DR protected', { + {t('{{ protected }} protected', { protected: summaryMap.applications.protectedCount, })} diff --git a/packages/mco/components/mco-dashboard/queries.ts b/packages/mco/components/mco-dashboard/queries.ts index e66270d95..e57ab0c17 100644 --- a/packages/mco/components/mco-dashboard/queries.ts +++ b/packages/mco/components/mco-dashboard/queries.ts @@ -22,11 +22,6 @@ export enum DRDashboard { RBD_SNAPSHOT_SNAPSHOTS = 'RBD_SNAPSHOT_SNAPSHOTS', } -// "recording_rules" added in "observability-metrics-custom-allowlist" ConfigMap -export const TOTAL_PVC_COUNT_QUERY = 'count_persistentvolumeclaim_total'; -export const getTotalPVCCountPerClusterQuery = (clusterName: string) => - `${TOTAL_PVC_COUNT_QUERY}{cluster="${clusterName}"}`; - export const LAST_SYNC_TIME_QUERY = 'ramen_sync_duration_seconds'; export const getLastSyncPerClusterQuery = () => `${LAST_SYNC_TIME_QUERY}{${DRPC_OBJECT_TYPE}, ${RAMEN_HUB_OPERATOR_METRICS_SERVICE}}`; diff --git a/packages/mco/constants/dashboard.ts b/packages/mco/constants/dashboard.ts index e73579137..e43ec3a94 100644 --- a/packages/mco/constants/dashboard.ts +++ b/packages/mco/constants/dashboard.ts @@ -1,4 +1,4 @@ -export const ALL_APPS = 'All applications'; +export const ALL_APPS = 'All ApplicationSet'; export const ALL_APPS_ITEM_ID = 'all-applications-itemid'; // Volume replication threshold diff --git a/packages/shared/src/alert/AlertsPanel.tsx b/packages/shared/src/alert/AlertsPanel.tsx index 20190dcf2..c3d3e4f8f 100644 --- a/packages/shared/src/alert/AlertsPanel.tsx +++ b/packages/shared/src/alert/AlertsPanel.tsx @@ -27,6 +27,7 @@ import { import { Divider } from '@patternfly/react-core'; import { InfoCircleIcon } from '@patternfly/react-icons'; import './alerts.scss'; +import { FieldLevelHelp } from '../generic'; type AlertBadgeProps = { alerts: Alert[]; @@ -135,6 +136,7 @@ type AlertsProps = { alertsFilter?: (alert: Alert) => boolean; className?: string; AlertItemComponent?: React.FC>; + titleToolTip?: JSX.Element | string; loaded: boolean; loadError: object; }; @@ -144,6 +146,7 @@ const AlertsPanel: React.FC = ({ alertsFilter, className, AlertItemComponent, + titleToolTip, loaded, loadError, }) => { @@ -182,6 +185,7 @@ const AlertsPanel: React.FC = ({ > {t('Alerts')} ({filteredAlerts.length}) + {!!titleToolTip && <FieldLevelHelp>{titleToolTip}</FieldLevelHelp>} <>