Skip to content

Commit

Permalink
Blocking DRPolicy creation for multiple ODF cluster
Browse files Browse the repository at this point in the history
Signed-off-by: Gowtham Shanmugasundaram <[email protected]>
  • Loading branch information
GowthamShanmugam committed Nov 28, 2023
1 parent 5646fc8 commit 5c5d5a1
Show file tree
Hide file tree
Showing 11 changed files with 454 additions and 446 deletions.
4 changes: 2 additions & 2 deletions locales/en/plugin__odf-console.json
Original file line number Diff line number Diff line change
Expand Up @@ -34,16 +34,16 @@
"Cluster search": "Cluster search",
"Cluster name": "Cluster name",
"Select cluster list": "Select cluster list",
"Cannot be selected as it has multiple storage instances.": "Cannot be selected as it has multiple storage instances.",
"Checkbox to select cluster": "Checkbox to select cluster",
"Select schedule time format in minutes, hours or days": "Select schedule time format in minutes, hours or days",
"Sync schedule": "Sync schedule",
"1 or more managed clusters are offline": "1 or more managed clusters are offline",
"The status for both the managed clusters must be available for creating a DR policy. To restore a cluster to an available state, refer to the instructions in the ACM documentation.": "The status for both the managed clusters must be available for creating a DR policy. To restore a cluster to an available state, refer to the instructions in the ACM documentation.",
"Cannot proceed with one or more selected clusters": "Cannot proceed with one or more selected clusters",
"We could not retrieve any information about the managed cluster {{names}}. Check the documentation for potential causes and follow the steps mentioned and try again.": "We could not retrieve any information about the managed cluster {{names}}. Check the documentation for potential causes and follow the steps mentioned and try again.",
"{{ names }} has either an unsupported ODF version or the ODF operator is missing, install or update to ODF {{ version }} or the latest version to enable DR protection.": "{{ names }} has either an unsupported ODF version or the ODF operator is missing, install or update to ODF {{ version }} or the latest version to enable DR protection.",
"{{ names }} is not connected to RHCS": "{{ names }} is not connected to RHCS",
"Replication policy": "Replication policy",
"Sync schedule": "Sync schedule",
"Information unavailable": "Information unavailable",
"Data policies": "Data policies",
"Disaster recovery": "Disaster recovery",
Expand Down
185 changes: 80 additions & 105 deletions packages/mco/components/create-dr-policy/create-dr-policy.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import * as React from 'react';
import { getMajorVersion } from '@odf/mco/utils';
import { CEPH_STORAGE_NAMESPACE } from '@odf/shared/constants/common';
import PageHeading from '@odf/shared/heading/page-heading';
import { useFetchCsv } from '@odf/shared/hooks/use-fetch-csv';
import { K8sResourceKind } from '@odf/shared/types';
Expand Down Expand Up @@ -37,6 +36,7 @@ import {
drPolicyReducer,
drPolicyInitialState,
DRPolicyActionType,
ManagedClusterInfoType,
} from './reducer';
import { SelectClusterList } from './select-cluster-list';
import { DRReplicationType } from './select-replication-type';
Expand All @@ -54,6 +54,65 @@ const fetchMirrorPeer = (
return existingPeerNames.sort().join(',') === peerNames.sort().join(',');
});

const getPeerClustersRef = (clusters: ManagedClusterInfoType[]) =>
clusters.map((cluster) => {
const { storageClusterName, storageClusterNamesapce } =
cluster?.odfInfo.odfConfigInfo.storageClusters[0];
return {
clusterName: cluster?.name,
storageClusterRef: {
name: storageClusterName,
namespace: storageClusterNamesapce,
},
};
});

const createDRPolicy = (
policyName: string,
replicationType: REPLICATION_TYPE,
syncIntervalTime: string,
peerNames: string[]
) => {
// DRPolicy creation
const drPolicyPayload: DRPolicyKind = {
apiVersion: getAPIVersionForModel(DRPolicyModel),
kind: DRPolicyModel.kind,
metadata: { name: policyName },
spec: {
schedulingInterval:
replicationType === REPLICATION_TYPE.ASYNC ? syncIntervalTime : '0m',
drClusters: peerNames,
},
};
return k8sCreate({
model: DRPolicyModel,
data: drPolicyPayload,
cluster: HUB_CLUSTER_NAME,
});
};

const createMirrorPeer = (
selectedClusters: ManagedClusterInfoType[],
replicationType: REPLICATION_TYPE
) => {
// MirrorPeer creation
const mirrorPeerPayload: MirrorPeerKind = {
apiVersion: getAPIVersionForModel(MirrorPeerModel),
kind: MirrorPeerModel.kind,
metadata: { generateName: 'mirrorpeer-' },
spec: {
manageS3: true,
type: replicationType,
items: getPeerClustersRef(selectedClusters),
},
};
return k8sCreate({
model: MirrorPeerModel,
data: mirrorPeerPayload,
cluster: HUB_CLUSTER_NAME,
});
};

const getDRPolicyListPageLink = (url: string) =>
url.replace(`${referenceForModel(DRPolicyModel)}/~new`, '');

Expand All @@ -67,6 +126,8 @@ export const CreateDRPolicy: React.FC<ReRouteResourceProps> = ({
drPolicyReducer,
drPolicyInitialState
);
const [errorMessage, setErrorMessage] = React.useState('');

// odfmco mirrorpeer info
const [mirrorPeers] = useK8sWatchResource<MirrorPeerKind[]>({
kind: referenceForModel(MirrorPeerModel),
Expand All @@ -80,105 +141,25 @@ export const CreateDRPolicy: React.FC<ReRouteResourceProps> = ({
});
const odfMCOVersion = getMajorVersion(csv?.spec?.version);

React.useEffect(() => {
if (state.selectedClusters.length === 2) {
// ODF detection
dispatch({
type: DRPolicyActionType.SET_IS_ODF_DETECTED,
payload: state.selectedClusters.every(
(cluster) => cluster?.cephFSID !== '' && cluster?.isValidODFVersion
),
});

// DR replication type
const isReplicationAutoDetectable = state.selectedClusters.every(
(cluster) => cluster?.cephFSID !== ''
);
const cephFSIDs = state.selectedClusters.reduce((ids, cluster) => {
if (cluster?.cephFSID !== '') {
ids.add(cluster?.cephFSID);
}
return ids;
}, new Set());

dispatch({
type: DRPolicyActionType.SET_IS_REPLICATION_INPUT_MANUAL,
payload: !isReplicationAutoDetectable,
});
dispatch({
type: DRPolicyActionType.SET_REPLICATION,
payload:
isReplicationAutoDetectable && cephFSIDs.size <= 1
? REPLICATION_TYPE.SYNC
: REPLICATION_TYPE.ASYNC,
});
} else {
dispatch({
type: DRPolicyActionType.SET_IS_ODF_DETECTED,
payload: false,
});
dispatch({
type: DRPolicyActionType.SET_IS_REPLICATION_INPUT_MANUAL,
payload: false,
});
dispatch({
type: DRPolicyActionType.SET_REPLICATION,
payload: '',
});
}
}, [state.selectedClusters, t, dispatch]);

// On DRPolicy creation
const onCreate = () => {
const promises: Promise<K8sResourceKind>[] = [];
const peerNames = state.selectedClusters.map((cluster) => cluster?.name);

// DRPolicy creation
const drPolicyPayload: DRPolicyKind = {
apiVersion: getAPIVersionForModel(DRPolicyModel),
kind: DRPolicyModel.kind,
metadata: { name: state.policyName },
spec: {
schedulingInterval:
state.replication === REPLICATION_TYPE.ASYNC ? state.syncTime : '0m',
drClusters: peerNames,
},
};
promises.push(
k8sCreate({
model: DRPolicyModel,
data: drPolicyPayload,
cluster: HUB_CLUSTER_NAME,
})
createDRPolicy(
state.policyName,
state.replicationType,
state.syncIntervalTime,
peerNames
)
);

const mirrorPeer: MirrorPeerKind =
fetchMirrorPeer(mirrorPeers, peerNames) ?? {};

if (Object.keys(mirrorPeer).length === 0) {
// MirrorPeer creation
const mirrorPeerPayload: MirrorPeerKind = {
apiVersion: getAPIVersionForModel(MirrorPeerModel),
kind: MirrorPeerModel.kind,
metadata: { generateName: 'mirrorpeer-' },
spec: {
manageS3: true,
type: state.replication,
items: state.selectedClusters.map((cluster) => ({
clusterName: cluster?.name,
storageClusterRef: {
name: cluster.storageClusterName,
// ToDo (epic 4422): Need to update this as per ConfigMap/ClusterClaim (whichever us decided) JSON output
namespace: CEPH_STORAGE_NAMESPACE,
},
})),
},
};
promises.push(
k8sCreate({
model: MirrorPeerModel,
data: mirrorPeerPayload,
cluster: HUB_CLUSTER_NAME,
})
createMirrorPeer(state.selectedClusters, state.replicationType)
);
}

Expand All @@ -187,10 +168,7 @@ export const CreateDRPolicy: React.FC<ReRouteResourceProps> = ({
history.push(getDRPolicyListPageLink(url));
})
.catch((error) => {
dispatch({
type: DRPolicyActionType.SET_ERROR_MESSAGE,
payload: error?.message,
});
setErrorMessage(error?.message);
});
};

Expand All @@ -202,8 +180,8 @@ export const CreateDRPolicy: React.FC<ReRouteResourceProps> = ({

const areDRPolicyInputsValid = () =>
!!state.policyName &&
Object.keys(state.selectedClusters)?.length === MAX_ALLOWED_CLUSTERS &&
state.isODFDetected;
!!state.replicationType &&
Object.keys(state.selectedClusters)?.length === MAX_ALLOWED_CLUSTERS;

return (
<div>
Expand Down Expand Up @@ -236,7 +214,7 @@ export const CreateDRPolicy: React.FC<ReRouteResourceProps> = ({
isHelperTextBeforeField
>
<SelectClusterList
state={state}
selectedClusters={state.selectedClusters}
requiredODFVersion={odfMCOVersion}
dispatch={dispatch}
/>
Expand All @@ -262,29 +240,26 @@ export const CreateDRPolicy: React.FC<ReRouteResourceProps> = ({
{!!state.selectedClusters.length && (
<FormGroup fieldId="selected-clusters" label={t('Selected clusters')}>
{state.selectedClusters.map((c, i) => (
<SelectedCluster
key={c.name}
id={i + 1}
cluster={c}
dispatch={dispatch}
/>
<SelectedCluster key={c.name} id={i + 1} cluster={c} />
))}
</FormGroup>
)}
<DRReplicationType
state={state}
selectedClusters={state.selectedClusters}
replicationType={state.replicationType}
syncIntervalTime={state.syncIntervalTime}
requiredODFVersion={odfMCOVersion}
dispatch={dispatch}
/>
{state.errorMessage && (
{errorMessage && (
<FormGroup fieldId="error-message">
<Alert
className="odf-alert mco-create-data-policy__alert"
title={t('An error occurred')}
variant="danger"
isInline
>
{state.errorMessage}
{errorMessage}
</Alert>
</FormGroup>
)}
Expand Down
Loading

0 comments on commit 5c5d5a1

Please sign in to comment.