From c8f0350fe2db1479f00308f2be7606ec1e26ca7e Mon Sep 17 00:00:00 2001 From: Gowtham Shanmugasundaram Date: Mon, 9 Dec 2024 21:01:22 +0530 Subject: [PATCH] UI support for consistency group - techpreview Signed-off-by: Gowtham Shanmugasundaram --- locales/en/plugin__odf-console.json | 3 + .../utils/reducer.ts | 16 ++ .../configuration-step/configuration-step.tsx | 19 +- .../assign-policy-view.tsx | 3 + .../helper/pvc-details-wizard-content.tsx | 236 +++++++++++------- .../app-manage-policies/utils/reducer.ts | 21 ++ 6 files changed, 203 insertions(+), 95 deletions(-) diff --git a/locales/en/plugin__odf-console.json b/locales/en/plugin__odf-console.json index 9a91eb3aa..66f827def 100644 --- a/locales/en/plugin__odf-console.json +++ b/locales/en/plugin__odf-console.json @@ -354,6 +354,9 @@ "{{count}} selected_one": "{{count}} selected", "{{count}} selected_other": "{{count}} selected", "Select labels": "Select labels", + "Volume consistency group": "Volume consistency group", + "Enable disaster recovery for volume consistency groups.": "Enable disaster recovery for volume consistency groups.", + "Protect applications deployed across multiple volumes and ensure consistent recovery with volume consistency groups.": "Protect applications deployed across multiple volumes and ensure consistent recovery with volume consistency groups.", "Use PVC label selectors to effortlessly specify the application resources that need protection. You can also create a custom PVC label selector if one doesn’t exists. For more information, ": "Use PVC label selectors to effortlessly specify the application resources that need protection. You can also create a custom PVC label selector if one doesn’t exists. For more information, ", "Help": "Help", "see PVC label selector requirements.": "see PVC label selector requirements.", diff --git a/packages/mco/components/discovered-application-wizard/utils/reducer.ts b/packages/mco/components/discovered-application-wizard/utils/reducer.ts index 3c8f7d7ad..c11719037 100644 --- a/packages/mco/components/discovered-application-wizard/utils/reducer.ts +++ b/packages/mco/components/discovered-application-wizard/utils/reducer.ts @@ -21,6 +21,7 @@ export enum EnrollDiscoveredApplicationStateType { SET_POLICY = 'REPLICATION/SET_POLICY', SET_K8S_RESOURCE_REPLICATION_INTERVAL = 'REPLICATION/SET_K8S_RESOURCE_REPLICATION_INTERVAL', SET_NAME = 'NAMESPACE/SET_NAME', + ENABLE_CONSISTENCY_GROUP = 'CONFIGURATION/ENABLE_CONSISTENCY_GROUP', } export type EnrollDiscoveredApplicationState = { @@ -45,6 +46,7 @@ export type EnrollDiscoveredApplicationState = { k8sResourceLabelExpressions: MatchExpression[]; pvcLabelExpressions: MatchExpression[]; }; + isConsistencyGroupEnabled: boolean; }; replication: { drPolicy: DRPolicyKind; @@ -75,6 +77,7 @@ export const initialState: EnrollDiscoveredApplicationState = { k8sResourceLabelExpressions: [], pvcLabelExpressions: [], }, + isConsistencyGroupEnabled: false, }, replication: { drPolicy: {}, @@ -119,6 +122,10 @@ export type EnrollDiscoveredApplicationAction = | { type: EnrollDiscoveredApplicationStateType.SET_NAME; payload: string; + } + | { + type: EnrollDiscoveredApplicationStateType.ENABLE_CONSISTENCY_GROUP; + payload: boolean; }; export const reducer: EnrollReducer = (state, action) => { @@ -225,6 +232,15 @@ export const reducer: EnrollReducer = (state, action) => { }, }; } + case EnrollDiscoveredApplicationStateType.ENABLE_CONSISTENCY_GROUP: { + return { + ...state, + configuration: { + ...state.configuration, + isConsistencyGroupEnabled: action.payload, + }, + }; + } default: throw new TypeError(`${action} is not a valid reducer action`); } diff --git a/packages/mco/components/discovered-application-wizard/wizard-steps/configuration-step/configuration-step.tsx b/packages/mco/components/discovered-application-wizard/wizard-steps/configuration-step/configuration-step.tsx index 2d74bb249..bc131ed3d 100644 --- a/packages/mco/components/discovered-application-wizard/wizard-steps/configuration-step/configuration-step.tsx +++ b/packages/mco/components/discovered-application-wizard/wizard-steps/configuration-step/configuration-step.tsx @@ -1,4 +1,5 @@ import * as React from 'react'; +import { ConsistencyGroupCheckBox } from '@odf/mco/components/modals/app-manage-policies/helper/pvc-details-wizard-content'; import { useCustomTranslation } from '@odf/shared/useCustomTranslationHook'; import { Alert, @@ -32,7 +33,12 @@ export const Configuration: React.FC = ({ const { t } = useCustomTranslation(); const { namespaces, clusterName } = state.namespace; - const { protectionMethod, recipe, resourceLabels } = state.configuration; + const { + protectionMethod, + recipe, + resourceLabels, + isConsistencyGroupEnabled, + } = state.configuration; const setProtectionMethod = (_unUsed, event) => { dispatch({ @@ -41,6 +47,13 @@ export const Configuration: React.FC = ({ }); }; + const consistencyGroupOnChange = (checked: boolean) => { + dispatch({ + type: EnrollDiscoveredApplicationStateType.ENABLE_CONSISTENCY_GROUP, + payload: checked, + }); + }; + return (
@@ -121,6 +134,10 @@ export const Configuration: React.FC = ({ dispatch={dispatch} /> )} +
); diff --git a/packages/mco/components/modals/app-manage-policies/assign-policy-view.tsx b/packages/mco/components/modals/app-manage-policies/assign-policy-view.tsx index 48733b049..868da556a 100644 --- a/packages/mco/components/modals/app-manage-policies/assign-policy-view.tsx +++ b/packages/mco/components/modals/app-manage-policies/assign-policy-view.tsx @@ -65,6 +65,9 @@ export const createSteps = ( isValidationEnabled={isValidationEnabled} dispatch={dispatch} protectedPVCSelectors={protectedPVCSelectors} + isConsistencyGroupEnabled={ + state.persistentVolumeClaim.isConsistencyGroupEnabled + } /> ), }, diff --git a/packages/mco/components/modals/app-manage-policies/helper/pvc-details-wizard-content.tsx b/packages/mco/components/modals/app-manage-policies/helper/pvc-details-wizard-content.tsx index 0afd4ca9a..af5d719ba 100644 --- a/packages/mco/components/modals/app-manage-policies/helper/pvc-details-wizard-content.tsx +++ b/packages/mco/components/modals/app-manage-policies/helper/pvc-details-wizard-content.tsx @@ -7,6 +7,7 @@ import { } from '@odf/mco/constants'; import { useACMSafeFetch } from '@odf/mco/hooks/acm-safe-fetch'; import { SearchResult } from '@odf/mco/types'; +import { TechPreviewBadge } from '@odf/shared'; import { MultiSelectDropdown } from '@odf/shared/dropdown/multiselectdropdown'; import { SingleSelectDropdown } from '@odf/shared/dropdown/singleselectdropdown'; import { StatusBox } from '@odf/shared/generic/status-box'; @@ -32,6 +33,7 @@ import { GridItem, Popover, ButtonVariant, + Checkbox, } from '@patternfly/react-core'; import { MinusCircleIcon } from '@patternfly/react-icons'; import { queryAppWorkloadPVCs } from '../../../../utils/acm-search-queries'; @@ -245,108 +247,145 @@ const PairElement: React.FC = ({ ); }; -export const PVCDetailsWizardContent: React.FC = - ({ - pvcSelectors, - unProtectedPlacements, - workloadNamespace, - isValidationEnabled, - protectedPVCSelectors, - dispatch, - }) => { +export const ConsistencyGroupCheckBox: React.FC = + ({ isConsistencyGroupEnabled, onChange }) => { const { t } = useCustomTranslation(); - // To update placement and label info - const selectedPVCSelectors = React.useMemo( - () => _.cloneDeep(pvcSelectors) || [], - [pvcSelectors] + return ( + } + > + onChange(checked)} + id="cg-heckbox" + name="cg-heckbox" + /> + ); + }; - // Selected placement and labels - const [tags, setTags] = React.useState( - getPlacementTags([...protectedPVCSelectors, ...selectedPVCSelectors]) - ); +export const PVCDetailsWizardContent: React.FC = ( + props +) => { + const { isConsistencyGroupEnabled, dispatch } = props; + const consistencyGroupOnChange = (checked: boolean) => { + dispatch({ + type: ManagePolicyStateType.ENABLE_CONSISTENCY_GROUP, + context: ModalViewContext.ASSIGN_POLICY_VIEW, + payload: checked, + }); + }; + return ( +
+ + + + ); +}; - // ACM search proxy api call - const searchQuery = React.useMemo( - () => - queryAppWorkloadPVCs( - workloadNamespace, - getClusterNamesFromPlacements(unProtectedPlacements) - ), - [unProtectedPlacements, workloadNamespace] - ); - const [searchResult, error, loaded] = useACMSafeFetch(searchQuery); +export const PVCLabelSelector: React.FC = ({ + pvcSelectors, + unProtectedPlacements, + workloadNamespace, + isValidationEnabled, + protectedPVCSelectors, + dispatch, +}) => { + const { t } = useCustomTranslation(); - // All labels - const labels: string[] = React.useMemo( - () => getLabelsFromSearchResult(searchResult), - [searchResult] - ); + // To update placement and label info + const selectedPVCSelectors = React.useMemo( + () => _.cloneDeep(pvcSelectors) || [], + [pvcSelectors] + ); - // All unprotected placements - const unProtectedPlacementNames: string[] = - unProtectedPlacements.map(getName); + // Selected placement and labels + const [tags, setTags] = React.useState( + getPlacementTags([...protectedPVCSelectors, ...selectedPVCSelectors]) + ); - // All protected placements - const protectedPlacementNames: string[] = protectedPVCSelectors.map( - (pvcSelector) => pvcSelector.placementName - ); + // ACM search proxy api call + const searchQuery = React.useMemo( + () => + queryAppWorkloadPVCs( + workloadNamespace, + getClusterNamesFromPlacements(unProtectedPlacements) + ), + [unProtectedPlacements, workloadNamespace] + ); + const [searchResult, error, loaded] = useACMSafeFetch(searchQuery); - return ( -
- - - {t( - 'Use PVC label selectors to effortlessly specify the application resources that need protection. You can also create a custom PVC label selector if one doesn’t exists. For more information, ' - )} - - - - - - {loaded && !error ? ( - { - setTags(nameValuePairs); - dispatch({ - type: ManagePolicyStateType.SET_PVC_SELECTORS, - context: ModalViewContext.ASSIGN_POLICY_VIEW, - payload: getPVCSelectors( - nameValuePairs, - protectedPlacementNames - ), - }); - }} - PairElementComponent={PairElement} - nameString={t('Application resource')} - valueString={t('PVC label selector')} - addString={t('Add application resource')} - extraProps={{ - unProtectedPlacementNames, - labels, - tags, - isValidationEnabled, - protectedPlacementNames, - }} - className="co-required mco-manage-policies__nameValue--weight" - /> - ) : ( - - )} - - ); - }; + // All labels + const labels: string[] = React.useMemo( + () => getLabelsFromSearchResult(searchResult), + [searchResult] + ); + + // All unprotected placements + const unProtectedPlacementNames: string[] = + unProtectedPlacements.map(getName); + + // All protected placements + const protectedPlacementNames: string[] = protectedPVCSelectors.map( + (pvcSelector) => pvcSelector.placementName + ); + + return ( + <> + + + {t( + 'Use PVC label selectors to effortlessly specify the application resources that need protection. You can also create a custom PVC label selector if one doesn’t exists. For more information, ' + )} + + + + + + {loaded && !error ? ( + { + setTags(nameValuePairs); + dispatch({ + type: ManagePolicyStateType.SET_PVC_SELECTORS, + context: ModalViewContext.ASSIGN_POLICY_VIEW, + payload: getPVCSelectors(nameValuePairs, protectedPlacementNames), + }); + }} + PairElementComponent={PairElement} + nameString={t('Application resource')} + valueString={t('PVC label selector')} + addString={t('Add application resource')} + extraProps={{ + unProtectedPlacementNames, + labels, + tags, + isValidationEnabled, + protectedPlacementNames, + }} + className="co-required mco-manage-policies__nameValue--weight" + /> + ) : ( + + )} + + ); +}; type TagsType = (string | string[] | number)[][]; @@ -358,11 +397,20 @@ type ExtraProps = { protectedPlacementNames: string[]; }; -type PVCDetailsWizardContentProps = { +type PVCLabelSelectorProps = { pvcSelectors: PVCSelectorType[]; unProtectedPlacements: PlacementType[]; workloadNamespace: string; isValidationEnabled: boolean; dispatch: React.Dispatch; protectedPVCSelectors: PVCSelectorType[]; + isConsistencyGroupEnabled: boolean; }; + +type ConsistencyGroupCheckBoxProps = { + isConsistencyGroupEnabled: boolean; + onChange: (checked: boolean) => void; +}; + +type PVCDetailsWizardContentProps = PVCLabelSelectorProps & + Omit; diff --git a/packages/mco/components/modals/app-manage-policies/utils/reducer.ts b/packages/mco/components/modals/app-manage-policies/utils/reducer.ts index a43c0c014..0e870cd38 100644 --- a/packages/mco/components/modals/app-manage-policies/utils/reducer.ts +++ b/packages/mco/components/modals/app-manage-policies/utils/reducer.ts @@ -20,6 +20,7 @@ export enum ManagePolicyStateType { SET_SELECTED_POLICY = 'SET_SELECTED_POLICY', SET_PVC_SELECTORS = 'SET_PVC_SELECTORS', RESET_ASSIGN_POLICY_STATE = 'RESET_ASSIGN_POLICY_STATE', + ENABLE_CONSISTENCY_GROUP = 'ENABLE_CONSISTENCY_GROUP', } export type PVCSelectorType = { @@ -30,6 +31,8 @@ export type AssignPolicyViewState = { policy: DRPolicyType; persistentVolumeClaim: { pvcSelectors: PVCSelectorType[]; + // Consistency group support for CephFS & RBD volumes + isConsistencyGroupEnabled: boolean; }; }; @@ -45,6 +48,7 @@ export const initialPolicyState: ManagePolicyState = { policy: undefined, persistentVolumeClaim: { pvcSelectors: [], + isConsistencyGroupEnabled: false, }, }, }; @@ -71,6 +75,11 @@ export type ManagePolicyStateAction = | { type: ManagePolicyStateType.RESET_ASSIGN_POLICY_STATE; context: ModalViewContext; + } + | { + type: ManagePolicyStateType.ENABLE_CONSISTENCY_GROUP; + context: ModalViewContext; + payload: boolean; }; export const managePolicyStateReducer = ( @@ -120,6 +129,18 @@ export const managePolicyStateReducer = ( }, }; } + case ManagePolicyStateType.ENABLE_CONSISTENCY_GROUP: { + return { + ...state, + [action.context]: { + ...state[action.context], + persistentVolumeClaim: { + ...state[action.context]['persistentVolumeClaim'], + isConsistencyGroupEnabled: action.payload, + }, + }, + }; + } default: return state; }