diff --git a/locales/en/plugin__odf-console.json b/locales/en/plugin__odf-console.json index a1c7fb22c..35ed39d1a 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", + "<0>Note: Alerts are deployed for both <2>ApplicationSet and <5>Subscription type applications.": "<0>Note: Alerts are deployed for both <2>ApplicationSet and <5>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,29 +79,30 @@ "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", + "<0>This graph provides an overview of the total number of snapshots synced across your clusters.<1>Note: Only inbound values are displayed exclusively for block volumes (ceph rbd) and include snapshots from all applications excluding apps using file volumes (cephfs).": "<0>This graph provides an overview of the total number of snapshots synced across your clusters.<1>Note: Only inbound values are displayed exclusively for block volumes (ceph rbd) and include snapshots from all applications excluding apps using file volumes (cephfs).", + "Block volumes replication throughput": "Block volumes replication throughput", + "<0>This graph provides an overview of the replication throughput for the cluster i.e, rate at which data is replicated within the cluster.<1>Note: Only inbound values are displayed exclusively for block volumes (ceph rbd) and include snapshots from all applications excluding apps using file volumes (cephfs).": "<0>This graph provides an overview of the replication throughput for the cluster i.e, rate at which data is replicated within the cluster.<1>Note: Only inbound values are displayed exclusively for block volumes (ceph rbd) and include snapshots from all applications excluding apps using file volumes (cephfs).", + "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", + "<0>Note: The applications count displayed herein pertain exclusively to <2>ApplicationsSet type applications.": "<0>Note: The applications count displayed herein pertain exclusively to <2>ApplicationsSet type applications.", "{{ protected }} DR protected": "{{ protected }} DR protected", "Systems": "Systems", "Storage System status": "Storage System status", 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..8f09b53c9 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={ + + Note: Alerts are deployed 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..af7a92aec 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,19 @@ 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, + // 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,9 +97,9 @@ 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..d96250fb7 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'; @@ -45,6 +38,7 @@ import { Humanize, } from '@openshift-console/dynamic-plugin-sdk'; import { UtilizationDurationDropdown } from '@openshift-console/dynamic-plugin-sdk-internal'; +import { Trans } from 'react-i18next'; import { Flex, Text, TextVariants } from '@patternfly/react-core'; import { ConnectedIcon } from '@patternfly/react-icons'; import { StatusText } from './common'; @@ -212,7 +206,7 @@ export const ApplicationsSection: React.FC = ({ return (
{totalAppSetsCount || 0} - {t('Total applications')} + {t('Total applications (ApplicationSet)')} {t(' {{ protectedAppSetsCount }} protected apps', { protectedAppSetsCount, @@ -228,33 +222,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,23 +321,44 @@ export const UtilizationCard: React.FC = ({
+

+ This graph provides an overview of the total number of snapshots + synced across your clusters. +

+

+ Note: Only inbound values are displayed exclusively for block + volumes (ceph rbd) and include snapshots from all applications + excluding apps using file volumes (cephfs). +

+ + } humanizeValue={humanizeNumber} />
+

+ This graph provides an overview of the replication throughput + for the cluster i.e, rate at which data is replicated within the + cluster. +

+

+ Note: Only inbound values are displayed exclusively for block + volumes (ceph rbd) and include snapshots from all applications + excluding apps using file volumes (cephfs). +

+ + } CustomUtilizationSummary={CustomUtilizationSummary} />
@@ -403,18 +391,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..c38f6c5ef 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 drpcChecks = + selectedAppSet && + protectedPVCItem?.drpcName === placementInfo?.drpcName && + protectedPVCItem?.drpcNamespace === placementInfo?.drpcNamespace; + + const replicationHealth = getVolumeReplicationHealth( + getTimeDifferenceInSeconds(protectedPVCItem?.lastSyncTime), + protectedPVCItem?.schedulingInterval + )[0]; + + if ( + (!selectedAppSet && + replicationHealth !== VOLUME_REPLICATION_HEALTH.HEALTHY) || + (selectedAppSet && + drpcChecks && + replicationHealth !== 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 (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..dc3a343a7 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,6 +107,12 @@ export const SummaryCard: React.FC = () => { {t('Applications')} + + + Note: The applications count displayed herein pertain + exclusively to ApplicationsSet type applications. + + {summaryMap.applications.totalCount} diff --git a/packages/mco/constants/dashboard.ts b/packages/mco/constants/dashboard.ts index e73579137..8845c3414 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>} <>