diff --git a/locales/en/plugin__odf-console.json b/locales/en/plugin__odf-console.json index a39692fe6..12f92556e 100644 --- a/locales/en/plugin__odf-console.json +++ b/locales/en/plugin__odf-console.json @@ -156,6 +156,7 @@ "Unknown": "Unknown", "Namespaces": "Namespaces", "Volume snapshot": "Volume snapshot", + "Kubernetes object snapshot": "Kubernetes object snapshot", "Last on: {{ syncTime }}": "Last on: {{ syncTime }}", "Subscription": "Subscription", "Last snapshot synced": "Last snapshot synced", diff --git a/packages/mco/components/mco-dashboard/disaster-recovery/cluster-app-card/application.tsx b/packages/mco/components/mco-dashboard/disaster-recovery/cluster-app-card/application.tsx index c0ddde416..c39f5d425 100644 --- a/packages/mco/components/mco-dashboard/disaster-recovery/cluster-app-card/application.tsx +++ b/packages/mco/components/mco-dashboard/disaster-recovery/cluster-app-card/application.tsx @@ -185,13 +185,16 @@ export const NamespaceSection: React.FC = ({ ); }; -export const SnapshotSection: React.FC = ({ +export const SnapshotSection: React.FC = ({ selectedApplication, + isVolumeSnapshot, }) => { const { t } = useCustomTranslation(); const [syncTime, setSyncTime] = React.useState('N/A'); - const lastSyncTime = - selectedApplication?.placementControlInfo?.[0]?.lastVolumeGroupSyncTime; + const lastSyncTime = isVolumeSnapshot + ? selectedApplication?.placementControlInfo?.[0]?.lastVolumeGroupSyncTime + : selectedApplication?.placementControlInfo?.[0] + ?.kubeObjectLastProtectionTime; const updateSyncTime = React.useCallback(() => { setSyncTime( @@ -203,9 +206,13 @@ export const SnapshotSection: React.FC = ({ useScheduler(updateSyncTime); + const title = isVolumeSnapshot + ? t('Volume snapshot') + : t('Kubernetes object snapshot'); + return (
- {t('Volume snapshot')} + {title} {t('Last on: {{ syncTime }}', { syncTime: syncTime, @@ -391,3 +398,7 @@ type SubscriptionRowProps = { type SubscriptionWiseRPOMap = { [subscriptionName: string]: string; }; + +type SnapshotSectionProps = CommonProps & { + isVolumeSnapshot?: boolean; +}; diff --git a/packages/mco/components/mco-dashboard/disaster-recovery/cluster-app-card/cluster-app-card.tsx b/packages/mco/components/mco-dashboard/disaster-recovery/cluster-app-card/cluster-app-card.tsx index 2273ab0de..8180505e7 100644 --- a/packages/mco/components/mco-dashboard/disaster-recovery/cluster-app-card/cluster-app-card.tsx +++ b/packages/mco/components/mco-dashboard/disaster-recovery/cluster-app-card/cluster-app-card.tsx @@ -88,15 +88,14 @@ const DiscoveredAppCard: React.FC = ({ - + - - { - // ToDo: Enable snapshot card for kube object last sync time once the DRPC has the status. - /* + - */ - } + { - const kubeObjectLastTransitionTime = - protectedAppMap.placementControlInfo[0].kubeObjectLastTransitionTime; + const { kubeObjectLastProtectionTime, replicationType } = + protectedAppMap.placementControlInfo[0]; const objCaptureInterval = protectedAppMap.placementControlInfo[0].kubeObjSyncInterval; - return protectedAppMap.appType === APPLICATION_TYPE.DISCOVERED + return protectedAppMap.appType === APPLICATION_TYPE.DISCOVERED && + replicationType === REPLICATION_TYPE.ASYNC ? getVolumeReplicationHealth( - !!kubeObjectLastTransitionTime - ? getTimeDifferenceInSeconds(kubeObjectLastTransitionTime) + !!kubeObjectLastProtectionTime + ? getTimeDifferenceInSeconds(kubeObjectLastProtectionTime) : LEAST_SECONDS_IN_PROMETHEUS, objCaptureInterval )[0] !== VOLUME_REPLICATION_HEALTH.HEALTHY diff --git a/packages/mco/components/mco-dashboard/disaster-recovery/parsers/discovered-parser.tsx b/packages/mco/components/mco-dashboard/disaster-recovery/parsers/discovered-parser.tsx index 0e4db0763..ea9206c7e 100644 --- a/packages/mco/components/mco-dashboard/disaster-recovery/parsers/discovered-parser.tsx +++ b/packages/mco/components/mco-dashboard/disaster-recovery/parsers/discovered-parser.tsx @@ -20,7 +20,6 @@ import { filerDRClustersUsingDRPolicy, findDRPolicyUsingDRPC, findDRType, - getKubeObjectLastTransitionTime, getLastAppDeploymentClusterName, getProtectedPVCsFromDRPC, } from '@odf/mco/utils'; @@ -122,10 +121,8 @@ export const useDiscoveredParser: UseDiscoveredParser = ( kubeObjSyncInterval: drPlacementControl.spec?.kubeObjectProtection ?.captureInterval, - // lastTransitionTime just to decide the status, it can't be considered as last synced on. - kubeObjectLastTransitionTime: getKubeObjectLastTransitionTime( - drPlacementControl?.status?.resourceConditions?.conditions - ), + kubeObjectLastProtectionTime: + drPlacementControl?.status?.lastKubeObjectProtectionTime, }, ], }); diff --git a/packages/mco/components/protected-applications/components.tsx b/packages/mco/components/protected-applications/components.tsx index 7d94df167..ac527f537 100644 --- a/packages/mco/components/protected-applications/components.tsx +++ b/packages/mco/components/protected-applications/components.tsx @@ -388,9 +388,9 @@ export const StatusDetails: React.FC = ({ syncStatusInfo.volumeLastGroupSyncTime || ( {t('No data available')} ), - // For kube object, it is always no data available message. - // lastTransitionTime just to decide the status, it can't be considered as last synced on. - {t('No data available')}, + syncStatusInfo.kubeObjectLastProtectionTime || ( + {t('No data available')} + ), ]; return ( diff --git a/packages/mco/components/protected-applications/list-page.tsx b/packages/mco/components/protected-applications/list-page.tsx index e84b3a41d..0badd798f 100644 --- a/packages/mco/components/protected-applications/list-page.tsx +++ b/packages/mco/components/protected-applications/list-page.tsx @@ -33,7 +33,7 @@ import { DRPlacementControlKind, DRPolicyKind } from '../../types'; import { getLastAppDeploymentClusterName, getDRPolicyName, - getKubeObjectLastTransitionTime, + getReplicationType, } from '../../utils'; import { EmptyRowMessage, @@ -261,21 +261,26 @@ export const ProtectedApplicationsListPage: React.FC = () => { const volumesSchedulingInterval = drPolicies.find( (policy) => getName(policy) === app.spec?.drPolicyRef?.name )?.spec?.schedulingInterval; + const replicationType = getReplicationType(volumesSchedulingInterval); const volumesLastSyncTime = app?.status?.lastGroupSyncTime; const kubeObjectsSchedulingInterval = app.spec?.kubeObjectProtection?.captureInterval; - const kubeObjectLastTransitionTime = getKubeObjectLastTransitionTime( - app?.status?.resourceConditions?.conditions - ); + const kubeObjectLastProtectionTime = + app?.status?.lastKubeObjectProtectionTime; acc[getName(app)] = { volumeReplicationStatus: getReplicationHealth( volumesLastSyncTime, - volumesSchedulingInterval + volumesSchedulingInterval, + replicationType ), volumeLastGroupSyncTime: formatTime(volumesLastSyncTime), kubeObjectReplicationStatus: getReplicationHealth( - kubeObjectLastTransitionTime, - kubeObjectsSchedulingInterval + kubeObjectLastProtectionTime, + kubeObjectsSchedulingInterval, + replicationType + ), + kubeObjectLastProtectionTime: formatTime( + kubeObjectLastProtectionTime ), }; diff --git a/packages/mco/components/protected-applications/utils.tsx b/packages/mco/components/protected-applications/utils.tsx index fe927e2ee..a56415526 100644 --- a/packages/mco/components/protected-applications/utils.tsx +++ b/packages/mco/components/protected-applications/utils.tsx @@ -24,6 +24,7 @@ import { LEAST_SECONDS_IN_PROMETHEUS, DR_BASE_ROUTE, DRActionType, + REPLICATION_TYPE, } from '../../constants'; import { DRPlacementControlModel } from '../../models'; import { DRPlacementControlKind } from '../../types'; @@ -88,8 +89,12 @@ export const isCleanupPending = (drpc: DRPlacementControlKind): boolean => export const getReplicationHealth = ( lastSyncTime: string, - schedulingInterval: string + schedulingInterval: string, + replicationType: REPLICATION_TYPE ): VOLUME_REPLICATION_HEALTH => { + if (replicationType === REPLICATION_TYPE.SYNC) { + return VOLUME_REPLICATION_HEALTH.HEALTHY; + } const seconds = !!lastSyncTime ? getTimeDifferenceInSeconds(lastSyncTime) : LEAST_SECONDS_IN_PROMETHEUS; @@ -138,6 +143,7 @@ export type SyncStatusInfo = { volumeReplicationStatus: VOLUME_REPLICATION_HEALTH; volumeLastGroupSyncTime: string; kubeObjectReplicationStatus: VOLUME_REPLICATION_HEALTH; + kubeObjectLastProtectionTime: string; }; export const getAppWorstSyncStatus = ( diff --git a/packages/mco/constants/disaster-recovery.ts b/packages/mco/constants/disaster-recovery.ts index a89f0ec59..c12e8fbde 100644 --- a/packages/mco/constants/disaster-recovery.ts +++ b/packages/mco/constants/disaster-recovery.ts @@ -98,6 +98,3 @@ export const EnrollDiscoveredApplicationStepNames = (t: TFunction) => ({ [EnrollDiscoveredApplicationSteps.Replication]: t('Replication'), [EnrollDiscoveredApplicationSteps.Review]: t('Review'), }); - -// DRPC status condition types -export const KUBE_OBJECT_SYNC_CONDITION_TYPE = 'ClusterDataProtected'; diff --git a/packages/mco/types/dashboard.ts b/packages/mco/types/dashboard.ts index ac4bcf3a4..d45e5933d 100644 --- a/packages/mco/types/dashboard.ts +++ b/packages/mco/types/dashboard.ts @@ -28,8 +28,8 @@ export type PlacementControlInfo = Partial<{ // Subscription name(Only for display purpose) subscriptions?: string[]; // Only applicable for Discovered type - // ClusterDataProtected condition transition time - kubeObjectLastTransitionTime?: string; + // Recent successful kube object protection Time + kubeObjectLastProtectionTime?: string; // Kube resources backup interval(interval + unit(m, h, d)) kubeObjSyncInterval?: string; }>; diff --git a/packages/mco/types/ramen.ts b/packages/mco/types/ramen.ts index 26e8af69c..e7e4a42b0 100644 --- a/packages/mco/types/ramen.ts +++ b/packages/mco/types/ramen.ts @@ -72,6 +72,8 @@ export type DRPlacementControlKind = K8sResourceCommon & { protectedNamespaces?: string[]; }; status?: { + // The time of the most recent successful kube object protection + lastKubeObjectProtectionTime?: string; conditions?: K8sResourceCondition[]; resourceConditions?: { conditions?: K8sResourceCondition[]; diff --git a/packages/mco/utils/disaster-recovery.tsx b/packages/mco/utils/disaster-recovery.tsx index 8eb87aa80..3a9bfe3a0 100644 --- a/packages/mco/utils/disaster-recovery.tsx +++ b/packages/mco/utils/disaster-recovery.tsx @@ -17,7 +17,6 @@ import { GreenCheckCircleIcon, Alert, AlertStates, - K8sResourceCondition, } from '@openshift-console/dynamic-plugin-sdk'; import { Operator, @@ -34,7 +33,6 @@ import { LABEL_SPLIT_CHAR, LABELS_SPLIT_CHAR, DR_BLOCK_LISTED_LABELS, - KUBE_OBJECT_SYNC_CONDITION_TYPE, } from '../constants'; import { DRPC_NAMESPACE_ANNOTATION, @@ -639,19 +637,3 @@ export const getLabelsFromSearchResult = ( return acc; }, {}); }; - -export const getKubeObjectLastTransitionTime = ( - conditions: K8sResourceCondition[] -): string => { - const condition = conditions?.find( - (condition) => condition.type === KUBE_OBJECT_SYNC_CONDITION_TYPE - ); - if (condition?.status === 'True') { - // For "True", checking the lastTransitionTime to ensure there is no reconcile problem on ramen. - // If there is any reconcile problem, And if status stuck in "True" - // then status will be displayed based on lastTransitionTime (critical/warning). - return condition.lastTransitionTime; - } - // Returning empty to show critical status immediately. - return ''; -};