From 2ea4b7e9e3e6a4228d1052cef55c2aba37707be6 Mon Sep 17 00:00:00 2001 From: jgyselov Date: Wed, 17 Sep 2025 10:43:14 +0200 Subject: [PATCH 1/2] Clean up ClusterDetailsFormFields --- .../ClusterDetailsFormFields.tsx | 39 ++----------------- 1 file changed, 4 insertions(+), 35 deletions(-) diff --git a/libs/ui-lib/lib/cim/components/ClusterDeployment/ClusterDetailsFormFields.tsx b/libs/ui-lib/lib/cim/components/ClusterDeployment/ClusterDetailsFormFields.tsx index b51a49e9a2..deec5450ef 100644 --- a/libs/ui-lib/lib/cim/components/ClusterDeployment/ClusterDetailsFormFields.tsx +++ b/libs/ui-lib/lib/cim/components/ClusterDeployment/ClusterDetailsFormFields.tsx @@ -1,5 +1,5 @@ import * as React from 'react'; -import { Alert, AlertVariant, FlexItem, Form } from '@patternfly/react-core'; +import { Form } from '@patternfly/react-core'; import { useFormikContext } from 'formik'; import { OpenShiftVersionDropdown, OpenShiftVersionModal } from '../../../common'; @@ -100,9 +100,6 @@ export const ClusterDetailsFormFields: React.FC = return []; }, [selectOptions, values.customOpenshiftSelect]); - const isDiskEncryptionEnabled = - values.enableDiskEncryptionOnMasters || values.enableDiskEncryptionOnWorkers; - return (
{isEditFlow ? ( @@ -167,40 +164,12 @@ export const ClusterDetailsFormFields: React.FC = {!isNutanix && ( )} + {extensionAfter?.['openshiftVersion'] && extensionAfter['openshiftVersion']} + {!isEditFlow && } + {extensionAfter?.['pullSecret'] && extensionAfter['pullSecret']} - {/* */} - {isDiskEncryptionEnabled && values.diskEncryptionMode === 'tpmv2' && ( - - {t( - 'ai:To use this encryption method, enable TPMv2 encryption in the BIOS of each selected host.', - )} - - } - /> - )} - {isDiskEncryptionEnabled && values.diskEncryptionMode === 'tang' && ( - - {t( - 'ai:The use of Tang encryption mode to encrypt your disks is only supported for bare metal or vSphere installations on user-provisioned infrastructure.', - )} - - } - /> - )} ); }; From e55c6903b98d4fc31039cb8df3f9d0247467177d Mon Sep 17 00:00:00 2001 From: jgyselov Date: Wed, 17 Sep 2025 16:01:55 +0200 Subject: [PATCH 2/2] Allow TNA arbiter in CIM --- libs/locales/lib/en/translation.json | 3 +- .../ClusterDetailsFormFields.tsx | 15 ++- .../cim/types/k8s/agent-cluster-install.ts | 1 + .../ControlPlaneNodesDropdown.tsx | 92 ++++++++++++------- .../clusterConfiguration/OcmTNADisclaimer.tsx | 2 +- .../clusterWizard/clusterDetailsValidation.ts | 2 +- libs/ui-lib/lib/common/config/constants.ts | 16 +--- .../ControlPlaneNodesDropdown.tsx | 2 +- 8 files changed, 85 insertions(+), 48 deletions(-) rename libs/ui-lib/lib/{ocm => common}/components/clusterConfiguration/OcmTNADisclaimer.tsx (87%) diff --git a/libs/locales/lib/en/translation.json b/libs/locales/lib/en/translation.json index b9ce10423b..45103fd175 100644 --- a/libs/locales/lib/en/translation.json +++ b/libs/locales/lib/en/translation.json @@ -34,6 +34,7 @@ "ai:1-{{count}} characters_plural": "1-{{count}} characters", "ai:1-253 characters": "1-253 characters", "ai:1-63 characters": "1-63 characters", + "ai:2 (Two-Nodes Arbiter)": "2 (Two-Nodes Arbiter)", "ai:2-{{count}} characters": "2-{{count}} characters", "ai:2-{{count}} characters_plural": "2-{{count}} characters", "ai:3 (highly available cluster)": "3 (highly available cluster)", @@ -815,7 +816,6 @@ "ai:The storage sizes will be used to store different files and data for cluster creation.": "The storage sizes will be used to store different files and data for cluster creation.", "ai:The subnet prefix length to assign to each individual node. For example, if Cluster Network Host Prefix is set to 116, then each node is assigned a /116 subnet out of the given cidr (clusterNetworkCIDR), which allows for 4,094 (2^(128 - 116) - 2) pod IPs addresses. If you are required to provide access to nodes from an external network, configure load balancers and routers to manage the traffic.": "The subnet prefix length to assign to each individual node. For example, if Cluster Network Host Prefix is set to 116, then each node is assigned a /116 subnet out of the given cidr (clusterNetworkCIDR), which allows for 4,094 (2^(128 - 116) - 2) pod IPs addresses. If you are required to provide access to nodes from an external network, configure load balancers and routers to manage the traffic.", "ai:The subnet prefix length to assign to each individual node. For example, if Cluster Network Host Prefix is set to 23, then each node is assigned a /23 subnet out of the given cidr (clusterNetworkCIDR), which allows for 510 (2^(32 - 23) - 2) pod IPs addresses. If you are required to provide access to nodes from an external network, configure load balancers and routers to manage the traffic.": "The subnet prefix length to assign to each individual node. For example, if Cluster Network Host Prefix is set to 23, then each node is assigned a /23 subnet out of the given cidr (clusterNetworkCIDR), which allows for 510 (2^(32 - 23) - 2) pod IPs addresses. If you are required to provide access to nodes from an external network, configure load balancers and routers to manage the traffic.", - "ai:The use of Tang encryption mode to encrypt your disks is only supported for bare metal or vSphere installations on user-provisioned infrastructure.": "The use of Tang encryption mode to encrypt your disks is only supported for bare metal or vSphere installations on user-provisioned infrastructure.", "ai:The Value is not valid BMC address, supported protocols are redfish-virtualmedia or idrac-virtualmedia.": "The Value is not valid BMC address, supported protocols are redfish-virtualmedia or idrac-virtualmedia.", "ai:The YAML file might not be formatted correctly. Use the template to format and try again.": "The YAML file might not be formatted correctly. Use the template to format and try again.", "ai:There are no events that match the current filters. Adjust or clear the filters to view events.": "There are no events that match the current filters. Adjust or clear the filters to view events.", @@ -859,7 +859,6 @@ "ai:To enable the host's baseboard management controller (BMC) on the hub cluster, you must first <2>create a provisioning configuration.": "To enable the host's baseboard management controller (BMC) on the hub cluster, you must first <2>create a provisioning configuration.", "ai:To finish adding nodes to the cluster, approve the join request inside OpenShift Console's Nodes section.": "To finish adding nodes to the cluster, approve the join request inside OpenShift Console's Nodes section.", "ai:To use static network configuration, follow the steps listed in the documentation.": "To use static network configuration, follow the steps listed in the documentation.", - "ai:To use this encryption method, enable TPMv2 encryption in the BIOS of each selected host.": "To use this encryption method, enable TPMv2 encryption in the BIOS of each selected host.", "ai:To verify that the agent ran successfully, check the logs:": "To verify that the agent ran successfully, check the logs:", "ai:To view detailed agent logs and communication use following command:": "To view detailed agent logs and communication use following command:", "ai:Total compute": "Total compute", diff --git a/libs/ui-lib/lib/cim/components/ClusterDeployment/ClusterDetailsFormFields.tsx b/libs/ui-lib/lib/cim/components/ClusterDeployment/ClusterDetailsFormFields.tsx index deec5450ef..da0dff8bf5 100644 --- a/libs/ui-lib/lib/cim/components/ClusterDeployment/ClusterDetailsFormFields.tsx +++ b/libs/ui-lib/lib/cim/components/ClusterDeployment/ClusterDetailsFormFields.tsx @@ -2,7 +2,11 @@ import * as React from 'react'; import { Form } from '@patternfly/react-core'; import { useFormikContext } from 'formik'; -import { OpenShiftVersionDropdown, OpenShiftVersionModal } from '../../../common'; +import { + isMajorMinorVersionEqualOrGreater, + OpenShiftVersionDropdown, + OpenShiftVersionModal, +} from '../../../common'; import { StaticTextField } from '../../../common/components/ui/StaticTextField'; import { PullSecret } from '../../../common/components/clusters'; import { OpenshiftVersionOptionType, SupportedCpuArchitecture } from '../../../common/types'; @@ -100,6 +104,14 @@ export const ClusterDetailsFormFields: React.FC = return []; }, [selectOptions, values.customOpenshiftSelect]); + const allowTNA = React.useMemo(() => { + const current = + values.customOpenshiftSelect?.version || + versions.find((version) => version.value === values.openshiftVersion)?.version; + + return isMajorMinorVersionEqualOrGreater(current, '4.19') && values.platform === 'baremetal'; + }, [values.customOpenshiftSelect?.version, values.openshiftVersion, values.platform, versions]); + return (
{isEditFlow ? ( @@ -160,6 +172,7 @@ export const ClusterDetailsFormFields: React.FC = {!isNutanix && ( diff --git a/libs/ui-lib/lib/cim/types/k8s/agent-cluster-install.ts b/libs/ui-lib/lib/cim/types/k8s/agent-cluster-install.ts index 4abc31af8f..bf8e15237e 100644 --- a/libs/ui-lib/lib/cim/types/k8s/agent-cluster-install.ts +++ b/libs/ui-lib/lib/cim/types/k8s/agent-cluster-install.ts @@ -42,6 +42,7 @@ export type AgentClusterInstallK8sResource = K8sResourceCommon & { provisionRequirements: { controlPlaneAgents: number; workerAgents?: number; + arbiterAgents?: number; }; networking: { clusterNetwork?: { diff --git a/libs/ui-lib/lib/common/components/clusterConfiguration/ControlPlaneNodesDropdown.tsx b/libs/ui-lib/lib/common/components/clusterConfiguration/ControlPlaneNodesDropdown.tsx index c871a7613c..79afe36864 100644 --- a/libs/ui-lib/lib/common/components/clusterConfiguration/ControlPlaneNodesDropdown.tsx +++ b/libs/ui-lib/lib/common/components/clusterConfiguration/ControlPlaneNodesDropdown.tsx @@ -12,6 +12,21 @@ import { useTranslation } from '../../hooks/use-translation-wrapper'; import { getFieldId, StaticField } from '../..'; import { useField } from 'formik'; import toNumber from 'lodash-es/toNumber'; +import OcmTNADisclaimer from './OcmTNADisclaimer'; + +const isItemEnabled = (value: number, allowHighlyAvailable?: boolean, allowTNA?: boolean) => { + switch (value) { + case 1: + case 3: + return true; + case 2: + return !!allowTNA; + case 4: + case 5: + return !!allowHighlyAvailable; + } + return false; +}; interface ControlPlaneNodesOption { value: number; @@ -21,9 +36,11 @@ interface ControlPlaneNodesOption { const ControlPlaneNodesDropdown = ({ isDisabled = false, allowHighlyAvailable, + allowTNA, }: { isDisabled?: boolean; allowHighlyAvailable?: boolean; + allowTNA?: boolean; }) => { const { t } = useTranslation(); const [{ name, value: selectedValue }, , { setValue }] = useField('controlPlaneCount'); @@ -32,6 +49,7 @@ const ControlPlaneNodesDropdown = ({ const options: ControlPlaneNodesOption[] = [ { value: 1, label: t('ai:1 (Single Node OpenShift - not highly available cluster)') }, + { value: 2, label: t('ai:2 (Two-Nodes Arbiter)') }, { value: 3, label: t('ai:3 (highly available cluster)') }, { value: 4, label: t('ai:4 (highly available cluster+)') }, { value: 5, label: t('ai:5 (highly available cluster++)') }, @@ -44,7 +62,8 @@ const ControlPlaneNodesDropdown = ({ }, [allowHighlyAvailable, selectedValue, setValue]); const dropdownItems = options.map(({ value, label }) => { - const isItemEnabled = [1, 3].includes(value) || allowHighlyAvailable; + const isEnabled = isItemEnabled(value, allowHighlyAvailable, allowTNA); + const disabledReason = t('ai:This option is not available with the selected OpenShift version'); return ( - @@ -70,36 +89,47 @@ const ControlPlaneNodesDropdown = ({ setControlPlanelOpen(false); }; - return !isDisabled ? ( - - setControlPlanelOpen(!controlPlanelOpen)} - toggle={(toggleRef: React.Ref) => ( - setControlPlanelOpen(!controlPlanelOpen)} - isExpanded={controlPlanelOpen} + return ( + <> + {!isDisabled ? ( + + setControlPlanelOpen(!controlPlanelOpen)} + toggle={(toggleRef: React.Ref) => ( + setControlPlanelOpen(!controlPlanelOpen)} + isExpanded={controlPlanelOpen} + > + {selectedValue ? selectedValue : '3'} + + )} + shouldFocusToggleOnSelect > - {selectedValue ? selectedValue : '3'} - - )} - shouldFocusToggleOnSelect - > - {dropdownItems} - - - ) : ( - - {selectedValue} - + {dropdownItems} + + + ) : ( + + {selectedValue} + + )} + + {selectedValue === 2 && } + ); }; diff --git a/libs/ui-lib/lib/ocm/components/clusterConfiguration/OcmTNADisclaimer.tsx b/libs/ui-lib/lib/common/components/clusterConfiguration/OcmTNADisclaimer.tsx similarity index 87% rename from libs/ui-lib/lib/ocm/components/clusterConfiguration/OcmTNADisclaimer.tsx rename to libs/ui-lib/lib/common/components/clusterConfiguration/OcmTNADisclaimer.tsx index 97bfc76b4c..902c963484 100644 --- a/libs/ui-lib/lib/ocm/components/clusterConfiguration/OcmTNADisclaimer.tsx +++ b/libs/ui-lib/lib/common/components/clusterConfiguration/OcmTNADisclaimer.tsx @@ -1,6 +1,6 @@ import React from 'react'; import { Alert, AlertVariant, List, ListItem } from '@patternfly/react-core'; -import { useTranslation } from '../../../common/hooks/use-translation-wrapper'; +import { useTranslation } from '../../hooks/use-translation-wrapper'; const OcmTNADisclaimer = () => { const { t } = useTranslation(); diff --git a/libs/ui-lib/lib/common/components/clusterWizard/clusterDetailsValidation.ts b/libs/ui-lib/lib/common/components/clusterWizard/clusterDetailsValidation.ts index 4f2c0be89d..34d07e9321 100644 --- a/libs/ui-lib/lib/common/components/clusterWizard/clusterDetailsValidation.ts +++ b/libs/ui-lib/lib/common/components/clusterWizard/clusterDetailsValidation.ts @@ -75,7 +75,7 @@ export const getClusterDetailsInitialValues = ({ diskEncryptionTangServers: parseTangServers(cluster?.diskEncryption?.tangServers), diskEncryption: cluster?.diskEncryption ?? {}, cpuArchitecture: cluster?.cpuArchitecture || getDefaultCpuArchitecture(), - platform: cluster?.platform?.type || 'none', + platform: cluster?.platform?.type || 'baremetal', customOpenshiftSelect: null, userManagedNetworking: cluster?.userManagedNetworking || false, enableDiskEncryptionOnArbiters: ['all', 'arbiters'].includes( diff --git a/libs/ui-lib/lib/common/config/constants.ts b/libs/ui-lib/lib/common/config/constants.ts index 6801376c21..c26882478c 100644 --- a/libs/ui-lib/lib/common/config/constants.ts +++ b/libs/ui-lib/lib/common/config/constants.ts @@ -5,12 +5,10 @@ import { ClusterValidationId, DiskRole, Event, - HostRoleUpdateParams, HostValidationId, } from '@openshift-assisted/types/assisted-installer-service'; import { ValidationGroup as ClusterValidationGroup } from '../types/clusters'; import buildManifest from '@openshift-assisted/ui-lib/package.json'; -import { isInOcm } from '../api/axiosClient'; export const DEFAULT_POLLING_INTERVAL = 10 * 1000; export const REDUCED_POLLING_INTERVAL = 10 * 1000 * 6; @@ -35,15 +33,11 @@ export const hostRoles = (t: TFunction): HostRole[] => [ 'ai:Runs application workloads. Connect at least 5 hosts to enable dedicated workers.', ), }, - ...(isInOcm - ? [ - { - value: 'arbiter' as HostRoleUpdateParams, - label: t('ai:Arbiter'), - description: t('ai:Prevents split-brain scenarios and maintains quorum.'), - }, - ] - : []), + { + value: 'arbiter', + label: t('ai:Arbiter'), + description: t('ai:Prevents split-brain scenarios and maintains quorum.'), + }, ]; export const clusterStatusLabels = (t: TFunction): { [key in Cluster['status']]: string } => ({ diff --git a/libs/ui-lib/lib/ocm/components/clusterConfiguration/ControlPlaneNodesDropdown.tsx b/libs/ui-lib/lib/ocm/components/clusterConfiguration/ControlPlaneNodesDropdown.tsx index a1f386ecdf..2e027e609c 100644 --- a/libs/ui-lib/lib/ocm/components/clusterConfiguration/ControlPlaneNodesDropdown.tsx +++ b/libs/ui-lib/lib/ocm/components/clusterConfiguration/ControlPlaneNodesDropdown.tsx @@ -17,7 +17,7 @@ import { useNewFeatureSupportLevel, } from '../../../common/components/newFeatureSupportLevels'; import { isFeatureSupportedAndAvailable } from '../featureSupportLevels/featureStateUtils'; -import OcmTNADisclaimer from './OcmTNADisclaimer'; +import OcmTNADisclaimer from '../../../common/components/clusterConfiguration/OcmTNADisclaimer'; import OcmSNODisclaimer from './OcmSNODisclaimer'; const INPUT_NAME = 'controlPlaneCount';