From 3465b4872ba5fa785e74bf36ef356a627e001ab9 Mon Sep 17 00:00:00 2001
From: Timothy Asir Jeyasingh
Date: Fri, 18 Oct 2024 16:16:25 +0530
Subject: [PATCH] Display non-blocking warnings for synchronization delays
https://issues.redhat.com/browse/RHSTOR-5895
Signed-off-by: Timothy Asir Jeyasingh
---
locales/en/plugin__odf-console.json | 2 +
.../failover-relocate-modal-body.tsx | 61 ++++++---
.../failover-relocate-modal.tsx | 3 +-
.../helper/error-messages.tsx | 12 ++
.../parser/argo-application-set-parser.tsx | 15 ++-
.../subscriptions/dr-policy-selector.tsx | 1 +
.../subscriptions/error-messages.tsx | 13 ++
.../failover-relocate-modal-body.tsx | 10 +-
.../subscriptions/failover-relocate-modal.tsx | 2 +-
.../subscriptions/peer-cluster-status.tsx | 121 +++++++++++++-----
.../subscriptions/reducer.ts | 3 +
.../protected-applications/list-page.tsx | 2 +-
.../protected-applications/utils.tsx | 17 ---
packages/mco/utils/disaster-recovery.tsx | 16 +++
14 files changed, 205 insertions(+), 73 deletions(-)
diff --git a/locales/en/plugin__odf-console.json b/locales/en/plugin__odf-console.json
index 53e46fda8..f5600a8c5 100644
--- a/locales/en/plugin__odf-console.json
+++ b/locales/en/plugin__odf-console.json
@@ -307,6 +307,8 @@
"Other applications may be affected.": "Other applications may be affected.",
"<0>This application uses placement that are also used by other applications. Failing over will automatically trigger a failover for other applications sharing the same placement.0>": "<0>This application uses placement that are also used by other applications. Failing over will automatically trigger a failover for other applications sharing the same placement.0>",
"<0>This application uses placement that are also used by other applications. Relocating will automatically trigger a relocate for other applications sharing the same placement.0>": "<0>This application uses placement that are also used by other applications. Relocating will automatically trigger a relocate for other applications sharing the same placement.0>",
+ "Inconsistent data on target cluster": "Inconsistent data on target cluster",
+ "The target cluster's volumes contain data inconsistencies caused by synchronization delays. Performing the failover could lead to data loss. Refer to the corresponding VolumeSynchronizationDelay OpenShift alert(s) for more information.": "The target cluster's volumes contain data inconsistencies caused by synchronization delays. Performing the failover could lead to data loss. Refer to the corresponding VolumeSynchronizationDelay OpenShift alert(s) for more information.",
"Attention": "Attention",
"A failover will occur for all namespaces currently under this DRPC.": "A failover will occur for all namespaces currently under this DRPC.",
"You need to clean up manually to begin replication after a successful failover.": "You need to clean up manually to begin replication after a successful failover.",
diff --git a/packages/mco/components/modals/app-failover-relocate/failover-relocate-modal-body.tsx b/packages/mco/components/modals/app-failover-relocate/failover-relocate-modal-body.tsx
index 807eedded..8e9790916 100644
--- a/packages/mco/components/modals/app-failover-relocate/failover-relocate-modal-body.tsx
+++ b/packages/mco/components/modals/app-failover-relocate/failover-relocate-modal-body.tsx
@@ -1,4 +1,5 @@
import * as React from 'react';
+import { getReplicationHealth } from '@odf/mco/utils';
import { ACM_DEFAULT_DOC_VERSION } from '@odf/shared/constants';
import { utcDateTimeFormatter } from '@odf/shared/details-page/datetime';
import { useDocVersion, DOC_VERSION as mcoDocVersion } from '@odf/shared/hooks';
@@ -25,6 +26,7 @@ import {
DRActionType,
REPLICATION_TYPE,
ACM_OPERATOR_SPEC_NAME,
+ VOLUME_REPLICATION_HEALTH,
} from '../../../constants';
import {
ErrorMessageType,
@@ -79,30 +81,56 @@ const validatePlacement = (
action: DRActionType
): ErrorMessageType => {
const isFailoverAction = action === DRActionType.FAILOVER;
+
+ // Check if DR is disabled
if (!placementControl?.drPlacementControlName) {
- // DR is disabled
return isFailoverAction
? ErrorMessageType.DR_IS_NOT_ENABLED_FAILOVER
: ErrorMessageType.DR_IS_NOT_ENABLED_RELOCATE;
- } else if (!placementControl?.isDRActionReady) {
- // Either Peer is not ready for DR failover
- // Or, Peer is not ready/available for DR relocate
+ }
+
+ // Check if DR action is ready
+ // Either Peer is not ready for DR failover
+ // Or, Peer is not ready/available for DR relocate
+ if (!placementControl?.isDRActionReady) {
return isFailoverAction
? ErrorMessageType.FAILOVER_READINESS_CHECK_FAILED
: ErrorMessageType.RELOCATE_READINESS_CHECK_FAILED;
- } else {
- const errorMessage = isFailoverAction
- ? failoverPreCheck(placementControl)
- : relocatePreCheck(placementControl);
- if (!errorMessage) {
- if (placementControl?.areSiblingApplicationsFound) {
- return isFailoverAction
- ? ErrorMessageType.SIBLING_APPLICATIONS_FOUND_FAILOVER
- : ErrorMessageType.SIBLING_APPLICATIONS_FOUND_RELOCATE;
- }
- }
- return errorMessage;
}
+
+ // Perform failover or relocate pre-check
+ const preCheckError = isFailoverAction
+ ? failoverPreCheck(placementControl)
+ : relocatePreCheck(placementControl);
+
+ if (preCheckError) {
+ return preCheckError;
+ }
+
+ // Check volume replication health
+ if (
+ [
+ VOLUME_REPLICATION_HEALTH.CRITICAL,
+ VOLUME_REPLICATION_HEALTH.WARNING,
+ ].includes(
+ getReplicationHealth(
+ placementControl?.snapshotTakenTime,
+ placementControl?.schedulingInterval,
+ placementControl?.replicationType
+ )
+ )
+ ) {
+ return ErrorMessageType.VOLUME_SYNC_DELAY;
+ }
+
+ // Check if sibling applications are found
+ if (placementControl?.areSiblingApplicationsFound) {
+ return isFailoverAction
+ ? ErrorMessageType.SIBLING_APPLICATIONS_FOUND_FAILOVER
+ : ErrorMessageType.SIBLING_APPLICATIONS_FOUND_RELOCATE;
+ }
+
+ return null;
};
const MessageStatus = ({ message }: { message: MessageKind }) => (
@@ -331,6 +359,7 @@ export type PlacementControlProps = Partial<{
isPrimaryClusterFenced: boolean;
areSiblingApplicationsFound: boolean;
kubeObjectLastSyncTime: string;
+ schedulingInterval: string;
}>;
export type ApplicationProps = {
diff --git a/packages/mco/components/modals/app-failover-relocate/failover-relocate-modal.tsx b/packages/mco/components/modals/app-failover-relocate/failover-relocate-modal.tsx
index 42569d164..1397bc19d 100644
--- a/packages/mco/components/modals/app-failover-relocate/failover-relocate-modal.tsx
+++ b/packages/mco/components/modals/app-failover-relocate/failover-relocate-modal.tsx
@@ -60,7 +60,7 @@ export const FailoverRelocateModal: React.FC = (
const onClick = () => {
setFooterStatus(ModalFooterStatus.INPROGRESS);
- // Prefered cluster and failover cluster should not be same for failover and relocate.
+ // Preferred cluster and failover cluster should not be the same for failover and relocate.
const patch = [
{
op: 'replace',
@@ -120,6 +120,7 @@ export const FailoverRelocateModal: React.FC = (
setCanInitiate={setCanInitiate}
setPlacement={setPlacementControl}
/>
+
{(!!errorMessage || !!loadError) && (
+ {t(
+ "The target cluster's volumes contain data inconsistencies caused by synchronization delays. Performing the failover could lead to data loss. Refer to the corresponding VolumeSynchronizationDelay OpenShift alert(s) for more information."
+ )}
+
+ ),
+ variant: AlertVariant.warning,
+ },
});
export const evaluateErrorMessage = (
diff --git a/packages/mco/components/modals/app-failover-relocate/parser/argo-application-set-parser.tsx b/packages/mco/components/modals/app-failover-relocate/parser/argo-application-set-parser.tsx
index 4eb4a3db7..dea0e1674 100644
--- a/packages/mco/components/modals/app-failover-relocate/parser/argo-application-set-parser.tsx
+++ b/packages/mco/components/modals/app-failover-relocate/parser/argo-application-set-parser.tsx
@@ -85,9 +85,11 @@ export const ArogoApplicationSetParser = (
props: ArogoApplicationSetParserProps
) => {
const { application, action, isOpen, close } = props;
+
const [drResources, drLoaded, drLoadError] = useDisasterRecoveryResourceWatch(
getDRResources(getNamespace(application))
);
+
const [aroAppSetResources, loaded, loadError] =
useArgoApplicationSetResourceWatch(
getApplicationSetResources(
@@ -99,15 +101,22 @@ export const ArogoApplicationSetParser = (
drLoadError
)
);
+
const aroAppSetResource = aroAppSetResources?.formattedResources?.[0];
+
const placementControls: PlacementControlProps[] = React.useMemo(() => {
const {
managedClusters,
siblingApplications,
placements: resourcePlacements,
} = aroAppSetResource || {};
- const { drClusters, drPlacementControl, placementDecision, placement } =
- resourcePlacements?.[0] || {};
+ const {
+ drClusters,
+ drPolicy,
+ drPlacementControl,
+ placementDecision,
+ placement,
+ } = resourcePlacements?.[0] || {};
const deploymentClusters = findDeploymentClusters(
placementDecision,
drPlacementControl
@@ -133,6 +142,7 @@ export const ArogoApplicationSetParser = (
targetCluster,
MANAGED_CLUSTER_CONDITION_AVAILABLE
);
+
return loaded && !loadError
? [
{
@@ -150,6 +160,7 @@ export const ArogoApplicationSetParser = (
isTargetClusterFenced: isDRClusterFenced(targetDRCluster),
isPrimaryClusterFenced: isDRClusterFenced(primaryDRCluster),
areSiblingApplicationsFound: !!siblingApplications?.length,
+ schedulingInterval: drPolicy?.spec?.schedulingInterval,
},
]
: [];
diff --git a/packages/mco/components/modals/app-failover-relocate/subscriptions/dr-policy-selector.tsx b/packages/mco/components/modals/app-failover-relocate/subscriptions/dr-policy-selector.tsx
index dd6877220..5c31044b2 100644
--- a/packages/mco/components/modals/app-failover-relocate/subscriptions/dr-policy-selector.tsx
+++ b/packages/mco/components/modals/app-failover-relocate/subscriptions/dr-policy-selector.tsx
@@ -89,6 +89,7 @@ export const DRPolicySelector: React.FC = ({
setSelected({
policyName: getName(drPolicy),
drClusters: drPolicy?.spec?.drClusters,
+ schedulingInterval: drPolicy?.spec?.schedulingInterval,
});
setOpen(false);
};
diff --git a/packages/mco/components/modals/app-failover-relocate/subscriptions/error-messages.tsx b/packages/mco/components/modals/app-failover-relocate/subscriptions/error-messages.tsx
index 42ed20d49..4fc7de4da 100644
--- a/packages/mco/components/modals/app-failover-relocate/subscriptions/error-messages.tsx
+++ b/packages/mco/components/modals/app-failover-relocate/subscriptions/error-messages.tsx
@@ -17,6 +17,8 @@ export enum ErrorMessageType {
PRIMARY_CLUSTER_IS_NOT_FENCED,
TARGET_CLUSTER_IS_FENCED,
NO_SUBSCRIPTION_GROUP_FOUND,
+ // Warning Messages
+ VOLUME_SYNC_DELAY,
}
export type MessageKind = Partial<{
@@ -243,4 +245,15 @@ export const ErrorMessages = (
title: t('No subscription groups are found.'),
variant: AlertVariant.danger,
},
+ [ErrorMessageType.VOLUME_SYNC_DELAY]: {
+ title: t('Inconsistent data on target cluster'),
+ message: (
+
+ {t(
+ "The target cluster's volumes contain data inconsistencies caused by synchronization delays. Performing the failover could lead to data loss. Refer to the corresponding VolumeSynchronizationDelay OpenShift alert(s) for more information."
+ )}
+
+ ),
+ variant: AlertVariant.warning,
+ },
});
diff --git a/packages/mco/components/modals/app-failover-relocate/subscriptions/failover-relocate-modal-body.tsx b/packages/mco/components/modals/app-failover-relocate/subscriptions/failover-relocate-modal-body.tsx
index 6962d9a1f..6434f83af 100644
--- a/packages/mco/components/modals/app-failover-relocate/subscriptions/failover-relocate-modal-body.tsx
+++ b/packages/mco/components/modals/app-failover-relocate/subscriptions/failover-relocate-modal-body.tsx
@@ -45,13 +45,17 @@ import {
import { SubscriptionGroupSelector } from './subscription-group-selector';
import { TargetClusterSelector } from './target-cluster-selector';
-export const findErrorMessage = (errorMessage: ErrorMessage) =>
+export const findErrorMessage = (
+ errorMessage: ErrorMessage,
+ includeWarning: Boolean
+) =>
[
errorMessage.drPolicyControlStateErrorMessage,
errorMessage.managedClustersErrorMessage,
errorMessage.targetClusterErrorMessage,
errorMessage.subscriptionGroupErrorMessage,
errorMessage.peerStatusErrorMessage,
+ includeWarning && errorMessage.syncDelayWarningMessage,
]
.filter(Boolean)
.find((errorMessageItem) => errorMessageItem);
@@ -250,11 +254,11 @@ export const FailoverRelocateModalBody: React.FC
})}
/>
)) ||
- ((!!findErrorMessage(state.errorMessage) ||
+ ((!!findErrorMessage(state.errorMessage, true) ||
!_.isEmpty(state.actionErrorMessage)) && (
))}
diff --git a/packages/mco/components/modals/app-failover-relocate/subscriptions/failover-relocate-modal.tsx b/packages/mco/components/modals/app-failover-relocate/subscriptions/failover-relocate-modal.tsx
index 2069c0fa4..8ef4a0899 100644
--- a/packages/mco/components/modals/app-failover-relocate/subscriptions/failover-relocate-modal.tsx
+++ b/packages/mco/components/modals/app-failover-relocate/subscriptions/failover-relocate-modal.tsx
@@ -109,7 +109,7 @@ export const SubscriptionFailoverRelocateModal: React.FC
- !findErrorMessage(state.errorMessage) &&
+ !findErrorMessage(state.errorMessage, false) &&
!!state.selectedSubsGroups.length;
const onClick = () => {
diff --git a/packages/mco/components/modals/app-failover-relocate/subscriptions/peer-cluster-status.tsx b/packages/mco/components/modals/app-failover-relocate/subscriptions/peer-cluster-status.tsx
index 75683877f..3d34c9a98 100644
--- a/packages/mco/components/modals/app-failover-relocate/subscriptions/peer-cluster-status.tsx
+++ b/packages/mco/components/modals/app-failover-relocate/subscriptions/peer-cluster-status.tsx
@@ -10,8 +10,12 @@ import {
import { TFunction } from 'i18next';
import { Flex, FlexItem } from '@patternfly/react-core';
import { UnknownIcon } from '@patternfly/react-icons';
-import { DRActionType } from '../../../../constants';
-import { checkDRActionReadiness } from '../../../../utils';
+import { DRActionType, VOLUME_REPLICATION_HEALTH } from '../../../../constants';
+import {
+ checkDRActionReadiness,
+ getReplicationHealth,
+ getReplicationType,
+} from '../../../../utils';
import { DateTimeFormat } from '../failover-relocate-modal-body';
import { ErrorMessageType } from './error-messages';
import {
@@ -35,6 +39,7 @@ const initalPeerStatus = (t: TFunction) => ({
dataLastSyncedOn: {
text: '',
},
+ replicationHealth: VOLUME_REPLICATION_HEALTH.HEALTHY,
});
const getPeerReadiness = (
@@ -75,29 +80,58 @@ const getPeerStatusSummary = (
drpcStateList: DRPolicyControlState[],
subsGroups: string[],
actionType: DRActionType,
+ schedulingInterval: string,
t: TFunction
) =>
- // Verify all DRPC has Peer ready status
- drpcStateList?.reduce(
- (acc, drpcState) =>
- subsGroups.includes(getName(drpcState?.drPlacementControl))
- ? {
- ...acc,
- peerReadiness: getPeerReadiness(
- acc.peerReadiness,
- drpcState,
- actionType,
- t
- ),
- dataLastSyncedOn: getDataLastSyncTime(
- acc.dataLastSyncedOn,
- drpcState
- ),
- }
- : acc,
- initalPeerStatus(t)
+ drpcStateList?.reduce((acc, drpcState) => {
+ const drPlacementControl = drpcState?.drPlacementControl;
+
+ if (subsGroups.includes(getName(drPlacementControl))) {
+ const lastGroupSyncTime = drPlacementControl?.status?.lastGroupSyncTime;
+ const higherSeverityHealth = getHigherSeverityHealth(
+ acc.replicationHealth,
+ lastGroupSyncTime,
+ schedulingInterval
+ );
+
+ return {
+ ...acc,
+ peerReadiness: getPeerReadiness(
+ acc.peerReadiness,
+ drpcState,
+ actionType,
+ t
+ ),
+ dataLastSyncedOn: getDataLastSyncTime(acc.dataLastSyncedOn, drpcState),
+ replicationHealth: higherSeverityHealth,
+ };
+ }
+ return acc;
+ }, initalPeerStatus(t));
+
+const getHigherSeverityHealth = (
+ previousHealth: VOLUME_REPLICATION_HEALTH,
+ lastGroupSyncTime: string,
+ schedulingInterval: string
+): VOLUME_REPLICATION_HEALTH => {
+ const replicationType = getReplicationType(schedulingInterval);
+ const currentHealth = getReplicationHealth(
+ lastGroupSyncTime,
+ schedulingInterval,
+ replicationType
);
+ if (
+ [
+ VOLUME_REPLICATION_HEALTH.CRITICAL,
+ VOLUME_REPLICATION_HEALTH.WARNING,
+ ].includes(currentHealth)
+ ) {
+ return currentHealth;
+ }
+ return previousHealth;
+};
+
type StatusProps = {
icon?: JSX.Element;
text: string;
@@ -113,7 +147,12 @@ export const PeerClusterStatus: React.FC = ({
dispatch,
}) => {
const { t } = useCustomTranslation();
- const { selectedSubsGroups, drPolicyControlState, actionType } = state;
+ const {
+ selectedSubsGroups,
+ drPolicyControlState,
+ actionType,
+ selectedDRPolicy,
+ } = state;
const [peerStatus, setPeerStatus] = React.useState(
initalPeerStatus(t)
);
@@ -121,9 +160,10 @@ export const PeerClusterStatus: React.FC = ({
(errorMessage: ErrorMessageType) => {
dispatch({
type: FailoverAndRelocateType.SET_ERROR_MESSAGE,
- payload: {
- peerStatusErrorMessage: errorMessage,
- },
+ payload:
+ errorMessage !== ErrorMessageType.VOLUME_SYNC_DELAY
+ ? { peerStatusErrorMessage: errorMessage }
+ : { syncDelayWarningMessage: errorMessage },
});
},
[dispatch]
@@ -135,15 +175,31 @@ export const PeerClusterStatus: React.FC = ({
drPolicyControlState,
selectedSubsGroups,
actionType,
+ selectedDRPolicy.schedulingInterval,
t
);
- peerCurrentStatus.peerReadiness.text === PEER_READINESS(t).PEER_NOT_READY
- ? setErrorMessage(
- actionType === DRActionType.FAILOVER
- ? ErrorMessageType.FAILOVER_READINESS_CHECK_FAILED
- : ErrorMessageType.RELOCATE_READINESS_CHECK_FAILED
- )
- : setErrorMessage(0 as ErrorMessageType);
+ if (
+ peerCurrentStatus.peerReadiness.text ===
+ PEER_READINESS(t).PEER_NOT_READY
+ ) {
+ setErrorMessage(
+ actionType === DRActionType.FAILOVER
+ ? ErrorMessageType.FAILOVER_READINESS_CHECK_FAILED
+ : ErrorMessageType.RELOCATE_READINESS_CHECK_FAILED
+ );
+ } else if (
+ !!peerCurrentStatus.replicationHealth &&
+ [
+ VOLUME_REPLICATION_HEALTH.CRITICAL,
+ VOLUME_REPLICATION_HEALTH.WARNING,
+ ].includes(
+ peerCurrentStatus.replicationHealth as VOLUME_REPLICATION_HEALTH
+ )
+ ) {
+ setErrorMessage(ErrorMessageType.VOLUME_SYNC_DELAY);
+ } else {
+ setErrorMessage(0 as ErrorMessageType);
+ }
setPeerStatus(peerCurrentStatus);
} else {
// Default peer status is Unknown
@@ -152,6 +208,7 @@ export const PeerClusterStatus: React.FC = ({
}
}, [
selectedSubsGroups,
+ selectedDRPolicy.schedulingInterval,
drPolicyControlState,
actionType,
t,
diff --git a/packages/mco/components/modals/app-failover-relocate/subscriptions/reducer.ts b/packages/mco/components/modals/app-failover-relocate/subscriptions/reducer.ts
index 1e9ace5e6..52a6284e9 100644
--- a/packages/mco/components/modals/app-failover-relocate/subscriptions/reducer.ts
+++ b/packages/mco/components/modals/app-failover-relocate/subscriptions/reducer.ts
@@ -19,6 +19,7 @@ export type DRPolicyControlState = ApplicationDRInfo;
export type DRPolicyType = Partial<{
policyName: string;
drClusters: string[];
+ schedulingInterval: string;
}>;
export type TargetClusterType = Partial<{
@@ -33,6 +34,7 @@ export type ErrorMessage = Partial<{
targetClusterErrorMessage: number;
subscriptionGroupErrorMessage: number;
peerStatusErrorMessage: number;
+ syncDelayWarningMessage: number;
}>;
export type FailoverAndRelocateState = {
@@ -72,6 +74,7 @@ export const failoverAndRelocateState = (
targetClusterErrorMessage: 0,
subscriptionGroupErrorMessage: 0,
peerStatusErrorMessage: 0,
+ syncDelayWarningMessage: 0,
},
actionErrorMessage: {},
});
diff --git a/packages/mco/components/protected-applications/list-page.tsx b/packages/mco/components/protected-applications/list-page.tsx
index a9b9a8e27..33162136f 100644
--- a/packages/mco/components/protected-applications/list-page.tsx
+++ b/packages/mco/components/protected-applications/list-page.tsx
@@ -34,6 +34,7 @@ import {
getLastAppDeploymentClusterName,
getDRPolicyName,
getReplicationType,
+ getReplicationHealth,
} from '../../utils';
import {
EmptyRowMessage,
@@ -50,7 +51,6 @@ import {
getHeaderColumns,
getColumnNames,
getRowActions,
- getReplicationHealth,
isFailingOrRelocating,
ReplicationHealthMap,
getAppWorstSyncStatus,
diff --git a/packages/mco/components/protected-applications/utils.tsx b/packages/mco/components/protected-applications/utils.tsx
index ef48d5497..de8da68b6 100644
--- a/packages/mco/components/protected-applications/utils.tsx
+++ b/packages/mco/components/protected-applications/utils.tsx
@@ -1,5 +1,4 @@
import * as React from 'react';
-import { getTimeDifferenceInSeconds } from '@odf/shared/details-page/datetime';
import { ActionDropdownItems } from '@odf/shared/dropdown/action-dropdown';
import { getName, getNamespace } from '@odf/shared/selectors';
import {
@@ -22,14 +21,12 @@ import { IAction } from '@patternfly/react-table';
import {
VOLUME_REPLICATION_HEALTH,
DRPC_STATUS,
- LEAST_SECONDS_IN_PROMETHEUS,
DR_BASE_ROUTE,
DRActionType,
REPLICATION_TYPE,
} from '../../constants';
import { DRPlacementControlModel } from '../../models';
import { DRPlacementControlKind, Progression } from '../../types';
-import { getVolumeReplicationHealth } from '../../utils';
import { DiscoveredApplicationParser as DiscoveredApplicationModal } from '../modals/app-failover-relocate/parser/discovered-application-parser';
import RemoveDisasterRecoveryModal from '../modals/remove-disaster-recovery/remove-disaster-recovery';
@@ -85,20 +82,6 @@ export const isCleanupPending = (drpc: DRPlacementControlKind): boolean =>
drpc?.status?.phase as DRPC_STATUS
) && drpc?.status?.progression === Progression.WaitOnUserToCleanUp;
-export const getReplicationHealth = (
- lastSyncTime: 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;
- return getVolumeReplicationHealth(seconds, schedulingInterval)[0];
-};
-
export type ReplicationHealthMap = {
title: string;
icon: JSX.Element;
diff --git a/packages/mco/utils/disaster-recovery.tsx b/packages/mco/utils/disaster-recovery.tsx
index 5c09e68c8..852df91a6 100644
--- a/packages/mco/utils/disaster-recovery.tsx
+++ b/packages/mco/utils/disaster-recovery.tsx
@@ -1,6 +1,7 @@
import * as React from 'react';
import {
daysToSeconds,
+ getTimeDifferenceInSeconds,
hoursToSeconds,
minutesToSeconds,
} from '@odf/shared/details-page/datetime';
@@ -36,6 +37,7 @@ import {
PLACEMENT_RULE_REF_LABEL,
MCV_NAME_TEMPLATE,
NAME_NAMESPACE_SPLIT_CHAR,
+ LEAST_SECONDS_IN_PROMETHEUS,
} from '../constants';
import {
DRPC_NAMESPACE_ANNOTATION,
@@ -246,6 +248,20 @@ export const getDRPoliciesCount = (drPolicies: DRPolicyMap) =>
export const getReplicationType = (interval: string) =>
interval !== '0m' ? REPLICATION_TYPE.ASYNC : REPLICATION_TYPE.SYNC;
+export const getReplicationHealth = (
+ lastSyncTime: 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;
+ return getVolumeReplicationHealth(seconds, schedulingInterval)[0];
+};
+
export const getPlacementKind = (subscription: ACMSubscriptionKind) =>
subscription?.spec?.placement?.placementRef?.kind;