diff --git a/locales/en/plugin__odf-console.json b/locales/en/plugin__odf-console.json index 43bb1a592..f0be2ccc6 100644 --- a/locales/en/plugin__odf-console.json +++ b/locales/en/plugin__odf-console.json @@ -853,7 +853,6 @@ "Files should be named private and public followed by compatible extensions": "Files should be named private and public followed by compatible extensions", "Deploys MultiCloud Object Gateway without block and file services.": "Deploys MultiCloud Object Gateway without block and file services.", "Deploys Data Foundation with block, shared fileSystem and object services.": "Deploys Data Foundation with block, shared fileSystem and object services.", - "Deploys Data Foundation as a provider cluster": "Deploys Data Foundation as a provider cluster", "Deployment type": "Deployment type", "Use Ceph RBD as the default StorageClass": "Use Ceph RBD as the default StorageClass", "Configure default RBD StorageClass to avoid adding manual annotations within a StorageClass and selecting a specific StorageClass when making storage requests or provisions in your PVCs.": "Configure default RBD StorageClass to avoid adding manual annotations within a StorageClass and selecting a specific StorageClass when making storage requests or provisions in your PVCs.", @@ -964,13 +963,10 @@ "Encryption: {{encryptionStatus}}": "Encryption: {{encryptionStatus}}", "In-transit encryption: {{hasInTransitEncryption}}": "In-transit encryption: {{hasInTransitEncryption}}", "Network: {{networkType}}": "Network: {{networkType}}", - "Public Network Interface": "Public Network Interface", - "Select NetworkAttachmentDefinition": "Select NetworkAttachmentDefinition", - "Cluster Network Interface": "Cluster Network Interface", "Network": "Network", - "Default (OVN)": "Default (OVN)", + "Default (Pod)": "Default (Pod)", "The default OVN uses a single network for all data operations such as read/write and also for control planes, such as data replication.": "The default OVN uses a single network for all data operations such as read/write and also for control planes, such as data replication.", - "Custom (Multus)": "Custom (Multus)", + "Host": "Host", "Multus allows a network seperation between the data operations and the control plane operations.": "Multus allows a network seperation between the data operations and the control plane operations.", "Encryption level": "Encryption level", "The StorageCluster encryption level can be set to include all components under the cluster (including StorageClass and PVs) or to include only StorageClass encryption. PV encryption can use an auth token that will be used with the KMS configuration to allow multi-tenancy.": "The StorageCluster encryption level can be set to include all components under the cluster (including StorageClass and PVs) or to include only StorageClass encryption. PV encryption can use an auth token that will be used with the KMS configuration to allow multi-tenancy.", @@ -984,6 +980,17 @@ "Encrypts all Ceph traffic including data, using Ceph msgrv2": "Encrypts all Ceph traffic including data, using Ceph msgrv2", "Verify your RHCS cluster has the necessary in-transit encryption settings configured to enable in-transit encryption on your external cluster. Refer to the documentation for detailed configuration steps.": "Verify your RHCS cluster has the necessary in-transit encryption settings configured to enable in-transit encryption on your external cluster. Refer to the documentation for detailed configuration steps.", "Documentation link": "Documentation link", + "Isolate network using Multus": "Isolate network using Multus", + "Public Network Interface": "Public Network Interface", + "Select NetworkAttachmentDefinition": "Select NetworkAttachmentDefinition", + "Cluster Network Interface": "Cluster Network Interface", + "Isolate network using NIC Operators": "Isolate network using NIC Operators", + "Specify the public and network interfaces that Ceph will use for data traffic. Use CIDR notation to define the IP addresses which will bind to on the host.": "Specify the public and network interfaces that Ceph will use for data traffic. Use CIDR notation to define the IP addresses which will bind to on the host.", + "Nodes must be annotated with network.rook.io/mon-ip: to set the correct IP address for the mon before proceeding with the host networking configuration. This ensures that the mons operate on the desired network": "Nodes must be annotated with network.rook.io/mon-ip: to set the correct IP address for the mon before proceeding with the host networking configuration. This ensures that the mons operate on the desired network", + "Ceph Cluster CIDR": "Ceph Cluster CIDR", + "Enter a CIDR block (Eg: 192.168.100.0/24)": "Enter a CIDR block (Eg: 192.168.100.0/24)", + "Ceph Public CIDR": "Ceph Public CIDR", + "Enter a CIDR block (Eg: 192.168.0.0/32)": "Enter a CIDR block (Eg: 192.168.0.0/32)", "An error has occurred: {{error}}": "An error has occurred: {{error}}", "The uploaded file is not a valid JSON file": "The uploaded file is not a valid JSON file", "External storage system metadata": "External storage system metadata", diff --git a/packages/odf/components/create-storage-system/create-steps.tsx b/packages/odf/components/create-storage-system/create-steps.tsx index 4d98b71e3..b879033fe 100644 --- a/packages/odf/components/create-storage-system/create-steps.tsx +++ b/packages/odf/components/create-storage-system/create-steps.tsx @@ -39,7 +39,6 @@ export const createSteps = ( const { encryption, kms } = securityAndNetwork; const isMCG = deployment === DeploymentType.MCG; - const isProviderMode = deployment === DeploymentType.PROVIDER_MODE; const commonSteps = { capacityAndNodes: { @@ -52,7 +51,6 @@ export const createSteps = ( volumeSetName={createLocalVolumeSet.volumeSetName} nodes={nodes} systemNamespace={systemNamespace} - deploymentMode={backingStorage.deployment} /> ), }, @@ -76,7 +74,6 @@ export const createSteps = ( kms={kms} dispatch={dispatch} isMCG={isMCG} - isProviderMode={isProviderMode} systemNamespace={systemNamespace} /> ), @@ -167,24 +164,6 @@ export const createSteps = ( ...commonSteps.reviewAndCreate, }, ]; - } else if (isProviderMode) { - return [ - { - id: 2, - canJumpTo: stepIdReached >= 2, - ...commonSteps.capacityAndNodes, - }, - { - id: 3, - canJumpTo: stepIdReached >= 3, - ...commonSteps.security, - }, - { - id: 4, - canJumpTo: stepIdReached >= 4, - ...commonSteps.reviewAndCreate, - }, - ]; } else return [ { @@ -218,25 +197,6 @@ export const createSteps = ( ...commonSteps.reviewAndCreate, }, ]; - } else if (isProviderMode) { - return [ - createLocalVolumeSetStep, - { - canJumpTo: stepIdReached >= 3, - ...commonSteps.capacityAndNodes, - id: 3, - }, - { - id: 4, - canJumpTo: stepIdReached >= 4, - ...commonSteps.security, - }, - { - id: 5, - canJumpTo: stepIdReached >= 5, - ...commonSteps.reviewAndCreate, - }, - ]; } return [ createLocalVolumeSetStep, diff --git a/packages/odf/components/create-storage-system/create-storage-system-steps/backing-storage-step/backing-storage-step.tsx b/packages/odf/components/create-storage-system/create-storage-system-steps/backing-storage-step/backing-storage-step.tsx index e1eb99144..5846f2293 100644 --- a/packages/odf/components/create-storage-system/create-storage-system-steps/backing-storage-step/backing-storage-step.tsx +++ b/packages/odf/components/create-storage-system/create-storage-system-steps/backing-storage-step/backing-storage-step.tsx @@ -3,7 +3,6 @@ import { STORAGE_CLUSTER_SYSTEM_KIND, NO_PROVISIONER, } from '@odf/core/constants'; -import { PROVIDER_MODE } from '@odf/core/features'; import { useSafeK8sGet } from '@odf/core/hooks'; import { useODFNamespaceSelector } from '@odf/core/redux'; import { scResource } from '@odf/core/resources'; @@ -27,7 +26,6 @@ import { } from '@odf/shared/types'; import { useCustomTranslation } from '@odf/shared/useCustomTranslationHook'; import { isDefaultClass, getODFCsv, getGVKLabel } from '@odf/shared/utils'; -import { useFlag } from '@openshift-console/dynamic-plugin-sdk'; import * as _ from 'lodash-es'; import { Form, @@ -214,8 +212,6 @@ export const BackingStorage: React.FC = ({ >(ClusterServiceVersionModel, null, odfNamespace); const isFullDeployment = deployment === DeploymentType.FULL; - const isProviderMode = deployment === DeploymentType.PROVIDER_MODE; - const isProviderModePresent = useFlag(PROVIDER_MODE) && hasInternal; const isNonRHCSExternalType = type === BackingStorageType.EXTERNAL && externalStorage !== StorageClusterModel.kind; @@ -272,7 +268,7 @@ export const BackingStorage: React.FC = ({ * Allow pre selecting the "external connection" option instead of the "existing" option * if an OCS Storage System is already created. */ - if (hasOCS && allowedExternalStorage.length && !isProviderModePresent) { + if (hasOCS && allowedExternalStorage.length) { dispatch({ type: 'backingStorage/setType', payload: BackingStorageType.EXTERNAL, @@ -285,7 +281,7 @@ export const BackingStorage: React.FC = ({ }, }); } - }, [dispatch, allowedExternalStorage.length, hasOCS, isProviderModePresent]); + }, [dispatch, allowedExternalStorage.length, hasOCS]); React.useEffect(() => { /* @@ -390,11 +386,7 @@ export const BackingStorage: React.FC = ({ value={BackingStorageType.EXTERNAL} isChecked={type === BackingStorageType.EXTERNAL} onChange={(event, _unused) => onRadioSelect(_unused, event)} - isDisabled={ - allowedExternalStorage.length === 0 || - isProviderMode || - isProviderModePresent - } + isDisabled={allowedExternalStorage.length === 0} body={ showExternalStorageSelection && ( { - const options = [ - DeploymentType.FULL, - ...(isFDF ? [DeploymentType.PROVIDER_MODE] : []), - DeploymentType.MCG, - ]; +const selectOptions = (t: TFunction) => { + const options = [DeploymentType.FULL, DeploymentType.MCG]; const optionsDescription = { [DeploymentType.MCG]: t( @@ -28,9 +22,6 @@ const selectOptions = (t: TFunction, isFDF: boolean) => { [DeploymentType.FULL]: t( 'Deploys Data Foundation with block, shared fileSystem and object services.' ), - [DeploymentType.PROVIDER_MODE]: t( - 'Deploys Data Foundation as a provider cluster' - ), }; return options.map((option) => ( @@ -51,20 +42,12 @@ export const SelectDeployment: React.FC = ({ const { t } = useCustomTranslation(); const [isSelectOpen, setIsSelectOpen] = React.useState(false); - const isFDF = useFlag(FDF_FLAG); - const handleSelection: SelectProps['onSelect'] = (_, value) => { dispatch({ type: 'backingStorage/setDeployment', // 'value' on SelectProps['onSelect'] is string hence does not match with payload of type "DeploymentType" payload: value as DeploymentType, }); - if (value === DeploymentType.PROVIDER_MODE) { - dispatch({ - type: 'backingStorage/setType', - payload: BackingStorageType.EXISTING, - }); - } setIsSelectOpen(false); }; @@ -85,7 +68,7 @@ export const SelectDeployment: React.FC = ({ selections={deployment} isOpen={isSelectOpen} > - {selectOptions(t, isFDF)} + {selectOptions(t)} ); diff --git a/packages/odf/components/create-storage-system/create-storage-system-steps/capacity-and-nodes-step/capacity-and-nodes-step.tsx b/packages/odf/components/create-storage-system/create-storage-system-steps/capacity-and-nodes-step/capacity-and-nodes-step.tsx index 2d7913004..5aa2b9e29 100644 --- a/packages/odf/components/create-storage-system/create-storage-system-steps/capacity-and-nodes-step/capacity-and-nodes-step.tsx +++ b/packages/odf/components/create-storage-system/create-storage-system-steps/capacity-and-nodes-step/capacity-and-nodes-step.tsx @@ -23,7 +23,6 @@ import { import { useNodesData } from '@odf/core/hooks'; import { pvResource } from '@odf/core/resources'; import { - DeploymentType, NodeData, NodesPerZoneMap, ResourceProfile, @@ -420,7 +419,6 @@ export const CapacityAndNodes: React.FC = ({ volumeSetName, nodes, systemNamespace, - deploymentMode, }) => { const { capacity, @@ -433,7 +431,6 @@ export const CapacityAndNodes: React.FC = ({ } = state; const isNoProvisioner = storageClass.provisioner === NO_PROVISIONER; - const isProviderMode = deploymentMode === DeploymentType.PROVIDER_MODE; const flexibleScaling = isFlexibleScaling( nodes, isNoProvisioner, @@ -453,7 +450,6 @@ export const CapacityAndNodes: React.FC = ({ isNoProvisioner, resourceProfile, osdAmount, - deploymentMode, volumeValidationType ); const onProfileChange = React.useCallback( @@ -487,16 +483,14 @@ export const CapacityAndNodes: React.FC = ({ )} {(!isNoProvisioner || nodes.length > 0) && ( <> - {!isProviderMode && ( - - )} + )} @@ -522,5 +516,4 @@ type CapacityAndNodesProps = { volumeSetName: WizardState['createLocalVolumeSet']['volumeSetName']; dispatch: WizardDispatch; systemNamespace: WizardState['backingStorage']['systemNamespace']; - deploymentMode: DeploymentType; }; diff --git a/packages/odf/components/create-storage-system/create-storage-system-steps/review-and-create-step/review-and-create-step.tsx b/packages/odf/components/create-storage-system/create-storage-system-steps/review-and-create-step/review-and-create-step.tsx index 517a876f9..72f249e2f 100644 --- a/packages/odf/components/create-storage-system/create-storage-system-steps/review-and-create-step/review-and-create-step.tsx +++ b/packages/odf/components/create-storage-system/create-storage-system-steps/review-and-create-step/review-and-create-step.tsx @@ -67,7 +67,6 @@ export const ReviewAndCreate: React.FC = ({ // NooBaa standalone deployment const isMCG = deployment === DeploymentType.MCG; - const isProviderMode = deployment === DeploymentType.PROVIDER_MODE; // External Red Hat Ceph Storage deployment const isRhcs = !_.isEmpty(connectionDetails); // External IBM deployment without ODF @@ -164,13 +163,11 @@ export const ReviewAndCreate: React.FC = ({ memory: humanizeBinaryBytes(totalMemory).string, })} - {!isProviderMode && ( - - {t('Performance profile: {{resourceProfile}}', { - resourceProfile: _.capitalize(capacityAndNodes.resourceProfile), - })} - - )} + + {t('Performance profile: {{resourceProfile}}', { + resourceProfile: _.capitalize(capacityAndNodes.resourceProfile), + })} + {t('Zone: {{zoneCount, number}} zone', { zoneCount: zones.size, @@ -219,13 +216,11 @@ export const ReviewAndCreate: React.FC = ({ })} )} - {!isProviderMode && ( - - {t('Network: {{networkType}}', { - networkType: NetworkTypeLabels[networkType], - })} - - )} + + {t('Network: {{networkType}}', { + networkType: NetworkTypeLabels[networkType], + })} + ))} {isRhcs && ( diff --git a/packages/odf/components/create-storage-system/create-storage-system-steps/security-and-network-step/configure.tsx b/packages/odf/components/create-storage-system/create-storage-system-steps/security-and-network-step/configure.tsx index dc4049bc1..b43c054f6 100644 --- a/packages/odf/components/create-storage-system/create-storage-system-steps/security-and-network-step/configure.tsx +++ b/packages/odf/components/create-storage-system/create-storage-system-steps/security-and-network-step/configure.tsx @@ -1,209 +1,31 @@ import * as React from 'react'; -import { NetworkAttachmentDefinitionModel } from '@odf/core/models'; -import { SingleSelectDropdown } from '@odf/shared/dropdown/singleselectdropdown'; import { FieldLevelHelp } from '@odf/shared/generic/FieldLevelHelp'; -import { useDeepCompareMemoize } from '@odf/shared/hooks/deep-compare-memoize'; -import { getName, getUID } from '@odf/shared/selectors'; import { NetworkAttachmentDefinitionKind } from '@odf/shared/types'; import { useCustomTranslation } from '@odf/shared/useCustomTranslationHook'; -import { referenceForModel } from '@odf/shared/utils'; -import { - ResourceIcon, - WatchK8sResults, - useK8sWatchResources, -} from '@openshift-console/dynamic-plugin-sdk'; +import { useFlag } from '@openshift-console/dynamic-plugin-sdk'; import { K8sResourceCommon } from '@openshift-console/dynamic-plugin-sdk-internal/lib/extensions/console-types'; -import { SelectOption } from '@patternfly/react-core/deprecated'; import * as _ from 'lodash-es'; import { FormGroup, Radio } from '@patternfly/react-core'; +import { FDF_FLAG } from '../../../../redux'; import { NetworkType, NADSelectorType } from '../../../../types'; import { WizardState } from '../../reducer'; +import { MultusNetworkConfigurationComponent } from './multus'; +import { NICSelectComponent } from './nic'; import './configure.scss'; -const resources = (ns: string) => ({ - openshift: { - isList: true, - kind: referenceForModel(NetworkAttachmentDefinitionModel), - namespace: ns, - namespaced: true, - }, - default: { - isList: true, - kind: referenceForModel(NetworkAttachmentDefinitionModel), - namespace: 'default', - namespaced: true, - }, - multus: { - isList: true, - kind: referenceForModel(NetworkAttachmentDefinitionModel), - namespace: 'openshift-multus', - namespaced: true, - }, -}); - -type MultusWatchResourcesObject = { - multus: NetworkAttachmentDefinitionKind[]; - openshift: NetworkAttachmentDefinitionKind[]; - default: NetworkAttachmentDefinitionKind[]; -}; - -const k8sToSelectResourceMapper = ( - item: NetworkAttachmentDefinitionKind -): JSX.Element => { - const uid = getUID(item); - return ( - - - {getName(item)} - - ); -}; - -const reduceResourceLoadAndErrorStatus = < - T extends keyof MultusWatchResourcesObject ->( - acc: { loaded: boolean; error: any }, - curr: WatchK8sResults[T] -) => { - const loadValue = curr.loaded && acc.loaded; - const errorValue = curr.loadError || acc.error; - return { loaded: loadValue, error: errorValue }; -}; - -export const MultusDropdown: React.FC = ({ - setNetwork, - clusterNetwork, - publicNetwork, - systemNamespace, -}) => { - const { t } = useCustomTranslation(); - - const clusterNetworkUID = getUID(clusterNetwork); - const publicNetworkUID = getUID(publicNetwork); - - const networkResources = useK8sWatchResources(resources(systemNamespace)); - - const networkDevices: K8sResourceCommon[] = React.useMemo(() => { - const { loaded: resourcesLoaded, error: resourcesLoadError } = - Object.values(networkResources).reduce(reduceResourceLoadAndErrorStatus, { - loaded: true, - error: null, - }); - - if (resourcesLoaded && !resourcesLoadError) { - const devices = _.flatMap( - Object.values(networkResources), - (res) => res.data - ); - return devices; - } - return []; - }, [networkResources]); - - const memoizedNetworkDevices = useDeepCompareMemoize(networkDevices, true); - - const selectOptions: JSX.Element[] = React.useMemo( - () => memoizedNetworkDevices.map(k8sToSelectResourceMapper), - [memoizedNetworkDevices] - ); - - const filter = React.useCallback( - (_unused, textInput: string) => { - if (!textInput) { - return selectOptions; - } else { - return selectOptions.filter((item) => - (item.props.id as string).toLowerCase().includes(textInput) - ); - } - }, - [selectOptions] - ); - - const onSelectPublicNetwork = React.useCallback( - (uid: string) => { - if (!(uid === publicNetworkUID)) { - const resource = memoizedNetworkDevices.find( - (res) => getUID(res) === uid - ); - setNetwork(NADSelectorType.PUBLIC, resource); - } else { - setNetwork(NADSelectorType.PUBLIC, null); - } - }, - [memoizedNetworkDevices, setNetwork, publicNetworkUID] - ); - - const onSelectClusterNetwork = React.useCallback( - (uid: string) => { - if (!(uid === clusterNetworkUID)) { - const resource = memoizedNetworkDevices.find( - (res) => getUID(res) === uid - ); - setNetwork(NADSelectorType.CLUSTER, resource); - } else { - setNetwork(NADSelectorType.CLUSTER, null); - } - }, - [memoizedNetworkDevices, setNetwork, clusterNetworkUID] - ); - - return ( - <> - - - - - - - - ); -}; - -type MultusDropdownProps = { - setNetwork: (type: NADSelectorType, resource: K8sResourceCommon) => void; - clusterNetwork: NetworkAttachmentDefinitionKind; - publicNetwork: NetworkAttachmentDefinitionKind; - systemNamespace: WizardState['backingStorage']['systemNamespace']; -}; - export const NetworkFormGroup: React.FC = ({ setNetworkType, networkType, - setNetwork, + setMultusNetwork, + setCIDRNetwork, + cephPublicCIDR, + cephClusterCIDR, clusterNetwork, publicNetwork, systemNamespace, }) => { const { t } = useCustomTranslation(); + const isFDF = useFlag(FDF_FLAG); return ( <> @@ -213,11 +35,14 @@ export const NetworkFormGroup: React.FC = ({ className="ceph__install-radio--inline" > - {t('Default (OVN)')} + {t('Default (Pod)')} {t( 'The default OVN uses a single network for all data operations such as read/write and also for control planes, such as data replication.' @@ -229,30 +54,48 @@ export const NetworkFormGroup: React.FC = ({ value={NetworkType.DEFAULT} id={NetworkType.DEFAULT} /> - - {t('Custom (Multus)')} - - {t( - 'Multus allows a network seperation between the data operations and the control plane operations.' - )} - - - } - onChange={() => setNetworkType(NetworkType.MULTUS)} - value={NetworkType.MULTUS} - id={NetworkType.MULTUS} - /> + {isFDF && ( + + {t('Host')} + + {t( + 'Multus allows a network seperation between the data operations and the control plane operations.' + )} + + + } + onChange={() => setNetworkType(NetworkType.HOST)} + value={NetworkType.MULTUS} + id={NetworkType.MULTUS} + /> + )} - {networkType === NetworkType.MULTUS && ( - setNetworkType(type)} + networkType={networkType} + /> + ) : ( + + setCIDRNetwork(cephCIDR, cephPublicCIDR) + } + setPublicCIDR={(publicCIDR: string) => + setCIDRNetwork(cephClusterCIDR, publicCIDR) + } + networkType={networkType} + setNetworkType={(type: NetworkType) => setNetworkType(type)} /> )} @@ -262,8 +105,14 @@ export const NetworkFormGroup: React.FC = ({ type NetworkFormGroupProps = { setNetworkType: any; networkType: NetworkType; - setNetwork: (type: NADSelectorType, resource: K8sResourceCommon) => void; + setMultusNetwork: ( + type: NADSelectorType, + resource: K8sResourceCommon + ) => void; + setCIDRNetwork: (clusterCIDR: string, publicCIDR: string) => void; clusterNetwork: NetworkAttachmentDefinitionKind; publicNetwork: NetworkAttachmentDefinitionKind; + cephClusterCIDR: string; + cephPublicCIDR: string; systemNamespace: WizardState['backingStorage']['systemNamespace']; }; diff --git a/packages/odf/components/create-storage-system/create-storage-system-steps/security-and-network-step/encryption.tsx b/packages/odf/components/create-storage-system/create-storage-system-steps/security-and-network-step/encryption.tsx index 4ae8ea0f6..3d42ca8e9 100644 --- a/packages/odf/components/create-storage-system/create-storage-system-steps/security-and-network-step/encryption.tsx +++ b/packages/odf/components/create-storage-system/create-storage-system-steps/security-and-network-step/encryption.tsx @@ -28,7 +28,6 @@ const EncryptionLabel: React.FC<{ label: string }> = ({ label }) => ( const EncryptionLevel: React.FC = ({ encryption, dispatch, - isProviderMode, }) => { const { t } = useCustomTranslation(); @@ -76,21 +75,19 @@ const EncryptionLevel: React.FC = ({ handleClusterWideEncryption(isChecked) } /> - {!isProviderMode && ( - } - description={t( - 'An encryption key will be generated for each persistent volume (block) created using an encryption enabled StorageClass.' - )} - onChange={(_event, isChecked: boolean) => - handleStorageClassEncryption(isChecked) - } - /> - )} + } + description={t( + 'An encryption key will be generated for each persistent volume (block) created using an encryption enabled StorageClass.' + )} + onChange={(_event, isChecked: boolean) => + handleStorageClassEncryption(isChecked) + } + /> ); }; @@ -98,7 +95,6 @@ const EncryptionLevel: React.FC = ({ type EncryptionLevelProps = { encryption: WizardState['securityAndNetwork']['encryption']; dispatch: WizardDispatch; - isProviderMode?: boolean; }; const KMSConnection: React.FC = ({ @@ -171,7 +167,6 @@ export const Encryption: React.FC = ({ isMCG, isExternal, systemNamespace, - isProviderMode, }) => { const { t } = useCustomTranslation(); const [encryptionChecked, setEncryptionChecked] = React.useState( @@ -274,7 +269,6 @@ export const Encryption: React.FC = ({ )} { + const uid = getUID(item); + return ( + + + {getName(item)} + + ); +}; + +type MultusNetworkConfigurationComponentProps = { + setNetworkType: any; + networkType: NetworkType; + setNetwork: (type: NADSelectorType, resource: K8sResourceCommon) => void; + clusterNetwork: NetworkAttachmentDefinitionKind; + publicNetwork: NetworkAttachmentDefinitionKind; + systemNamespace: WizardState['backingStorage']['systemNamespace']; +}; + +const reduceResourceLoadAndErrorStatus = < + T extends keyof MultusWatchResourcesObject +>( + acc: { loaded: boolean; error: any }, + curr: WatchK8sResults[T] +) => { + const loadValue = curr.loaded && acc.loaded; + const errorValue = curr.loadError || acc.error; + return { loaded: loadValue, error: errorValue }; +}; +type MultusDropdownProps = { + setNetwork: (type: NADSelectorType, resource: K8sResourceCommon) => void; + clusterNetwork: NetworkAttachmentDefinitionKind; + publicNetwork: NetworkAttachmentDefinitionKind; + systemNamespace: WizardState['backingStorage']['systemNamespace']; +}; + +export const MultusNetworkConfigurationComponent: React.FC = + ({ + setNetworkType, + setNetwork, + clusterNetwork, + publicNetwork, + networkType, + systemNamespace, + }) => { + const { t } = useCustomTranslation(); + + return ( + <> + + setNetworkType( + networkType === NetworkType.DEFAULT + ? NetworkType.MULTUS + : NetworkType.DEFAULT + ) + } + label={t('Isolate network using Multus')} + description={t( + 'Multus allows a network seperation between the data operations and the control plane operations.' + )} + id="multus-checkbox" + /> + {networkType === NetworkType.MULTUS && ( + + )} + + ); + }; + +const resources = (ns: string) => ({ + openshift: { + isList: true, + kind: referenceForModel(NetworkAttachmentDefinitionModel), + namespace: ns, + namespaced: true, + }, + default: { + isList: true, + kind: referenceForModel(NetworkAttachmentDefinitionModel), + namespace: 'default', + namespaced: true, + }, + multus: { + isList: true, + kind: referenceForModel(NetworkAttachmentDefinitionModel), + namespace: 'openshift-multus', + namespaced: true, + }, +}); + +export const MultusDropdown: React.FC = ({ + setNetwork, + clusterNetwork, + publicNetwork, + systemNamespace, +}) => { + const { t } = useCustomTranslation(); + + const clusterNetworkUID = getUID(clusterNetwork); + const publicNetworkUID = getUID(publicNetwork); + + const networkResources = useK8sWatchResources(resources(systemNamespace)); + + const networkDevices: K8sResourceCommon[] = React.useMemo(() => { + const { loaded: resourcesLoaded, error: resourcesLoadError } = + Object.values(networkResources).reduce(reduceResourceLoadAndErrorStatus, { + loaded: true, + error: null, + }); + + if (resourcesLoaded && !resourcesLoadError) { + const devices = _.flatMap( + Object.values(networkResources), + (res) => res.data + ); + return devices; + } + return []; + }, [networkResources]); + + const memoizedNetworkDevices = useDeepCompareMemoize(networkDevices, true); + + const selectOptions: JSX.Element[] = React.useMemo( + () => memoizedNetworkDevices.map(k8sToSelectResourceMapper), + [memoizedNetworkDevices] + ); + + const filter = React.useCallback( + (_unused, textInput: string) => { + if (!textInput) { + return selectOptions; + } else { + return selectOptions.filter((item) => + (item.props.id as string).toLowerCase().includes(textInput) + ); + } + }, + [selectOptions] + ); + + const onSelectPublicNetwork = React.useCallback( + (uid: string) => { + if (!(uid === publicNetworkUID)) { + const resource = memoizedNetworkDevices.find( + (res) => getUID(res) === uid + ); + setNetwork(NADSelectorType.PUBLIC, resource); + } else { + setNetwork(NADSelectorType.PUBLIC, null); + } + }, + [memoizedNetworkDevices, setNetwork, publicNetworkUID] + ); + + const onSelectClusterNetwork = React.useCallback( + (uid: string) => { + if (!(uid === clusterNetworkUID)) { + const resource = memoizedNetworkDevices.find( + (res) => getUID(res) === uid + ); + setNetwork(NADSelectorType.CLUSTER, resource); + } else { + setNetwork(NADSelectorType.CLUSTER, null); + } + }, + [memoizedNetworkDevices, setNetwork, clusterNetworkUID] + ); + + return ( + <> + + + + + + + + ); +}; diff --git a/packages/odf/components/create-storage-system/create-storage-system-steps/security-and-network-step/nic.scss b/packages/odf/components/create-storage-system/create-storage-system-steps/security-and-network-step/nic.scss new file mode 100644 index 000000000..60321994c --- /dev/null +++ b/packages/odf/components/create-storage-system/create-storage-system-steps/security-and-network-step/nic.scss @@ -0,0 +1,3 @@ +.odf-install-network__form-group { + max-width: 50%; +} diff --git a/packages/odf/components/create-storage-system/create-storage-system-steps/security-and-network-step/nic.spec.tsx b/packages/odf/components/create-storage-system/create-storage-system-steps/security-and-network-step/nic.spec.tsx new file mode 100644 index 000000000..dd5d063e9 --- /dev/null +++ b/packages/odf/components/create-storage-system/create-storage-system-steps/security-and-network-step/nic.spec.tsx @@ -0,0 +1,73 @@ +import * as React from 'react'; +import { NetworkType } from '@odf/core/types'; +import { fireEvent, render, screen } from '@testing-library/react'; +import userEvent from '@testing-library/user-event'; +import '@testing-library/jest-dom'; +import { NICSelectComponent } from './nic'; + +jest.mock('@odf/shared', () => ({ + useCustomTranslation: () => ({ t: (key: string) => key }), +})); + +describe('NICSelectComponent', () => { + const setPublicCIDR = jest.fn(); + const setCephCIDR = jest.fn(); + const setNetworkType = jest.fn(); + + const renderComponent = (networkType: NetworkType) => + render( + + ); + + it('should render the component with NIC network type', () => { + renderComponent(NetworkType.NIC); + expect( + screen.getByLabelText('Isolate network using NIC Operators') + ).toBeChecked(); + expect(screen.getByText('Ceph Cluster CIDR')).toBeInTheDocument(); + expect(screen.getByText('Ceph Public CIDR')).toBeInTheDocument(); + }); + + it('should render the component with HOST network type', () => { + renderComponent(NetworkType.HOST); + expect( + screen.getByLabelText('Isolate network using NIC Operators') + ).not.toBeChecked(); + expect(screen.queryByText('Ceph Cluster CIDR')).not.toBeInTheDocument(); + expect(screen.queryByText('Ceph Public CIDR')).not.toBeInTheDocument(); + }); + + it('should call setNetworkType when checkbox is clicked', async () => { + renderComponent(NetworkType.HOST); + const user = userEvent.setup(); + await user.click( + screen.getByLabelText('Isolate network using NIC Operators') + ); + expect(setNetworkType).toHaveBeenCalledWith(NetworkType.NIC); + }); + + it('should call setPublicCIDR when public CIDR input is changed', () => { + renderComponent(NetworkType.NIC); + const input = screen.getByPlaceholderText( + 'Enter a CIDR block (Eg: 192.168.0.0/32)' + ); + fireEvent.change(input, { target: { value: '192.168.1.0/24' } }); + expect(setPublicCIDR).toHaveBeenCalledWith('192.168.1.0/24'); + }); + + it('should call setCephCIDR when cluster CIDR input is changed', () => { + renderComponent(NetworkType.NIC); + const input = screen.getByPlaceholderText( + 'Enter a CIDR block (Eg: 192.168.100.0/24)' + ); + fireEvent.change(input, { target: { value: '192.168.101.0/24' } }); + expect(setCephCIDR).toHaveBeenCalledWith('192.168.101.0/24'); + }); +}); diff --git a/packages/odf/components/create-storage-system/create-storage-system-steps/security-and-network-step/nic.tsx b/packages/odf/components/create-storage-system/create-storage-system-steps/security-and-network-step/nic.tsx new file mode 100644 index 000000000..bd363dde9 --- /dev/null +++ b/packages/odf/components/create-storage-system/create-storage-system-steps/security-and-network-step/nic.tsx @@ -0,0 +1,91 @@ +import * as React from 'react'; +import { NetworkType } from '@odf/core/types'; +import { useCustomTranslation } from '@odf/shared'; +import { + FormGroup, + TextInput, + Form, + Checkbox, + Alert, +} from '@patternfly/react-core'; +import './nic.scss'; + +type NICSelectComponentProps = { + cephPublicCIDR: string; + cephClusterCIDR: string; + setPublicCIDR: (cidr: string) => void; + setCephCIDR: (cidr: string) => void; + networkType: NetworkType; + setNetworkType: (networkType: NetworkType) => void; +}; + +export const NICSelectComponent: React.FC = ({ + cephClusterCIDR, + cephPublicCIDR, + setCephCIDR, + setPublicCIDR, + networkType, + setNetworkType, +}) => { + const { t } = useCustomTranslation(); + return ( + <> + + setNetworkType( + networkType === NetworkType.HOST + ? NetworkType.NIC + : NetworkType.HOST + ) + } + id="nic-checkbox" + description={t( + 'Specify the public and network interfaces that Ceph will use for data traffic. Use CIDR notation to define the IP addresses which will bind to on the host.' + )} + /> + {networkType === NetworkType.NIC && ( + <> + to set the correct IP address for the mon before proceeding with the host networking configuration. This ensures that the mons operate on the desired network' + )} + isInline + /> +
+ + setCephCIDR(cidr)} + type="text" + id="ceph-cluster-cidr" + aria-describedby="ceph-cluster-cidr" + placeholder={t('Enter a CIDR block (Eg: 192.168.100.0/24)')} + /> + + + setPublicCIDR(cidr)} + type="text" + id="ceph-public-cidr" + aria-describedby="ceph-public-cidr" + placeholder={t('Enter a CIDR block (Eg: 192.168.0.0/32)')} + /> + +
+ + )} + + ); +}; diff --git a/packages/odf/components/create-storage-system/create-storage-system-steps/security-and-network-step/security-and-network-step.tsx b/packages/odf/components/create-storage-system/create-storage-system-steps/security-and-network-step/security-and-network-step.tsx index 85b71757e..855093b4e 100644 --- a/packages/odf/components/create-storage-system/create-storage-system-steps/security-and-network-step/security-and-network-step.tsx +++ b/packages/odf/components/create-storage-system/create-storage-system-steps/security-and-network-step/security-and-network-step.tsx @@ -24,6 +24,10 @@ export const SecurityAndNetwork: React.FC = ({ publicNetwork, encryption, kms, + addressRanges: { + cluster: [cephClusterCIDR], + public: [cephPublicCIDR], + }, } = securityAndNetworkState; const setNetworkType = (networkType: NetworkType) => { @@ -31,13 +35,16 @@ export const SecurityAndNetwork: React.FC = ({ type: 'securityAndNetwork/setNetworkType', payload: networkType, }); - if (networkType === NetworkType.DEFAULT) { + if ( + networkType === NetworkType.DEFAULT || + networkType === NetworkType.HOST + ) { dispatch({ type: 'securityAndNetwork/setClusterNetwork', payload: null }); dispatch({ type: 'securityAndNetwork/setPublicNetwork', payload: null }); } }; - const setNetwork = React.useCallback( + const setMultusNetwork = React.useCallback( (network: NADSelectorType, resource: K8sResourceCommon) => dispatch({ type: @@ -49,6 +56,20 @@ export const SecurityAndNetwork: React.FC = ({ [dispatch] ); + const setCIDRNetwork = React.useCallback( + (clusterCIDR: string, publicCIDR: string) => { + dispatch({ + type: 'securityAndNetwork/setCephCIDR', + payload: [clusterCIDR], + }); + dispatch({ + type: 'securityAndNetwork/setPublicCIDR', + payload: [publicCIDR], + }); + }, + [dispatch] + ); + return (
= ({ { case 'securityAndNetwork/setNetworkType': newState.securityAndNetwork.networkType = action.payload; break; + case 'securityAndNetwork/setPublicCIDR': + newState.securityAndNetwork.addressRanges.public = action.payload; + break; + case 'securityAndNetwork/setCephCIDR': + newState.securityAndNetwork.addressRanges.cluster = action.payload; + break; default: throw new TypeError(`${action} is not a valid reducer action`); } @@ -503,6 +519,14 @@ export type CreateStorageSystemAction = type: 'securityAndNetwork/setClusterNetwork'; payload: WizardState['securityAndNetwork']['clusterNetwork']; } + | { + type: 'securityAndNetwork/setPublicCIDR'; + payload: WizardState['securityAndNetwork']['addressRanges']['public']; + } + | { + type: 'securityAndNetwork/setCephCIDR'; + payload: WizardState['securityAndNetwork']['addressRanges']['cluster']; + } | { type: 'securityAndNetwork/setNetworkType'; payload: WizardState['securityAndNetwork']['networkType']; diff --git a/packages/odf/components/utils/common.ts b/packages/odf/components/utils/common.ts index 3fb94a695..025610df5 100644 --- a/packages/odf/components/utils/common.ts +++ b/packages/odf/components/utils/common.ts @@ -4,8 +4,8 @@ import { EncryptionType, ResourceProfile, NodeData, - DeploymentType, VolumeTypeValidation, + NetworkType, } from '@odf/core/types'; import { getNodeCPUCapacity, @@ -147,7 +147,6 @@ export const capacityAndNodesValidate = ( isNoProvSC: boolean, resourceProfile: ResourceProfile, osdAmount: number, - deploymentType: DeploymentType, volumeValidationType: VolumeTypeValidation ): ValidationType[] => { const validations = []; @@ -160,11 +159,7 @@ export const capacityAndNodesValidate = ( } if (!enableStretchCluster && nodes.length && nodes.length < MINIMUM_NODES) { validations.push(ValidationType.MINIMUMNODES); - } else if ( - nodes.length && - nodes.length >= MINIMUM_NODES && - deploymentType !== DeploymentType.PROVIDER_MODE - ) { + } else if (nodes.length && nodes.length >= MINIMUM_NODES) { if ( !isResourceProfileAllowed( resourceProfile, @@ -387,21 +382,24 @@ export const getDeviceSetReplica = ( const generateNetworkCardName = (resource: NetworkAttachmentDefinitionKind) => `${getNamespace(resource)}/${getName(resource)}`; -type OCSRequestData = { +type NetworkConfiguration = Omit< + WizardState['securityAndNetwork'], + 'encryption' | 'kms' +>; + +export type OCSRequestData = { storageClass: WizardState['storageClass']; storage: string; encryption: EncryptionType; resourceProfile: ResourceProfile; nodes: WizardNodeState[]; flexibleScaling: boolean; - publicNetwork?: NetworkAttachmentDefinitionKind; - clusterNetwork?: NetworkAttachmentDefinitionKind; + networkConfiguration: NetworkConfiguration; kmsEnable?: boolean; selectedArbiterZone?: string; stretchClusterChecked?: boolean; availablePvsCount?: number; isMCG?: boolean; - isProviderMode?: boolean; isNFSEnabled?: boolean; shouldSetCephRBDAsDefault?: boolean; storageClusterNamespace: string; @@ -419,15 +417,13 @@ export const getOCSRequestData = ({ resourceProfile, nodes, flexibleScaling, - publicNetwork, - clusterNetwork, + networkConfiguration, kmsEnable, selectedArbiterZone, stretchClusterChecked, availablePvsCount, isMCG, isNFSEnabled, - isProviderMode, shouldSetCephRBDAsDefault, storageClusterNamespace, useExternalPostgres, @@ -494,34 +490,19 @@ export const getOCSRequestData = ({ ), ], ...Object.assign( - getNetworkField(publicNetwork, clusterNetwork, encryption.inTransit) + getNetworkField(networkConfiguration, encryption.inTransit) ), managedResources: { cephBlockPools: { defaultStorageClass: shouldSetCephRBDAsDefault }, }, }; + } - if (isProviderMode) { - requestData.spec.allowRemoteStorageConsumers = true; - requestData.spec.hostNetwork = true; - Object.assign< - StorageClusterKind['spec']['managedResources'], - StorageClusterKind['spec']['managedResources'] - >(requestData.spec.managedResources, { - ...requestData.spec.managedResources, - cephBlockPools: { - disableSnapshotClass: true, - disableStorageClass: true, - }, - cephFilesystems: { - disableSnapshotClass: true, - disableStorageClass: true, - }, - cephObjectStores: { - hostNetwork: false, - }, - }); - } + if ( + networkConfiguration.networkType === NetworkType.HOST || + networkConfiguration.networkType === NetworkType.NIC + ) { + requestData.spec.hostNetwork = true; } if (encryption) { @@ -571,10 +552,30 @@ export const getOCSRequestData = ({ }; const getNetworkField = ( - publicNetwork: NetworkAttachmentDefinitionKind, - clusterNetwork: NetworkAttachmentDefinitionKind, - inTransitEncryption: boolean + networkConfiguration: NetworkConfiguration, + isTransitEncryptionEnabled: boolean ) => { + const { + networkType, + publicNetwork, + clusterNetwork, + addressRanges: { cluster, public: publicAddressRange }, + } = networkConfiguration; + if (networkType === NetworkType.HOST || networkType === NetworkType.NIC) { + return { + network: { + connections: { + encryption: { + enabled: isTransitEncryptionEnabled, + }, + }, + addressRanges: { + cluster: cluster, + public: publicAddressRange, + }, + }, + } as StorageClusterKind['spec']['network']; + } const publicNetworkString = generateNetworkCardName(publicNetwork); const privateNetworkString = generateNetworkCardName(clusterNetwork); const multusNetwork = @@ -596,7 +597,7 @@ const getNetworkField = ( ...multusNetwork, connections: { encryption: { - enabled: inTransitEncryption, + enabled: isTransitEncryptionEnabled, }, }, }, diff --git a/packages/odf/types/common.ts b/packages/odf/types/common.ts index 382f0ce96..d59270c20 100644 --- a/packages/odf/types/common.ts +++ b/packages/odf/types/common.ts @@ -15,7 +15,6 @@ export enum BackingStorageType { export enum DeploymentType { FULL = 'Full deployment', MCG = 'MultiCloud Object Gateway', - PROVIDER_MODE = 'Provider Mode', } export enum VolumeTypeValidation { diff --git a/packages/odf/types/network.ts b/packages/odf/types/network.ts index 1630c1f89..d991e1a80 100644 --- a/packages/odf/types/network.ts +++ b/packages/odf/types/network.ts @@ -1,6 +1,8 @@ export enum NetworkType { DEFAULT = 'DEFAULT', MULTUS = 'MULTUS', + HOST = 'HOST', + NIC = 'NIC', } export enum NADSelectorType { diff --git a/packages/shared/src/types/storage.ts b/packages/shared/src/types/storage.ts index bef7d4dd8..52c06729f 100644 --- a/packages/shared/src/types/storage.ts +++ b/packages/shared/src/types/storage.ts @@ -25,6 +25,10 @@ export type StorageClusterKind = K8sResourceCommon & { public: string; private?: string; }; + addressRanges?: { + public: string[]; + cluster: string[]; + }; }; nfs?: { enable?: boolean;