Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 0 additions & 3 deletions locales/en/plugin__odf-console.json
Original file line number Diff line number Diff line change
Expand Up @@ -268,7 +268,6 @@
"All associated LocalDisks for this FileSystem": "All associated LocalDisks for this FileSystem",
"All characters are allowed except for the forward slash (\"/\").": "All characters are allowed except for the forward slash (\"/\").",
"All checks": "All checks",
"All disaster recovery prerequisites are met for both clusters. Multiple storage backends are available on both of the selected clusters.": "All disaster recovery prerequisites are met for both clusters. Multiple storage backends are available on both of the selected clusters.",
"All disaster recovery prerequisites met for both clusters.": "All disaster recovery prerequisites met for both clusters.",
"All disk type may include HDD disks. Data Foundation does not support HDD disks as local devices. Select SSD if you plan to use Data Foundation.": "All disk type may include HDD disks. Data Foundation does not support HDD disks as local devices. Select SSD if you plan to use Data Foundation.",
"All headers": "All headers",
Expand Down Expand Up @@ -1526,7 +1525,6 @@
"No capacity data available.": "No capacity data available.",
"No chart data available": "No chart data available",
"No clusters found": "No clusters found",
"No clusters selected": "No clusters selected",
"No common storage class found for the selected managed clusters. To create a DR policy, a common storage class must exist, if not configured already, provision a common storage class and try again.": "No common storage class found for the selected managed clusters. To create a DR policy, a common storage class must exist, if not configured already, provision a common storage class and try again.",
"no compression": "no compression",
"No conditions found": "No conditions found",
Expand Down Expand Up @@ -2046,7 +2044,6 @@
"Selected BackingStores": "Selected BackingStores",
"Selected capacity": "Selected capacity",
"Selected clusters cannot be used to create a DRPolicy.": "Selected clusters cannot be used to create a DRPolicy.",
"Selected Clusters: {{clusters}}": "Selected Clusters: {{clusters}}",
"Selected Disks": "Selected Disks",
"Selected nodes": "Selected nodes",
"Selected nodes are based on the StorageClass <1>{{scName}}</1> and fulfill the stretch cluster requirements with a recommended requirement of 14 CPU and 34 GiB RAM per node.": "Selected nodes are based on the StorageClass <1>{{scName}}</1> and fulfill the stretch cluster requirements with a recommended requirement of 14 CPU and 34 GiB RAM per node.",
Expand Down
86 changes: 28 additions & 58 deletions packages/mco/components/create-dr-policy/CreateDRPolicyForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -43,64 +43,27 @@ import {
DRPolicyState,
} from './utils/reducer';
import {
isValidBucketName,
isValidEndpoint,
isValidS3ProfileName,
} from './utils/s3-validators';
validateClustersStepInputs,
validateConfigureStepInputs,
} from './utils/step-validation';

const isFilled = (v: string) => !!v && v.trim().length > 0;

const areS3DetailsFormatValid = (d: S3Details): boolean =>
isValidBucketName(d.bucketName) &&
isValidEndpoint(d.endpoint) &&
isFilled(d.accessKeyId) &&
isFilled(d.secretKey) &&
isFilled(d.region) &&
isFilled(d.s3ProfileName) &&
isValidS3ProfileName(d.s3ProfileName);

const validateClusterInputs = (
state: DRPolicyState,
allDRClustersExist = false
): boolean => {
const {
selectedClusters,
isClusterSelectionValid,
replicationBackend,
cluster1S3Details,
cluster2S3Details,
useSameS3Connection,
} = state;

const baseValid =
isClusterSelectionValid && selectedClusters.length === MAX_ALLOWED_CLUSTERS;

if (!baseValid) return false;

if (replicationBackend === BackendType.ThirdParty) {
if (allDRClustersExist) return true;
const c2ProfileValid =
isFilled(cluster2S3Details.s3ProfileName) &&
isValidS3ProfileName(cluster2S3Details.s3ProfileName);
return (
areS3DetailsFormatValid(cluster1S3Details) &&
c2ProfileValid &&
(useSameS3Connection || areS3DetailsFormatValid(cluster2S3Details))
);
}

return true;
};

const validatePolicyInputs = (state: DRPolicyState): boolean =>
isFilled(state.policyName) && !!state.replicationType;

const validateDRPolicyInputs = (
state: DRPolicyState,
allDRClustersExist = false
allDRClustersExist = false,
prePairValidationPassed = true
): boolean =>
validatePolicyInputs(state) &&
validateClusterInputs(state, allDRClustersExist);
validateClustersStepInputs(state) &&
validateConfigureStepInputs(
state,
allDRClustersExist,
prePairValidationPassed
);

const convertS3ProfileToDetails = (
profile: S3StoreProfile,
Expand Down Expand Up @@ -288,15 +251,18 @@ export const CreateDRPolicyForm: React.FC<CreateDRPolicyFormProps> = ({

const stepNames = CreateDRPolicyStepNames(t);
const stepValidity: Record<CreateDRPolicyWizardSteps, boolean> = {
[CreateDRPolicyWizardSteps.Clusters]: validateClusterInputs(
[CreateDRPolicyWizardSteps.Clusters]: validateClustersStepInputs(state),
[CreateDRPolicyWizardSteps.Configure]: validateConfigureStepInputs(
state,
allDRClustersExist
allDRClustersExist,
prePairValidationPassed
),
[CreateDRPolicyWizardSteps.Configure]: prePairValidation.canProceed,
[CreateDRPolicyWizardSteps.Policy]: validatePolicyInputs(state),
[CreateDRPolicyWizardSteps.Review]:
validateDRPolicyInputs(state, allDRClustersExist) &&
prePairValidationPassed,
[CreateDRPolicyWizardSteps.Review]: validateDRPolicyInputs(
state,
allDRClustersExist,
prePairValidationPassed
),
};

return (
Expand Down Expand Up @@ -328,20 +294,24 @@ export const CreateDRPolicyForm: React.FC<CreateDRPolicyFormProps> = ({
preSelectedClusters={preSelectedClusters}
acmDoc={acmDoc}
mirrorPeers={mirrorPeers}
clusterNames={clusterNames}
selectedDRClusters={selectedDRClusters}
errorMessage={s3ErrorMessage}
/>
</WizardStep>
<WizardStep
id={CreateDRPolicyWizardSteps.Configure}
name={stepNames[CreateDRPolicyWizardSteps.Configure]}
isHidden={state.replicationBackend !== BackendType.DataFoundation}
>
<ConfigureClusterPairStep
replicationBackend={state.replicationBackend}
selectedClusters={state.selectedClusters}
cluster1S3Details={state.cluster1S3Details}
cluster2S3Details={state.cluster2S3Details}
useSameS3Connection={state.useSameS3Connection}
dispatch={dispatch}
clusterNames={clusterNames}
selectedDRClusters={selectedDRClusters}
validation={prePairValidation}
docHref={submarinerDoc}
errorMessage={s3ErrorMessage}
/>
</WizardStep>
<WizardStep
Expand Down
108 changes: 11 additions & 97 deletions packages/mco/components/create-dr-policy/clusters-step.tsx
Original file line number Diff line number Diff line change
@@ -1,25 +1,18 @@
import * as React from 'react';
import { DOC_VERSION, tpsDoc } from '@odf/shared';
import { getName } from '@odf/shared/selectors';
import { useCustomTranslation } from '@odf/shared/useCustomTranslationHook';
import { ExternalLink } from '@odf/shared/utils';
import { Trans } from 'react-i18next';
import {
Alert,
AlertVariant,
Form,
FormGroup,
FormHelperText,
HelperText,
HelperTextItem,
} from '@patternfly/react-core';
import { BackendType, MAX_ALLOWED_CLUSTERS } from '../../constants';
import { DRClusterKind, MirrorPeerKind } from '../../types';
import { ClusterS3BucketDetailsForm } from './add-s3-bucket-details/s3-bucket-details-form';
import { MAX_ALLOWED_CLUSTERS } from '../../constants';
import { MirrorPeerKind } from '../../types';
import { SelectClusterList } from './select-cluster-list';
import { SelectReplicationBackend } from './select-replication-backend/select-replication-backend';
import { SelectedClusterValidation } from './selected-cluster-validator';
import ThirdPartyStorageWarning from './third-party-storage-alert';
import { DRPolicyAction, DRPolicyState } from './utils/reducer';

export const ClustersStep: React.FC<ClustersStepProps> = ({
Expand All @@ -29,9 +22,6 @@ export const ClustersStep: React.FC<ClustersStepProps> = ({
preSelectedClusters,
acmDoc,
mirrorPeers,
clusterNames,
selectedDRClusters,
errorMessage,
}) => {
const { t } = useCustomTranslation();

Expand All @@ -55,7 +45,7 @@ export const ClustersStep: React.FC<ClustersStepProps> = ({
showOnlyPreselected={preSelectedClusters.length > 0}
/>
</FormGroup>
<FormGroup>
<FormGroup fieldId="cluster-note">
<FormHelperText>
<HelperText>
<HelperTextItem>
Expand All @@ -70,87 +60,14 @@ export const ClustersStep: React.FC<ClustersStepProps> = ({
</FormHelperText>
</FormGroup>
{state.selectedClusters.length === MAX_ALLOWED_CLUSTERS && (
<>
<FormGroup fieldId="cluster-selection-validation">
<SelectedClusterValidation
selectedClusters={state.selectedClusters}
requiredODFVersion={requiredODFVersion}
dispatch={dispatch}
mirrorPeers={mirrorPeers}
/>
</FormGroup>
{state.isClusterSelectionValid && (
<>
{!state.selectedClustersHaveODF &&
state.selectedClusters.some(
(cluster) => cluster?.odfInfo?.storageClusterCount > 0
) && (
<FormGroup
fieldId="select-backend"
label={t('Select replication')}
>
<FormHelperText>
<HelperText className="mco-create-data-policy__text-input">
<HelperTextItem>
{t(
'All disaster recovery prerequisites are met for both clusters. Multiple storage backends are available on both of the selected clusters.'
)}
</HelperTextItem>
</HelperText>
</FormHelperText>
<SelectReplicationBackend
clusterNames={clusterNames}
doClustersHaveODF={state.selectedClustersHaveODF}
dispatch={dispatch}
selectedKey={state.replicationBackend}
/>
</FormGroup>
)}
{!state.selectedClustersHaveODF &&
state.replicationBackend === BackendType.ThirdParty && (
<>
<ThirdPartyStorageWarning docHref={tpsDoc(DOC_VERSION)} />
<FormGroup
fieldId="add-s3-bucket-details"
label={t('Replication site')}
>
<FormHelperText>
<HelperText className="mco-create-data-policy__text-input">
<HelperTextItem>
{t(
'Provide S3 bucket connection details for each managed cluster. If a S3 bucket is not already configured for cluster, create one and then continue.'
)}
</HelperTextItem>
</HelperText>
</FormHelperText>
<ClusterS3BucketDetailsForm
selectedClusters={state.selectedClusters}
cluster1Details={state.cluster1S3Details}
cluster2Details={state.cluster2S3Details}
useSameConnection={state.useSameS3Connection}
existingDRClusterNames={
new Set(selectedDRClusters.map(getName))
}
dispatch={dispatch}
/>
</FormGroup>
</>
)}
{!!errorMessage && (
<FormGroup fieldId="error-message">
<Alert
className="odf-alert mco-create-data-policy__alert"
title={t('An error occurred')}
variant={AlertVariant.danger}
isInline
>
{errorMessage}
</Alert>
</FormGroup>
)}
</>
)}
</>
<FormGroup fieldId="cluster-selection-validation">
<SelectedClusterValidation
selectedClusters={state.selectedClusters}
requiredODFVersion={requiredODFVersion}
dispatch={dispatch}
mirrorPeers={mirrorPeers}
/>
</FormGroup>
)}
</Form>
);
Expand All @@ -163,7 +80,4 @@ type ClustersStepProps = {
preSelectedClusters: string[];
acmDoc: string;
mirrorPeers: MirrorPeerKind[];
clusterNames: string[];
selectedDRClusters: DRClusterKind[];
errorMessage: string;
};
Loading