diff --git a/libs/locales/lib/en/translation.json b/libs/locales/lib/en/translation.json index 5fb1169015..25099c90ec 100644 --- a/libs/locales/lib/en/translation.json +++ b/libs/locales/lib/en/translation.json @@ -83,7 +83,6 @@ "ai:Allocate IPs via DHCP server": "Allocate IPs using DHCP server", "ai:Already approved": "Already approved", "ai:Also note that each host's disk write speed should meet the minimum requirements to run OpenShift. ": "Also note that the disk write speed of each host must meet the minimum requirements to run OpenShift. ", - "ai:AMD GPU": "AMD GPU", "ai:AMD GPU requirements": "AMD GPU requirements", "ai:An error occured": "An error occured.", "ai:An error occured while approving agents": "An error occured while approving agents.", @@ -108,7 +107,6 @@ "ai:arm64 is not supported in this OpenShift version": "arm64 is not supported in this OpenShift version", "ai:At least 3 hosts are required, capable of functioning as control plane nodes.": "At least 3 hosts are required that are capable of functioning as control plane nodes.", "ai:Authentication is provided by the discovery ISO, therefore when you access your host using SSH, a password is not required. Optional -i parameter can be used to specify the private key that matches the public key provided when generating Discovery ISO.": "Authentication is provided by the Discovery ISO, so a password is not required when you access your host using SSH. The optional -i parameter can be used to specify the private key that matches the public key that is provided when generating Discovery ISO.", - "ai:Authorino": "Authorino", "ai:Auto synchronized NTP (Network Time Protocol) sources": "Auto synchronized NTP (Network Time Protocol) sources", "ai:Auto-assign": "Auto-assign", "ai:Auto-select hosts": "Auto-select hosts", @@ -342,7 +340,6 @@ "ai:Failed to update the AgentServiceConfig": "Failed to update the AgentServiceConfig", "ai:Failed validations:": "Failed validations:", "ai:Failing infrastructure environment": "Failing infrastructure environment", - "ai:Fence Agents Remediation": "Fence Agents Remediation", "ai:Fence Agents Remediation requirements": "Fence Agents Remediation requirements", "ai:File is not structured correctly. Use the template to use the right file structure.": "File is not structured correctly. Use the template to use the right file structure.", "ai:File size is too big. Upload a new {{maxFileSizeKb}} Kb or less.": "File size is too big. Upload a new file that is {{maxFileSizeKb}} Kb or less.", @@ -483,9 +480,7 @@ "ai:Keep the Discovery ISO media connected to the device throughout the installation process and set each host to boot only one time from this device. ": "Keep the Discovery ISO media connected to the device throughout the installation process and set each host to boot only one time from this device. ", "ai:Keep the Discovery ISO media connected to the device throughout the installation process and set each host to boot only one time from this device. _plural": "Keep the Discovery ISO media connected to the device throughout the installation process and set each host to boot only one time from this device. ", "ai:Keep the field empty to match any location.": "Keep the field empty to match any location.", - "ai:Kernel Module Management": "Kernel Module Management", "ai:Kernel Module Management requirements": "Kernel Module Management requirements", - "ai:Kube Descheduler": "Kube Descheduler", "ai:Kube Descheduler requirements": "Kube Descheduler requirements", "ai:Kubeconfig is empty.": "Kubeconfig is empty.", "ai:Labels": "Labels", @@ -507,9 +502,7 @@ "ai:Loading proxy configuration": "Loading proxy configuration", "ai:Location": "Location", "ai:Location is a required field.": "Location is a required field.", - "ai:Logical Volume Manager": "Logical Volume Manager", "ai:Logical Volume Manager requirements": "Logical Volume Manager requirements", - "ai:Logical Volume Manager Storage": "Logical Volume Manager Storage", "ai:LSO requirements": "LSO requirements", "ai:MAC address": "MAC address", "ai:MAC has to be specified": "MAC must be specified", @@ -534,7 +527,6 @@ "ai:Memory capacity": "Memory capacity", "ai:Message": "Message", "ai:Metal3 operator is not configured": "Metal3 operator is not configured", - "ai:Migration Toolkit for Virtualization": "Migration Toolkit for Virtualization", "ai:Migration toolkit for virtualization requirements": "Migration toolkit for virtualization requirements", "ai:Minimal image file": "Minimal image file", "ai:Minimal value is 10Gi": "Minimal value is 10Gi", @@ -555,7 +547,6 @@ "ai:More info for system storage": "More information for system storage", "ai:MTU (maximum transmission unit) failure": "MTU (maximum transmission unit) failure", "ai:MTU requirements": "MTU requirements", - "ai:Multicluster engine": "Multicluster engine", "ai:Multicluster engine requirements": "Multicluster engine requirements", "ai:Must be at least 1": "Must be at least 1", "ai:Must be unique": "Must be unique", @@ -602,11 +593,8 @@ "ai:No subnets are currently available": "No subnets are currently available", "ai:No support level data for version {{openshiftVersion}}": "No support level data for version {{openshiftVersion}}", "ai:No version selected": "No version selected", - "ai:Node Feature Discovery": "Node Feature Discovery", "ai:Node Feature Discovery requirements": "Node Feature Discovery requirements", - "ai:Node Healthcheck": "Node Healthcheck", "ai:Node Healthcheck requirements": "Node Healthcheck requirements", - "ai:Node Maintenace": "Node Maintenace", "ai:Node Maintenance requirements": "Node Maintenance requirements", "ai:Nodepool": "Nodepool", "ai:Nodepool conditions": "Nodepool conditions", @@ -627,7 +615,6 @@ "ai:Number of characters between dots (.) must be 1-63": "Number of characters between dots (.) must be 1-63", "ai:Number of control plane nodes": "Number of control plane nodes", "ai:Number of hosts": "Number of hosts", - "ai:NVIDIA GPU": "NVIDIA GPU", "ai:NVIDIA GPU requirements": "NVIDIA GPU requirements", "ai:OCS requirements": "OCS requirements", "ai:ODF requirements": "ODF requirements", @@ -642,15 +629,11 @@ "ai:Open Virtual Networking (OVN)": "Open Virtual Networking (OVN)", "ai:Opening file": "Opening file", "ai:OpenShift": "OpenShift", - "ai:OpenShift AI": "OpenShift AI", "ai:OpenShift AI requirements": "OpenShift AI requirements", "ai:OpenShift Cluster Manager": "OpenShift Cluster Manager", - "ai:OpenShift Data Foundation": "OpenShift Data Foundation", "ai:OpenShift in-place upgrades aren't expected to work with SNO. If an upgrade is needed, your system will need a redeployment.": "OpenShift in-place upgrades are not expected to work with SNO. If an upgrade is needed, your system to be redeployed.", - "ai:OpenShift sandboxed containers": "OpenShift sandboxed containers", "ai:OpenShift sandboxed containers requirements": "OpenShift sandboxed containers requirements", "ai:OpenShift version": "OpenShift version", - "ai:OpenShift Virtualization": "OpenShift Virtualization", "ai:OpenShift Web Console troubleshooting": "OpenShift Web Console troubleshooting", "ai:Operators": "Operators", "ai:Option 1: Add the following records to your DNS server (recommended)": "Option 1: Add the following records to your DNS server (recommended)", @@ -667,7 +650,6 @@ "ai:Pending input": "Pending input", "ai:Pending user action": "Pending user action", "ai:Pending validations:": "Pending validations:", - "ai:Pipelines": "Pipelines", "ai:Pipelines requirements": "Pipelines requirements", "ai:Platform network settings": "Platform network settings", "ai:Platform requirements": "Platform requirements", @@ -748,14 +730,11 @@ "ai:Select one or multiple locations to choose the hosts from.": "Select one or multiple locations of the hosts.", "ai:selected": "selected", "ai:Selected image does not support arm64": "Selected image does not support arm64", - "ai:Self Node Remediation": "Self Node Remediation", "ai:Self Node Remediation requirements": "Self Node Remediation requirements", "ai:Serial": "Serial", "ai:Serial number": "Serial number", - "ai:Serverless": "Serverless", "ai:Serverless requirements": "Serverless requirements", "ai:Service CIDR": "Service CIDR", - "ai:Service Mesh": "Service Mesh", "ai:Service Mesh requirements": "Service Mesh requirements", "ai:Service network CIDR": "Service network CIDR", "ai:Service networks": "Service networks", diff --git a/libs/ui-lib-tests/cypress/integration/use-cases/create-cluster/with-mtv-operator.cy.ts b/libs/ui-lib-tests/cypress/integration/use-cases/create-cluster/with-mtv-operator.cy.ts index 4a25374bcb..e9610d3286 100644 --- a/libs/ui-lib-tests/cypress/integration/use-cases/create-cluster/with-mtv-operator.cy.ts +++ b/libs/ui-lib-tests/cypress/integration/use-cases/create-cluster/with-mtv-operator.cy.ts @@ -1,5 +1,6 @@ import { commonActions } from '../../../views/common'; import OperatorsForm from '../../../views/forms/Operators/OperatorsForm'; +import { operatorsPage } from '../../../views/operatorsPage'; describe(`Create cluster with MTV operator enabled`, () => { const setTestStartSignal = (activeSignal: string) => { @@ -15,6 +16,7 @@ describe(`Create cluster with MTV operator enabled`, () => { setTestStartSignal('CLUSTER_CREATED'); commonActions.visitClusterDetailsPage(); commonActions.startAtWizardStep('Operators'); + operatorsPage.singleOperatorsToggle().click(); }); describe('When the feature is enabled:', () => { @@ -28,8 +30,8 @@ describe(`Create cluster with MTV operator enabled`, () => { cy.wait('@update-cluster').then(({ request }) => { expect(request.body.olm_operators).to.deep.equal([ { name: 'cnv' }, - { name: 'mtv' }, { name: 'lso' }, + { name: 'mtv' }, ]); }); }); diff --git a/libs/ui-lib-tests/cypress/support/interceptors.ts b/libs/ui-lib-tests/cypress/support/interceptors.ts index b095035893..a2775ceeea 100644 --- a/libs/ui-lib-tests/cypress/support/interceptors.ts +++ b/libs/ui-lib-tests/cypress/support/interceptors.ts @@ -22,6 +22,7 @@ const getDay2ClusterApiPath = () => `${allClustersApiPath}${day2FlowIds.day2.aiC const day2InfraEnvDetailsUrl = new RegExp( `${getDay2InfraEnvApiPath(x86)}|${getDay2InfraEnvApiPath(arm)}`, ); +const getDay1ClusterPreflightRequirementsApiPath = () => `${allClustersApiPath}${Cypress.env('clusterId')}/preflight-requirements`; const getDay2InfraEnv = (cpuArch: Archs) => fixtures.day2InfraEnvs[cpuArch]; const getCpuArchitectureParam = (cpuArch: string): Archs | undefined => { @@ -207,6 +208,20 @@ const addClusterListIntercepts = () => { }); }; +const addPreflightRequirementsIntercepts = () => { + const clusterReqApiPath = getDay1ClusterPreflightRequirementsApiPath(); + cy.intercept('GET', clusterReqApiPath, { operators: [ + { + operatorName: 'mtv', + dependencies: ['cnv'] + }, + { + operatorName: 'cnv', + dependencies: ['lso'] + } + ] }).as('cluster-req'); +} + const addClusterPatchAndDetailsIntercepts = () => { const clusterApiPath = getDay1ClusterApiPath(); cy.intercept('GET', clusterApiPath, mockClusterResponse).as('cluster-details'); @@ -470,6 +485,7 @@ const loadDay1Intercepts = () => { addDay1InfraEnvIntercepts(); addDay1HostIntercepts(); addEventsIntercepts(); + addPreflightRequirementsIntercepts(); }; const loadDay2Intercepts = () => { diff --git a/libs/ui-lib-tests/cypress/support/variables/host-discovery.ts b/libs/ui-lib-tests/cypress/support/variables/host-discovery.ts index caf3893a8a..9f7d9ba610 100644 --- a/libs/ui-lib-tests/cypress/support/variables/host-discovery.ts +++ b/libs/ui-lib-tests/cypress/support/variables/host-discovery.ts @@ -38,8 +38,8 @@ Cypress.env( ); Cypress.env( 'useContainerNativeVirtualizationField', - '#form-checkbox-useContainerNativeVirtualization-field', + '#form-input-cnv-field', ); -Cypress.env('useOpenShiftDataFoundation', '#form-checkbox-useOpenShiftDataFoundation-field'); +Cypress.env('useOpenShiftDataFoundation', '#form-input-odf-field'); Cypress.env('useOdfLogicalVolumeManagerField', '#form-checkbox-useOdfLogicalVolumeManager-field'); Cypress.env('integrateWithVsphere', 'Integrate with vSphere'); diff --git a/libs/ui-lib-tests/cypress/views/bareMetalDiscovery.ts b/libs/ui-lib-tests/cypress/views/bareMetalDiscovery.ts index abf30f8d5f..26f2a0cc00 100644 --- a/libs/ui-lib-tests/cypress/views/bareMetalDiscovery.ts +++ b/libs/ui-lib-tests/cypress/views/bareMetalDiscovery.ts @@ -21,9 +21,6 @@ export const bareMetalDiscoveryPage = { getOdfOperator: () => { return cy.get(Cypress.env('useOpenShiftDataFoundation')); }, - getLvmOperator: () => { - return cy.get(Cypress.env('useOdfLogicalVolumeManagerField')); - }, platformIntegration: { getFullPotentialHint: () => { return cy.get('[data-testid="discover-platform-integration-hosts"]', { timeout: 16000 }); diff --git a/libs/ui-lib-tests/cypress/views/forms/Operators/OperatorsForm.ts b/libs/ui-lib-tests/cypress/views/forms/Operators/OperatorsForm.ts index d9f358494b..beedbf0943 100644 --- a/libs/ui-lib-tests/cypress/views/forms/Operators/OperatorsForm.ts +++ b/libs/ui-lib-tests/cypress/views/forms/Operators/OperatorsForm.ts @@ -15,7 +15,7 @@ export default class OperatorsForm { /** @private */ class MceOperatorControl { static get body() { - return OperatorsForm.body.find('#form-control__form-checkbox-useMultiClusterEngine-field'); + return OperatorsForm.body.find('#form-control__form-input-mce-field'); } static findPopoverButton() { @@ -25,7 +25,7 @@ class MceOperatorControl { } static findPopoverContent() { - return MceOperatorControl.body.scrollIntoView().get('#popover-useMultiClusterEngine-body'); + return MceOperatorControl.body.scrollIntoView().get('#popover-mce-body'); } static findLabel() { @@ -43,7 +43,7 @@ class MceOperatorControl { class MtvOperatorControl { static get body() { return OperatorsForm.body.find( - '#form-control__form-checkbox-useMigrationToolkitforVirtualization-field', + '#form-control__form-input-mtv-field', ); } @@ -56,7 +56,7 @@ class MtvOperatorControl { static findPopoverContent() { return MtvOperatorControl.body .scrollIntoView() - .get('#popover-useMigrationToolkitforVirtualization-body'); + .get('#popover-mtv-body'); } static findLabel() { diff --git a/libs/ui-lib-tests/cypress/views/operatorsPage.ts b/libs/ui-lib-tests/cypress/views/operatorsPage.ts index 00f6a6d2cb..421a47e72d 100644 --- a/libs/ui-lib-tests/cypress/views/operatorsPage.ts +++ b/libs/ui-lib-tests/cypress/views/operatorsPage.ts @@ -1,6 +1,6 @@ export const operatorsPage = { openshiftVirtualization: () => { - return cy.get('#form-checkbox-useContainerNativeVirtualization-field'); + return cy.get('#form-input-cnv-field'); }, singleOperatorsToggle: () => { return cy.contains('Single Operators '); diff --git a/libs/ui-lib/lib/cim/components/featureSupportLevels/ACMFeatureSupportLevelProvider.tsx b/libs/ui-lib/lib/cim/components/featureSupportLevels/ACMFeatureSupportLevelProvider.tsx index cf2c7ede36..22c9c8ace5 100644 --- a/libs/ui-lib/lib/cim/components/featureSupportLevels/ACMFeatureSupportLevelProvider.tsx +++ b/libs/ui-lib/lib/cim/components/featureSupportLevels/ACMFeatureSupportLevelProvider.tsx @@ -9,9 +9,10 @@ import { import { ClusterImageSetK8sResource } from '../../types'; import { featureSupportLevelsACM } from '../../config/constants'; import { getFeatureDisabledReason, isFeatureSupported } from './featureStateUtils'; -import { getOCPVersions, getVersionFromReleaseImage, getMajorMinorVersion } from '../helpers'; +import { getOCPVersions, getVersionFromReleaseImage } from '../helpers'; import { useTranslation } from '../../../common/hooks/use-translation-wrapper'; import { SupportLevel } from '@openshift-assisted/types/assisted-installer-service'; +import { getMajorMinorVersion } from '../../../common/utils'; export type ACMFeatureSupportLevelProvider = PropsWithChildren<{ clusterImages: ClusterImageSetK8sResource[]; diff --git a/libs/ui-lib/lib/cim/components/helpers/versions.ts b/libs/ui-lib/lib/cim/components/helpers/versions.ts index 24f68a3f98..e4a68011fb 100644 --- a/libs/ui-lib/lib/cim/components/helpers/versions.ts +++ b/libs/ui-lib/lib/cim/components/helpers/versions.ts @@ -8,6 +8,7 @@ import { } from '../../types'; import { CpuArchitecture, OpenshiftVersionOptionType } from '../../../common'; import { OpenshiftVersion } from '@openshift-assisted/types/assisted-installer-service'; +import { getMajorMinorVersion } from '../../../common/utils'; export const getVersionFromReleaseImage = (releaseImage = '') => { const match = /.+:(.*)/gm.exec(releaseImage); @@ -120,11 +121,6 @@ export const getSelectedVersion = ( export const getCurrentClusterVersion = (cv?: ClusterVersionK8sResource): string | undefined => cv?.status?.history?.[0]?.version || cv?.spec?.desiredUpdate?.version; -export const getMajorMinorVersion = (version = '') => { - const match = /[0-9].[0-9][0-9]?/g.exec(version); - return match?.[0] || ''; -}; - export const getNetworkType = ( ocpVersion: OpenshiftVersionOptionType | undefined, ): 'OVNKubernetes' | 'OpenShiftSDN' => diff --git a/libs/ui-lib/lib/common/components/clusterDetail/ClusterProgressItems.tsx b/libs/ui-lib/lib/common/components/clusterDetail/ClusterProgressItems.tsx index 8c93bc5226..606b22c814 100644 --- a/libs/ui-lib/lib/common/components/clusterDetail/ClusterProgressItems.tsx +++ b/libs/ui-lib/lib/common/components/clusterDetail/ClusterProgressItems.tsx @@ -50,10 +50,7 @@ const ClusterProgressItems = ({ 0}> - + diff --git a/libs/ui-lib/lib/common/components/clusterDetail/OperatorsProgressItem.tsx b/libs/ui-lib/lib/common/components/clusterDetail/OperatorsProgressItem.tsx index 5a7310a5ac..95555fa7d9 100644 --- a/libs/ui-lib/lib/common/components/clusterDetail/OperatorsProgressItem.tsx +++ b/libs/ui-lib/lib/common/components/clusterDetail/OperatorsProgressItem.tsx @@ -9,15 +9,13 @@ import { global_success_color_100 as okColor } from '@patternfly/react-tokens/di import { pluralize } from 'humanize-plus'; import { TFunction } from 'i18next'; import { - Cluster, MonitoredOperator, MonitoredOperatorsList, OperatorStatus, } from '@openshift-assisted/types/assisted-installer-service'; -import { operatorLabelsCim, OperatorName } from '../../config'; import ClusterProgressItem from './ClusterProgressItem'; import { useTranslation } from '../../hooks/use-translation-wrapper'; -import { useFeatureSupportLevel } from '../featureSupportLevels'; +import { useOperatorSpecs } from '../operators/operatorSpecs'; import './OperatorsProgressItem.css'; @@ -74,16 +72,15 @@ export function getOperatorsIcon(status: OperatorStatus | 'pending') { type OperatorListProps = { operators: MonitoredOperatorsList; - openshiftVersion: Cluster['openshiftVersion']; }; type OperatorsPopoverProps = OperatorListProps & { children: React.ComponentProps['children']; }; -const OperatorsPopover = ({ operators, openshiftVersion, children }: OperatorsPopoverProps) => { +const OperatorsPopover = ({ operators, children }: OperatorsPopoverProps) => { const { t } = useTranslation(); - const featureSupportLevel = useFeatureSupportLevel(); + const opSpecs = useOperatorSpecs(); return ( {name} {status} @@ -115,14 +109,14 @@ const OperatorsPopover = ({ operators, openshiftVersion, children }: OperatorsPo ); }; -const OperatorsProgressItem = ({ operators, openshiftVersion }: OperatorListProps) => { +const OperatorsProgressItem = ({ operators }: OperatorListProps) => { const { t } = useTranslation(); const icon = getOperatorsIcon(getAggregatedStatus(operators)); const label = getOperatorsLabel(operators, t); return ( <> - + diff --git a/libs/ui-lib/lib/common/components/clusterWizard/types.ts b/libs/ui-lib/lib/common/components/clusterWizard/types.ts index 87ef044bd1..49678be2e8 100644 --- a/libs/ui-lib/lib/common/components/clusterWizard/types.ts +++ b/libs/ui-lib/lib/common/components/clusterWizard/types.ts @@ -69,9 +69,9 @@ export type ValidationActionLinkProps = { wizardStepNames: { [key in S]: string }; }; -export type ClusterOperatorProps = Pick & { - clusterId: Cluster['id']; -} & { monitoredOperators?: Cluster['monitoredOperators'] }; +export type ClusterOperatorProps = { + cluster: Cluster; +}; export type ItemDropdown = { label: string; diff --git a/libs/ui-lib/lib/common/components/newFeatureSupportLevels/types.ts b/libs/ui-lib/lib/common/components/newFeatureSupportLevels/types.ts index 214502e83d..951b87c2e1 100644 --- a/libs/ui-lib/lib/common/components/newFeatureSupportLevels/types.ts +++ b/libs/ui-lib/lib/common/components/newFeatureSupportLevels/types.ts @@ -31,6 +31,9 @@ export type NewFeatureSupportLevelData = { getFeatureSupportLevel: GetFeatureSupportLevel; isFeatureDisabled(featureId: FeatureId, supportLevelData?: NewFeatureSupportLevelMap): boolean; getFeatureDisabledReason: GetFeatureDisabledReason; - isFeatureSupported(featureId: FeatureId, supportLevelData?: NewFeatureSupportLevelMap): boolean; + isFeatureSupported: ( + featureId: FeatureId, + supportLevelData?: NewFeatureSupportLevelMap, + ) => boolean; activeFeatureConfiguration?: ActiveFeatureConfiguration; }; diff --git a/libs/ui-lib/lib/common/components/operators/operatorSpecs.tsx b/libs/ui-lib/lib/common/components/operators/operatorSpecs.tsx new file mode 100644 index 0000000000..5cd8d2d71c --- /dev/null +++ b/libs/ui-lib/lib/common/components/operators/operatorSpecs.tsx @@ -0,0 +1,343 @@ +import React from 'react'; +import { Cluster } from '@openshift-assisted/types/assisted-installer-service'; +import { FeatureId } from '../../types'; +import { + OPERATOR_NAME_AMD_GPU, + OPERATOR_NAME_AUTHORINO, + OPERATOR_NAME_CNV, + OPERATOR_NAME_KMM, + OPERATOR_NAME_LSO, + OPERATOR_NAME_LVM, + OPERATOR_NAME_MCE, + OPERATOR_NAME_MTV, + OPERATOR_NAME_NMSTATE, + OPERATOR_NAME_NODE_FEATURE_DISCOVERY, + OPERATOR_NAME_NVIDIA_GPU, + OPERATOR_NAME_ODF, + OPERATOR_NAME_OPENSHIFT_AI, + OPERATOR_NAME_OSC, + OPERATOR_NAME_PIPELINES, + OPERATOR_NAME_SERVERLESS, + OPERATOR_NAME_SERVICEMESH, + OPERATOR_NAME_NODE_HEALTHCHECK, + OPERATOR_NAME_SELF_NODE_REMEDIATION, + OPERATOR_NAME_FENCE_AGENTS_REMEDIATION, + OPERATOR_NAME_NODE_MAINTENANCE, + OPERATOR_NAME_KUBE_DESCHEDULER, +} from '../../config/constants'; +import { ExternalLink } from '../ui'; +import { + AUTHORINO_OPERATOR_LINK, + CNV_LINK, + FENCE_AGENTS_REMEDIATION_LINK, + getLsoLink, + getLvmsDocsLink, + getMceDocsLink, + getNmstateLink, + getNodeFeatureDiscoveryLink, + getNvidiaGpuLink, + getServiceMeshLink, + KMM_LINK, + MTV_LINK, + NODE_HEALTHCHECK_LINK, + ODF_LINK, + ODF_REQUIREMENTS_LINK, + OPENSHIFT_AI_LINK, + OPENSHIFT_AI_REQUIREMENTS_LINK, + OSC_LINK, + OSC_REQUIREMENTS_LINK, + PIPELINES_OPERATOR_LINK, + SELF_NODE_REMEDIATION_LINK, + SERVERLESS_OPERATOR_LINK, +} from '../../config'; +import { getMajorMinorVersion } from '../../utils'; +import { useNewFeatureSupportLevel } from '../newFeatureSupportLevels'; + +export const isOCPVersionEqualsOrMore = ( + openshiftVersion: string, + ocpVersionToCompare: string, +): boolean => { + const [majorA, minorA] = getMajorMinorVersion(openshiftVersion).split('.'); + const [majorB, minorB] = ocpVersionToCompare.split('.'); + return ( + Number(majorA) > Number(majorB) || + (Number(majorA) === Number(majorB) && Number(minorA) >= Number(minorB)) + ); +}; + +export type OperatorSpec = { + title: string; + featureId: FeatureId; + Description?: React.ComponentType<{ openshiftVersion?: string }>; + notStandalone?: boolean; + Requirements?: React.ComponentType<{ cluster: Cluster }>; +}; + +export const getOperatorSpecs = (useLVMS?: boolean): { [key: string]: OperatorSpec } => { + return { + [OPERATOR_NAME_MTV]: { + title: 'Migration Toolkit for Virtualization', + featureId: 'MTV', + Description: () => ( + <> + This Toolkit (MTV) enables you to migrate virtual machines from VMware vSphere, Red Hat + Virtualization, or OpenStack to OpenShift Virtualization running on Red Hat OpenShift.{' '} + Learn more + + ), + }, + [OPERATOR_NAME_AMD_GPU]: { + title: 'AMD GPU', + featureId: 'AMD_GPU', + Requirements: () => <>Requires at least one supported AMD GPU, + Description: () => ( + <> + Automate the management of AMD software components needed to provision and monitor GPUs. + + ), + }, + [OPERATOR_NAME_LSO]: { + title: 'Local Storage Operator', + featureId: 'LSO', + Description: ({ openshiftVersion }) => ( + <> + Allows provisioning of persistent storage by using local volumes.{' '} + Learn more + + ), + notStandalone: true, + }, + [OPERATOR_NAME_AUTHORINO]: { + title: 'Authorino', + featureId: 'AUTHORINO', + Description: () => ( + <> + Lightweight external authorization service for tailor-made Zero Trust API security.{' '} + Learn more + + ), + notStandalone: true, + }, + [OPERATOR_NAME_CNV]: { + title: 'OpenShift Virtualization', + featureId: 'CNV', + Requirements: () => ( + <>Enabled CPU virtualization support in BIOS (Intel-VT / AMD-V) on all nodes + ), + Description: () => ( + <> + Run virtual machines alongside containers on one platform.{' '} + Learn more + + ), + }, + [OPERATOR_NAME_FENCE_AGENTS_REMEDIATION]: { + title: 'Fence Agents Remediation', + featureId: 'FENCE_AGENTS_REMEDIATION', + Description: () => ( + <> + Externally fences failed nodes using power controllers.{' '} + Learn more + + ), + notStandalone: true, + }, + [OPERATOR_NAME_KMM]: { + title: 'Kernel Module Management', + featureId: 'KMM', + Description: () => ( + <> + Management of kernel modules. Learn more + + ), + }, + [OPERATOR_NAME_KUBE_DESCHEDULER]: { + title: 'Kube Descheduler', + featureId: 'KUBE_DESCHEDULER', + Description: () => ( + <> + Evicts pods to reschedule them onto more suitable nodes.{' '} + Learn more + + ), + notStandalone: true, + }, + [OPERATOR_NAME_MCE]: { + title: 'Multicluster engine', + featureId: 'MCE', + Description: ({ openshiftVersion }) => ( + <> + Create, import, and manage multiple clusters from this cluster.{' '} + Learn more + + ), + }, + [OPERATOR_NAME_NMSTATE]: { + title: 'NMState', + featureId: 'NMSTATE', + Description: ({ openshiftVersion }) => ( + <> + Provides users with functionality to configure various network interface types, DNS, and + routing on cluster nodes.{' '} + Learn more + + ), + }, + [OPERATOR_NAME_NODE_FEATURE_DISCOVERY]: { + title: 'Node Feature Discovery', + featureId: 'NODE_FEATURE_DISCOVERY', + Description: ({ openshiftVersion }) => ( + <> + Manage the detection of hardware features and configuration by labeling nodes with + hardware-specific information.{' '} + + Learn more + + + ), + }, + [OPERATOR_NAME_NODE_HEALTHCHECK]: { + title: 'Node Healthcheck', + featureId: 'NODE_HEALTHCHECK', + Description: () => ( + <> + Identify Unhealthy Nodes.{' '} + Learn more + + ), + notStandalone: true, + }, + [OPERATOR_NAME_NODE_MAINTENANCE]: { + title: 'Node Maintenance', + featureId: 'NODE_MAINTENANCE', + Description: () => ( + <> + Place nodes in maintenance mode.{' '} + Learn more + + ), + notStandalone: true, + }, + [OPERATOR_NAME_NVIDIA_GPU]: { + title: 'NVIDIA GPU', + featureId: 'NVIDIA_GPU', + Requirements: () => <>Requires at least one supported NVIDIA GPU, + Description: ({ openshiftVersion }) => ( + <> + Automate the management of NVIDIA software components needed to provision and monitor + GPUs. Learn more + + ), + }, + [OPERATOR_NAME_ODF]: { + title: 'OpenShift Data Foundation', + featureId: 'ODF', + Requirements: () => ( + + Learn more about the requirements for OpenShift Data Foundation + + ), + Description: () => ( + <> + Persistent software-defined storage for hybrid applications.{' '} + Learn more + + ), + }, + [OPERATOR_NAME_OPENSHIFT_AI]: { + title: 'OpenShift AI', + featureId: 'OPENSHIFT_AI', + Requirements: () => ( + Learn more + ), + Description: () => ( + <> + Train, serve, monitor and manage AI/ML models and applications.{' '} + Learn more + + ), + }, + [OPERATOR_NAME_OSC]: { + title: 'OpenShift sandboxed containers', + featureId: 'OSC', + Requirements: () => ( + + Learn more about the requirements for OpenShift sandboxed containers + + ), + Description: () => ( + <> + OpenShift sandboxed containers support for OpenShift Container Platform provides users + with built-in support for running Kata Containers as an additional optional runtime. It + provides an additional virtualization machine(VM) isolation layer for pods.{' '} + Learn more + + ), + }, + [OPERATOR_NAME_PIPELINES]: { + title: 'Pipelines', + featureId: 'PIPELINES', + Description: () => ( + <> + Cloud-native continuous integration and delivery (CI/CD) solution for building pipelines + using Tekton. Learn more + + ), + notStandalone: true, + }, + [OPERATOR_NAME_SELF_NODE_REMEDIATION]: { + title: 'Self Node Remediation', + featureId: 'SELF_NODE_REMEDIATION', + Description: () => ( + <> + Allows nodes to reboot themselves when they become unhealthy.{' '} + Learn more + + ), + notStandalone: true, + }, + [OPERATOR_NAME_SERVERLESS]: { + title: 'Serverless', + featureId: 'SERVERLESS', + Description: () => ( + <> + Deploy workflow applications based on the CNCF Serverless Workflow specification.{' '} + Learn more + + ), + notStandalone: true, + }, + [OPERATOR_NAME_SERVICEMESH]: { + title: 'Service Mesh', + featureId: 'SERVICEMESH', + Description: ({ openshiftVersion }) => ( + <> + Platform that provides behavioral insight and operational control over a service mesh.{' '} + Learn more + + ), + notStandalone: true, + }, + [OPERATOR_NAME_LVM]: { + title: useLVMS ? 'Logical Volume Manager Storage' : 'Logical Volume Manager', + featureId: 'LVM', + Description: ({ openshiftVersion }) => + useLVMS ? ( + <> + Storage virtualization that offers a more flexible approach for disk space management.{' '} + Learn more + + ) : ( + <> + Storage virtualization that offers a more flexible approach for disk space management. + + ), + }, + }; +}; + +export const useOperatorSpecs = () => { + const { getFeatureSupportLevel } = useNewFeatureSupportLevel(); + const useLVMS = getFeatureSupportLevel('LVM') === 'supported'; + return React.useMemo(() => getOperatorSpecs(useLVMS), [useLVMS]); +}; diff --git a/libs/ui-lib/lib/common/config/constants.ts b/libs/ui-lib/lib/common/config/constants.ts index 2029663cda..998d165a5a 100644 --- a/libs/ui-lib/lib/common/config/constants.ts +++ b/libs/ui-lib/lib/common/config/constants.ts @@ -8,13 +8,8 @@ import { HostValidationId, } from '@openshift-assisted/types/assisted-installer-service'; import { ValidationGroup as ClusterValidationGroup } from '../types/clusters'; -import { FeatureSupportLevelData } from '../components/featureSupportLevels/FeatureSupportLevelContext'; -import type { NewFeatureSupportLevelData } from '../components/newFeatureSupportLevels'; import buildManifest from '@openshift-assisted/ui-lib/package.json'; -// TODO(mlibra): Retrieve branding dynamically, if needed, i.e. via injecting to the "window" object -export const getProductBrandingCode = () => 'redhat'; - export const POLLING_INTERVAL = 10 * 1000; export const EVENTS_POLLING_INTERVAL = 10 * 1000 * 6; @@ -303,7 +298,6 @@ export const OPERATOR_NAME_CNV = 'cnv'; export const OPERATOR_NAME_LSO = 'lso'; export const OPERATOR_NAME_ODF = 'odf'; export const OPERATOR_NAME_LVM = 'lvm'; -export const OPERATOR_NAME_LVMS = 'lvms'; export const OPERATOR_NAME_MCE = 'mce'; export const OPERATOR_NAME_MTV = 'mtv'; export const OPERATOR_NAME_NODE_FEATURE_DISCOVERY = 'node-feature-discovery'; @@ -323,108 +317,20 @@ export const OPERATOR_NAME_FENCE_AGENTS_REMEDIATION = 'fence-agents-remediation' export const OPERATOR_NAME_NODE_MAINTENANCE = 'node-maintenance'; export const OPERATOR_NAME_KUBE_DESCHEDULER = 'kube-descheduler'; -const OperatorNames = [ +export const singleClusterOperators = [ OPERATOR_NAME_CNV, - OPERATOR_NAME_LSO, - OPERATOR_NAME_ODF, - OPERATOR_NAME_LVM, - OPERATOR_NAME_LVMS, - OPERATOR_NAME_MCE, - OPERATOR_NAME_MTV, - OPERATOR_NAME_NODE_FEATURE_DISCOVERY, - OPERATOR_NAME_NVIDIA_GPU, - OPERATOR_NAME_PIPELINES, - OPERATOR_NAME_SERVICEMESH, - OPERATOR_NAME_SERVERLESS, - OPERATOR_NAME_OPENSHIFT_AI, - OPERATOR_NAME_OSC, - OPERATOR_NAME_NMSTATE, - OPERATOR_NAME_AUTHORINO, - OPERATOR_NAME_AMD_GPU, - OPERATOR_NAME_KMM, OPERATOR_NAME_NODE_HEALTHCHECK, - OPERATOR_NAME_SELF_NODE_REMEDIATION, OPERATOR_NAME_FENCE_AGENTS_REMEDIATION, OPERATOR_NAME_NODE_MAINTENANCE, - OPERATOR_NAME_KUBE_DESCHEDULER, -]; -export const ExposedOperatorNames = [ - OPERATOR_NAME_CNV, - OPERATOR_NAME_ODF, - OPERATOR_NAME_LVM, - OPERATOR_NAME_LVMS, - OPERATOR_NAME_MCE, OPERATOR_NAME_MTV, - OPERATOR_NAME_NODE_FEATURE_DISCOVERY, - OPERATOR_NAME_NVIDIA_GPU, - OPERATOR_NAME_PIPELINES, - OPERATOR_NAME_SERVICEMESH, - OPERATOR_NAME_SERVERLESS, - OPERATOR_NAME_OPENSHIFT_AI, - OPERATOR_NAME_OSC, + OPERATOR_NAME_KUBE_DESCHEDULER, OPERATOR_NAME_NMSTATE, - OPERATOR_NAME_AUTHORINO, - OPERATOR_NAME_AMD_GPU, - OPERATOR_NAME_KMM, - OPERATOR_NAME_NODE_HEALTHCHECK, OPERATOR_NAME_SELF_NODE_REMEDIATION, - OPERATOR_NAME_FENCE_AGENTS_REMEDIATION, - OPERATOR_NAME_NODE_MAINTENANCE, - OPERATOR_NAME_KUBE_DESCHEDULER, + OPERATOR_NAME_ODF, + OPERATOR_NAME_LSO, ]; -export type OperatorName = (typeof OperatorNames)[number]; -export type ExposedOperatorName = (typeof ExposedOperatorNames)[number]; - -export const operatorLabelsCim = ( - t: TFunction, - openshiftVersion: Cluster['openshiftVersion'], - featureSupportLevel: FeatureSupportLevelData, -): { [key in ExposedOperatorName]: string } => { - const useLVMS = - featureSupportLevel.getFeatureSupportLevel(openshiftVersion || '', 'LVM') === 'supported'; - - return { - [OPERATOR_NAME_ODF]: t('ai:OpenShift Data Foundation'), - [OPERATOR_NAME_CNV]: t('ai:OpenShift Virtualization'), - [OPERATOR_NAME_LVM]: useLVMS - ? t('ai:Logical Volume Manager Storage') - : t('ai:Logical Volume Manager'), - }; -}; - -export const operatorLabels = ( - t: TFunction, - featureSupportLevel: NewFeatureSupportLevelData, -): { [key in ExposedOperatorName]: string } => { - const useLVMS = featureSupportLevel.getFeatureSupportLevel('LVM') === 'supported'; - - return { - [OPERATOR_NAME_ODF]: t('ai:OpenShift Data Foundation'), - [OPERATOR_NAME_CNV]: t('ai:OpenShift Virtualization'), - [OPERATOR_NAME_LVM]: useLVMS - ? t('ai:Logical Volume Manager Storage') - : t('ai:Logical Volume Manager'), - [OPERATOR_NAME_MCE]: t('ai:Multicluster engine'), - [OPERATOR_NAME_NODE_FEATURE_DISCOVERY]: t('ai:Node Feature Discovery'), - [OPERATOR_NAME_NVIDIA_GPU]: t('ai:NVIDIA GPU'), - [OPERATOR_NAME_PIPELINES]: t('ai:Pipelines'), - [OPERATOR_NAME_SERVICEMESH]: t('ai:Service Mesh'), - [OPERATOR_NAME_SERVERLESS]: t('ai:Serverless'), - [OPERATOR_NAME_OPENSHIFT_AI]: t('ai:OpenShift AI'), - [OPERATOR_NAME_OSC]: t('ai:OpenShift sandboxed containers'), - [OPERATOR_NAME_MTV]: t('ai:Migration Toolkit for Virtualization'), - [OPERATOR_NAME_NMSTATE]: t('ai:NMState'), - [OPERATOR_NAME_AUTHORINO]: t('ai:Authorino'), - [OPERATOR_NAME_AMD_GPU]: t('ai:AMD GPU'), - [OPERATOR_NAME_KMM]: t('ai:Kernel Module Management'), - [OPERATOR_NAME_NODE_HEALTHCHECK]: t('ai:Node Healthcheck'), - [OPERATOR_NAME_SELF_NODE_REMEDIATION]: t('ai:Self Node Remediation'), - [OPERATOR_NAME_FENCE_AGENTS_REMEDIATION]: t('ai:Fence Agents Remediation'), - [OPERATOR_NAME_NODE_MAINTENANCE]: t('ai:Node Maintenace'), - [OPERATOR_NAME_KUBE_DESCHEDULER]: t('ai:Kube Descheduler'), - }; -}; +export const singleClusterBundles = ['virtualization']; export const AI_UI_TAG = 'ui_ocm'; diff --git a/libs/ui-lib/lib/common/selectors/clusterSelectors.ts b/libs/ui-lib/lib/common/selectors/clusterSelectors.ts index afe53379a7..583e99fd4f 100644 --- a/libs/ui-lib/lib/common/selectors/clusterSelectors.ts +++ b/libs/ui-lib/lib/common/selectors/clusterSelectors.ts @@ -1,7 +1,6 @@ import head from 'lodash-es/head.js'; import { SupportedPlatformIntegrations, ValidationsInfo } from '../types'; import { Cluster, Ip } from '@openshift-assisted/types/assisted-installer-service'; -import { ExposedOperatorName } from '../config'; import { stringToJSON } from '../utils'; export const selectMachineNetworkCIDR = ({ @@ -46,7 +45,7 @@ export const selectOlmOperators = (cluster?: Pick export const hasEnabledOperators = ( monitoredOperators: Cluster['monitoredOperators'], - searchOperator: ExposedOperatorName, + searchOperator: string, ) => { return selectMonitoredOperators(monitoredOperators).some( (operator) => operator.name && operator.name === searchOperator, diff --git a/libs/ui-lib/lib/common/types/clusters.ts b/libs/ui-lib/lib/common/types/clusters.ts index d3d0844551..1771ec1235 100644 --- a/libs/ui-lib/lib/common/types/clusters.ts +++ b/libs/ui-lib/lib/common/types/clusters.ts @@ -58,29 +58,9 @@ export type HostDiscoveryValues = V2ClusterUpdateParams & { export type StorageValues = V2ClusterUpdateParams & { nodeLabeling: string; }; -export type OperatorsValues = V2ClusterUpdateParams & { - useOpenShiftDataFoundation: boolean; - useOdfLogicalVolumeManager: boolean; - useContainerNativeVirtualization: boolean; - useMultiClusterEngine: boolean; - useMigrationToolkitforVirtualization: boolean; - useOpenShiftAI: boolean; - useOsc: boolean; - useNodeFeatureDiscovery: boolean; - useNmstate: boolean; - useLso: boolean; - useServerless: boolean; - useAuthorino: boolean; - usePipelines: boolean; - useServicemesh: boolean; - useNvidiaGpu: boolean; - useAmdGpu: boolean; - useKmm: boolean; - useNodeHealthcheck: boolean; - useSelfNodeRemediation: boolean; - useFenceAgentsRemediation: boolean; - useNodeMaintenance: boolean; - useKubeDescheduler: boolean; +export type OperatorsValues = { + selectedBundles: string[]; + selectedOperators: string[]; }; export type SupportedPlatformType = Extract; diff --git a/libs/ui-lib/lib/common/utils.ts b/libs/ui-lib/lib/common/utils.ts index a103de3502..4eae7e6047 100644 --- a/libs/ui-lib/lib/common/utils.ts +++ b/libs/ui-lib/lib/common/utils.ts @@ -106,3 +106,8 @@ export const downloadFile = (fileUrl?: string, dataBlob?: Blob, fileName?: strin link.click(); document.body.removeChild(link); }; + +export const getMajorMinorVersion = (version = '') => { + const match = /[0-9].[0-9][0-9]?/g.exec(version); + return match?.[0] || ''; +}; diff --git a/libs/ui-lib/lib/ocm/components/clusterConfiguration/DiscoveryImageModal.tsx b/libs/ui-lib/lib/ocm/components/clusterConfiguration/DiscoveryImageModal.tsx index cb037f9583..07eab66e3e 100644 --- a/libs/ui-lib/lib/ocm/components/clusterConfiguration/DiscoveryImageModal.tsx +++ b/libs/ui-lib/lib/ocm/components/clusterConfiguration/DiscoveryImageModal.tsx @@ -120,7 +120,7 @@ export const DiscoveryImageModal = () => { isSNO={isSNOCluster} onReset={onReset} onClose={close} - updateTagsForCiscoIntersight={() => updateTagsForCiscoIntersight(cluster)} + updateTagsForCiscoIntersight={() => void updateTagsForCiscoIntersight(cluster)} /> ) : ipxeDownloadUrl ? ( 0}> - + diff --git a/libs/ui-lib/lib/ocm/components/clusterConfiguration/OcmOperatorProgressItem.tsx b/libs/ui-lib/lib/ocm/components/clusterConfiguration/OcmOperatorProgressItem.tsx index 7c30f60558..6b258dd3af 100644 --- a/libs/ui-lib/lib/ocm/components/clusterConfiguration/OcmOperatorProgressItem.tsx +++ b/libs/ui-lib/lib/ocm/components/clusterConfiguration/OcmOperatorProgressItem.tsx @@ -12,14 +12,12 @@ import { TFunction } from 'i18next'; import './OcmOperatorsProgressItem.css'; import { useTranslation } from '../../../common/hooks/use-translation-wrapper'; import ClusterProgressItem from '../../../common/components/clusterDetail/ClusterProgressItem'; -import { operatorLabels, OperatorName } from '../../../common'; -import { useNewFeatureSupportLevel } from '../../../common/components/newFeatureSupportLevels'; import { - Cluster, MonitoredOperator, MonitoredOperatorsList, OperatorStatus, } from '@openshift-assisted/types/assisted-installer-service'; +import { useOperatorSpecs } from '../../../common/components/operators/operatorSpecs'; export function getAggregatedStatus(operators: MonitoredOperatorsList) { const operatorStates: (OperatorStatus | 'pending')[] = operators.map( @@ -74,7 +72,6 @@ export function getOperatorsIcon(status: OperatorStatus | 'pending') { type OperatorListProps = { operators: MonitoredOperatorsList; - openshiftVersion: Cluster['openshiftVersion']; }; type OperatorsPopoverProps = OperatorListProps & { @@ -83,7 +80,7 @@ type OperatorsPopoverProps = OperatorListProps & { const OperatorsPopover = ({ operators, children }: OperatorsPopoverProps) => { const { t } = useTranslation(); - const featureSupportLevel = useNewFeatureSupportLevel(); + const opSpecs = useOperatorSpecs(); return ( { if (operator.status === 'available') { status = 'installed'; } - const name = - operatorLabels(t, featureSupportLevel)[operator.name as OperatorName] || - operator.name; + const name = opSpecs[operator.name as string]?.title || operator.name; return ( {name} {status} @@ -114,14 +109,14 @@ const OperatorsPopover = ({ operators, children }: OperatorsPopoverProps) => { ); }; -const OcmOperatorsProgressItem = ({ operators, openshiftVersion }: OperatorListProps) => { +const OcmOperatorsProgressItem = ({ operators }: OperatorListProps) => { const { t } = useTranslation(); const icon = getOperatorsIcon(getAggregatedStatus(operators)); const label = getOperatorsLabel(operators, t); return ( <> - + diff --git a/libs/ui-lib/lib/ocm/components/clusterConfiguration/operators/AmdGpuCheckbox.tsx b/libs/ui-lib/lib/ocm/components/clusterConfiguration/operators/AmdGpuCheckbox.tsx deleted file mode 100644 index 9063c543b8..0000000000 --- a/libs/ui-lib/lib/ocm/components/clusterConfiguration/operators/AmdGpuCheckbox.tsx +++ /dev/null @@ -1,64 +0,0 @@ -import React from 'react'; -import { FormGroup, HelperText, HelperTextItem, Tooltip } from '@patternfly/react-core'; -import { getFieldId, PopoverIcon } from '../../../../common'; -import { OcmCheckboxField } from '../../ui/OcmFormFields'; -import NewFeatureSupportLevelBadge from '../../../../common/components/newFeatureSupportLevels/NewFeatureSupportLevelBadge'; -import { SupportLevel } from '@openshift-assisted/types/./assisted-installer-service'; - -const AMDGPU_FIELD_NAME = 'useAmdGpu'; - -const AmdGpuLabel = ({ - disabledReason, - supportLevel, -}: { - disabledReason?: string; - supportLevel?: SupportLevel; -}) => { - return ( - <> - - - - - ); -}; - -const AmdGpuHelperText = () => { - return ( - - - Automate the management of AMD software components needed to provision and monitor GPUs.{' '} - - - ); -}; - -const AmdGpuCheckbox = ({ - disabledReason, - supportLevel, -}: { - disabledReason?: string; - supportLevel?: SupportLevel | undefined; -}) => { - const fieldId = getFieldId(AMDGPU_FIELD_NAME, 'input'); - - return ( - - } - helperText={} - isDisabled={!!disabledReason} - /> - - ); -}; - -export default AmdGpuCheckbox; diff --git a/libs/ui-lib/lib/ocm/components/clusterConfiguration/operators/AuthorinoCheckbox.tsx b/libs/ui-lib/lib/ocm/components/clusterConfiguration/operators/AuthorinoCheckbox.tsx deleted file mode 100644 index e6e5a61d82..0000000000 --- a/libs/ui-lib/lib/ocm/components/clusterConfiguration/operators/AuthorinoCheckbox.tsx +++ /dev/null @@ -1,63 +0,0 @@ -import React from 'react'; -import { FormGroup, HelperText, HelperTextItem, Tooltip } from '@patternfly/react-core'; -import { AUTHORINO_OPERATOR_LINK, getFieldId } from '../../../../common'; -import { OcmCheckboxField } from '../../ui/OcmFormFields'; -import NewFeatureSupportLevelBadge from '../../../../common/components/newFeatureSupportLevels/NewFeatureSupportLevelBadge'; -import { SupportLevel } from '@openshift-assisted/types/./assisted-installer-service'; -import { ExternalLinkAltIcon } from '@patternfly/react-icons/dist/js/icons/external-link-alt-icon'; - -const AUTHORINO_FIELD_NAME = 'useAuthorino'; - -const AuthorinoLabel = ({ - disabledReason, - supportLevel, -}: { - disabledReason?: string; - supportLevel?: SupportLevel; -}) => { - return ( - <> - - - - - ); -}; - -const AuthorinoHelperText = () => { - return ( - - - Lightweigth external authorization service for tailor-made Zero Trust API security.{' '} - - {'Learn more'} - - - - ); -}; - -const AuthorinoCheckbox = ({ - disabledReason, - supportLevel, -}: { - disabledReason?: string; - supportLevel?: SupportLevel | undefined; -}) => { - const fieldId = getFieldId(AUTHORINO_FIELD_NAME, 'input'); - - return ( - - } - helperText={} - isDisabled={!!disabledReason} - /> - - ); -}; - -export default AuthorinoCheckbox; diff --git a/libs/ui-lib/lib/ocm/components/clusterConfiguration/operators/CnvCheckbox.tsx b/libs/ui-lib/lib/ocm/components/clusterConfiguration/operators/CnvCheckbox.tsx deleted file mode 100644 index 97072fbac7..0000000000 --- a/libs/ui-lib/lib/ocm/components/clusterConfiguration/operators/CnvCheckbox.tsx +++ /dev/null @@ -1,105 +0,0 @@ -import React from 'react'; -import { FormGroup, HelperText, HelperTextItem, Tooltip } from '@patternfly/react-core'; -import { ExternalLinkAltIcon } from '@patternfly/react-icons/dist/js/icons/external-link-alt-icon'; -import { - ClusterOperatorProps, - CNV_LINK, - getFieldId, - OperatorsValues, - PopoverIcon, -} from '../../../../common'; -import CnvHostRequirements from './CnvHostRequirements'; -import { OcmCheckboxField } from '../../ui/OcmFormFields'; -import NewFeatureSupportLevelBadge from '../../../../common/components/newFeatureSupportLevels/NewFeatureSupportLevelBadge'; -import { SupportLevel } from '@openshift-assisted/types/assisted-installer-service'; -import { useFormikContext } from 'formik'; -import { useNewFeatureSupportLevel } from '../../../../common/components/newFeatureSupportLevels'; - -const CNV_FIELD_NAME = 'useContainerNativeVirtualization'; - -type CnvLabelProps = { - clusterId: string; - disabledReason?: string; - isVersionEqualsOrMajorThan4_15: boolean; - supportLevel?: SupportLevel; -}; - -const CnvLabel = ({ - clusterId, - disabledReason, - isVersionEqualsOrMajorThan4_15, - supportLevel, -}: CnvLabelProps) => { - return ( - <> - - - } - /> - - - ); -}; - -const CnvHelperText = () => { - return ( - - - Run virtual machines alongside containers on one platform.{' '} - - {'Learn more'} - - - - ); -}; - -const CnvCheckbox = ({ - clusterId, - isVersionEqualsOrMajorThan4_15, - disabledReason, - supportLevel, -}: { - clusterId: ClusterOperatorProps['clusterId']; - isVersionEqualsOrMajorThan4_15: boolean; - disabledReason?: string; - supportLevel?: SupportLevel | undefined; -}) => { - const featureSupportLevelData = useNewFeatureSupportLevel(); - const { setFieldValue } = useFormikContext(); - const fieldId = getFieldId(CNV_FIELD_NAME, 'input'); - const selectOperatorsNeeded = (checked: boolean) => { - if (featureSupportLevelData.isFeatureSupported('LSO')) setFieldValue('useLso', checked); - if (featureSupportLevelData.isFeatureSupported('MTV')) - setFieldValue('useMigrationToolkitforVirtualization', checked); - }; - return ( - - - } - helperText={} - isDisabled={!!disabledReason} - onChange={selectOperatorsNeeded} - /> - - ); -}; - -export default CnvCheckbox; diff --git a/libs/ui-lib/lib/ocm/components/clusterConfiguration/operators/CnvHostRequirements.tsx b/libs/ui-lib/lib/ocm/components/clusterConfiguration/operators/CnvHostRequirements.tsx deleted file mode 100644 index 90ccad6f27..0000000000 --- a/libs/ui-lib/lib/ocm/components/clusterConfiguration/operators/CnvHostRequirements.tsx +++ /dev/null @@ -1,64 +0,0 @@ -import React from 'react'; -import { useSelector } from 'react-redux'; -import { List, ListItem } from '@patternfly/react-core'; -import { useClusterPreflightRequirements } from '../../../hooks'; -import { ErrorState, LoadingState, OPERATOR_NAME_CNV, RenderIf } from '../../../../common'; -import { Cluster } from '@openshift-assisted/types/assisted-installer-service'; -import { selectIsCurrentClusterSNO } from '../../../store/slices/current-cluster/selectors'; -import { getOdfLvmsText } from './utils'; - -const CnvHostRequirements = ({ - clusterId, - isVersionEqualsOrMajorThan4_15, -}: { - clusterId: Cluster['id']; - isVersionEqualsOrMajorThan4_15: boolean; -}) => { - const { preflightRequirements, error, isLoading } = useClusterPreflightRequirements(clusterId); - const isSingleNode = useSelector(selectIsCurrentClusterSNO); - - if (isLoading) { - return ; - } - if (error) { - return ; - } - - const cnvRequirements = preflightRequirements?.operators?.find( - (operatorRequirements) => operatorRequirements.operatorName === OPERATOR_NAME_CNV, - ); - - const workerRequirements = cnvRequirements?.requirements?.worker?.quantitative; - const masterRequirements = cnvRequirements?.requirements?.master?.quantitative; - const odfLvmsText = getOdfLvmsText(isSingleNode, isVersionEqualsOrMajorThan4_15); - return ( - <> - - - Enabled CPU virtualization support in BIOS (Intel-VT / AMD-V) on all nodes - - - - Each worker node requires an additional {workerRequirements?.ramMib || 360} MiB of - memory {workerRequirements?.diskSizeGb ? ',' : ' and'}{' '} - {workerRequirements?.cpuCores || 2} CPUs - {workerRequirements?.diskSizeGb - ? ` and ${workerRequirements?.diskSizeGb} storage space` - : ''} - - - - Each control plane node requires an additional {masterRequirements?.ramMib || 150} MiB of - memory {masterRequirements?.diskSizeGb ? ',' : ' and'} {masterRequirements?.cpuCores || 4}{' '} - CPUs - {masterRequirements?.diskSizeGb - ? ` and ${masterRequirements?.diskSizeGb} storage space` - : ''} - - {odfLvmsText} - - - ); -}; - -export default CnvHostRequirements; diff --git a/libs/ui-lib/lib/ocm/components/clusterConfiguration/operators/FenceAgentsRemediationCheckbox.tsx b/libs/ui-lib/lib/ocm/components/clusterConfiguration/operators/FenceAgentsRemediationCheckbox.tsx deleted file mode 100644 index 8d0dcfeee5..0000000000 --- a/libs/ui-lib/lib/ocm/components/clusterConfiguration/operators/FenceAgentsRemediationCheckbox.tsx +++ /dev/null @@ -1,71 +0,0 @@ -import React from 'react'; -import { FormGroup, HelperText, HelperTextItem, Tooltip } from '@patternfly/react-core'; -import { getFieldId, FENCE_AGENTS_REMEDIATION_LINK } from '../../../../common'; -import { ExternalLinkAltIcon } from '@patternfly/react-icons/dist/js/icons/external-link-alt-icon'; -import { OcmCheckboxField } from '../../ui/OcmFormFields'; -import NewFeatureSupportLevelBadge from '../../../../common/components/newFeatureSupportLevels/NewFeatureSupportLevelBadge'; -import { SupportLevel } from '@openshift-assisted/types/./assisted-installer-service'; - -const FENCE_AGENTS_REMEDIATION_FIELD_NAME = 'useFenceAgentsRemediation'; -const FENCE_AGENTS_REMEDIATION_FEATURE_ID = 'FENCE_AGENTS_REMEDIATION'; - -const FenceAgentsRemediationLabel = ({ - disabledReason, - supportLevel, -}: { - disabledReason?: string; - supportLevel?: SupportLevel; -}) => { - return ( - <> - - - - ); -}; - -const FenceAgentsRemediationHelperText = () => { - return ( - - - Externally fences failed nodes using power controllers.{' '} - - {'Learn more'} - - - - ); -}; - -const FenceAgentsRemediationCheckbox = ({ - disabledReason, - supportLevel, -}: { - disabledReason?: string; - supportLevel?: SupportLevel | undefined; -}) => { - const fieldId = getFieldId(FENCE_AGENTS_REMEDIATION_FIELD_NAME, 'input'); - - return ( - - - } - helperText={} - isDisabled={!!disabledReason} - /> - - ); -}; - -export default FenceAgentsRemediationCheckbox; diff --git a/libs/ui-lib/lib/ocm/components/clusterConfiguration/operators/KmmCheckbox.tsx b/libs/ui-lib/lib/ocm/components/clusterConfiguration/operators/KmmCheckbox.tsx deleted file mode 100644 index 627d07aabd..0000000000 --- a/libs/ui-lib/lib/ocm/components/clusterConfiguration/operators/KmmCheckbox.tsx +++ /dev/null @@ -1,69 +0,0 @@ -import React, { useState } from 'react'; -import { FormGroup, HelperText, HelperTextItem, Tooltip } from '@patternfly/react-core'; -import { getFieldId, KMM_LINK } from '../../../../common'; -import { OcmCheckboxField } from '../../ui/OcmFormFields'; -import { useNewFeatureSupportLevel } from '../../../../common/components/newFeatureSupportLevels'; -import NewFeatureSupportLevelBadge from '../../../../common/components/newFeatureSupportLevels/NewFeatureSupportLevelBadge'; -import { SupportLevel } from '@openshift-assisted/types/./assisted-installer-service'; -import { ExternalLinkAltIcon } from '@patternfly/react-icons/dist/js/icons/external-link-alt-icon'; - -const KMM_FIELD_NAME = 'useKmm'; - -const KmmLabel = ({ - disabledReason, - supportLevel, -}: { - disabledReason?: string; - supportLevel?: SupportLevel; -}) => { - return ( - <> - - - - ); -}; - -const KmmHelperText = () => { - return ( - - - Management of kernel modules.{' '} - - {'Learn more'} - - - - ); -}; - -const KmmCheckbox = ({ disabledReason }: { disabledReason?: string }) => { - const fieldId = getFieldId(KMM_FIELD_NAME, 'input'); - const featureSupportLevel = useNewFeatureSupportLevel(); - const [disabledReasonKmm, setDisabledReason] = useState(); - - React.useEffect(() => { - const reason = featureSupportLevel.getFeatureDisabledReason('KMM'); - setDisabledReason(reason); - }, [featureSupportLevel]); - - return ( - - - } - helperText={} - isDisabled={!!disabledReason || !!disabledReasonKmm} - /> - - ); -}; - -export default KmmCheckbox; diff --git a/libs/ui-lib/lib/ocm/components/clusterConfiguration/operators/KubeDeschedulerCheckbox.tsx b/libs/ui-lib/lib/ocm/components/clusterConfiguration/operators/KubeDeschedulerCheckbox.tsx deleted file mode 100644 index 679ba2b8e8..0000000000 --- a/libs/ui-lib/lib/ocm/components/clusterConfiguration/operators/KubeDeschedulerCheckbox.tsx +++ /dev/null @@ -1,66 +0,0 @@ -import React from 'react'; -import { FormGroup, HelperText, HelperTextItem, Tooltip } from '@patternfly/react-core'; -import { getFieldId, NODE_HEALTHCHECK_LINK } from '../../../../common'; -import { ExternalLinkAltIcon } from '@patternfly/react-icons/dist/js/icons/external-link-alt-icon'; -import { OcmCheckboxField } from '../../ui/OcmFormFields'; -import NewFeatureSupportLevelBadge from '../../../../common/components/newFeatureSupportLevels/NewFeatureSupportLevelBadge'; -import { SupportLevel } from '@openshift-assisted/types/./assisted-installer-service'; - -const KUBE_DESCHEDULER_FIELD_NAME = 'useKubeDescheduler'; -const KUBE_DESCHEDULER_FEATURE_ID = 'KUBE_DESCHEDULER'; - -const KubeDeschedulerLabel = ({ - disabledReason, - supportLevel, -}: { - disabledReason?: string; - supportLevel?: SupportLevel; -}) => { - return ( - <> - - - - ); -}; - -const KubeDeschedulerHelperText = () => { - return ( - - - Evicts pods to reschedule them onto more suitable nodes.{' '} - - {'Learn more'} - - - - ); -}; - -const KubeDeschedulerCheckbox = ({ - disabledReason, - supportLevel, -}: { - disabledReason?: string; - supportLevel?: SupportLevel | undefined; -}) => { - const fieldId = getFieldId(KUBE_DESCHEDULER_FIELD_NAME, 'input'); - - return ( - - } - helperText={} - isDisabled={!!disabledReason} - /> - - ); -}; - -export default KubeDeschedulerCheckbox; diff --git a/libs/ui-lib/lib/ocm/components/clusterConfiguration/operators/LsoCheckbox.tsx b/libs/ui-lib/lib/ocm/components/clusterConfiguration/operators/LsoCheckbox.tsx deleted file mode 100644 index fddbece07d..0000000000 --- a/libs/ui-lib/lib/ocm/components/clusterConfiguration/operators/LsoCheckbox.tsx +++ /dev/null @@ -1,64 +0,0 @@ -import React from 'react'; -import { FormGroup, HelperText, HelperTextItem, Tooltip } from '@patternfly/react-core'; -import { getFieldId, getLsoLink } from '../../../../common'; -import { OcmCheckboxField } from '../../ui/OcmFormFields'; -import NewFeatureSupportLevelBadge from '../../../../common/components/newFeatureSupportLevels/NewFeatureSupportLevelBadge'; -import { SupportLevel } from '@openshift-assisted/types/./assisted-installer-service'; -import ExternalLinkAltIcon from '@patternfly/react-icons/dist/js/icons/external-link-alt-icon'; - -const LSO_FIELD_NAME = 'useLso'; - -const LsoLabel = ({ - disabledReason, - supportLevel, -}: { - disabledReason?: string; - supportLevel?: SupportLevel; -}) => { - return ( - <> - - - - ); -}; - -const LsoHelperText = ({ openshiftVersion }: { openshiftVersion?: string }) => { - return ( - - - Allows provisioning of persistent storage by using local volumes.{' '} - - {'Learn more'} - - - - ); -}; - -const LsoCheckbox = ({ - disabledReason, - supportLevel, - openshiftVersion, -}: { - disabledReason?: string; - supportLevel?: SupportLevel | undefined; - openshiftVersion?: string; -}) => { - const fieldId = getFieldId(LSO_FIELD_NAME, 'input'); - - return ( - - } - helperText={} - isDisabled={!!disabledReason} - /> - - ); -}; - -export default LsoCheckbox; diff --git a/libs/ui-lib/lib/ocm/components/clusterConfiguration/operators/LvmCheckbox.tsx b/libs/ui-lib/lib/ocm/components/clusterConfiguration/operators/LvmCheckbox.tsx deleted file mode 100644 index 0fa50841f1..0000000000 --- a/libs/ui-lib/lib/ocm/components/clusterConfiguration/operators/LvmCheckbox.tsx +++ /dev/null @@ -1,112 +0,0 @@ -import React from 'react'; -import { FormGroup, HelperText, HelperTextItem, Tooltip } from '@patternfly/react-core'; -import { - ClusterOperatorProps, - getFieldId, - PopoverIcon, - operatorLabels, - OPERATOR_NAME_LVM, - ExposedOperatorName, - ExternalLink, - OPERATOR_NAME_LVMS, - getLvmsDocsLink, -} from '../../../../common'; -import LvmHostRequirements from './LvmHostRequirements'; -import { OcmCheckboxField } from '../../ui/OcmFormFields'; -import { useTranslation } from '../../../../common/hooks/use-translation-wrapper'; -import { useNewFeatureSupportLevel } from '../../../../common/components/newFeatureSupportLevels'; -import NewFeatureSupportLevelBadge from '../../../../common/components/newFeatureSupportLevels/NewFeatureSupportLevelBadge'; -import { SupportLevel } from '@openshift-assisted/types/assisted-installer-service'; - -const LVM_FIELD_NAME = 'useOdfLogicalVolumeManager'; - -type LvmLabelProps = ClusterOperatorProps & { - disabledReason?: string; - operatorLabel: string; - supportLevel?: SupportLevel; -}; - -const LvmHelperText = ({ - operatorName, - docsVersion, -}: { - operatorName: ExposedOperatorName; - docsVersion: string; -}) => { - return ( - - - Storage virtualization that offers a more flexible approach for disk space management.{' '} - {operatorName === OPERATOR_NAME_LVMS && ( - Learn more - )} - - - ); -}; - -const LvmLabel = ({ clusterId, operatorLabel, disabledReason, supportLevel }: LvmLabelProps) => { - return ( - <> - - } - /> - - - ); -}; - -const LvmCheckbox = ({ - clusterId, - openshiftVersion, - disabledReason, - supportLevel, -}: { - clusterId: ClusterOperatorProps['clusterId']; - openshiftVersion?: ClusterOperatorProps['openshiftVersion']; - disabledReason?: string; - supportLevel?: SupportLevel | undefined; -}) => { - const fieldId = getFieldId(LVM_FIELD_NAME, 'input'); - - const featureSupportLevel = useNewFeatureSupportLevel(); - const { t } = useTranslation(); - - const operatorInfo = React.useMemo(() => { - const operatorLabel = operatorLabels(t, featureSupportLevel)[OPERATOR_NAME_LVM]; - return { - operatorLabel, - operatorName: supportLevel === 'supported' ? OPERATOR_NAME_LVMS : OPERATOR_NAME_LVM, - }; - }, [featureSupportLevel, supportLevel, t]); - - return ( - - - } - helperText={ - - } - isDisabled={!!disabledReason} - /> - - ); -}; - -export default LvmCheckbox; diff --git a/libs/ui-lib/lib/ocm/components/clusterConfiguration/operators/LvmHostRequirements.tsx b/libs/ui-lib/lib/ocm/components/clusterConfiguration/operators/LvmHostRequirements.tsx deleted file mode 100644 index 86694589f6..0000000000 --- a/libs/ui-lib/lib/ocm/components/clusterConfiguration/operators/LvmHostRequirements.tsx +++ /dev/null @@ -1,38 +0,0 @@ -import React from 'react'; -import { List, ListItem } from '@patternfly/react-core'; -import { useClusterPreflightRequirements } from '../../../hooks'; -import { - ClusterOperatorProps, - ErrorState, - LoadingState, - OPERATOR_NAME_LVM, -} from '../../../../common'; -import { HostTypeHardwareRequirements } from '@openshift-assisted/types/assisted-installer-service'; - -const LvmHostRequirements = ({ clusterId }: { clusterId: ClusterOperatorProps['clusterId'] }) => { - const { preflightRequirements, error, isLoading } = useClusterPreflightRequirements(clusterId); - - if (isLoading) { - return ; - } - if (error) { - return ; - } - - const lvmRequirements = preflightRequirements?.operators?.find( - (operatorRequirements) => operatorRequirements.operatorName === OPERATOR_NAME_LVM, - ); - - const { qualitative = [] } = lvmRequirements?.requirements - ?.master as HostTypeHardwareRequirements; - - return ( - - {qualitative.map((qualitativeItem, index) => ( - {qualitativeItem} - ))} - - ); -}; - -export default LvmHostRequirements; diff --git a/libs/ui-lib/lib/ocm/components/clusterConfiguration/operators/MceCheckbox.tsx b/libs/ui-lib/lib/ocm/components/clusterConfiguration/operators/MceCheckbox.tsx deleted file mode 100644 index 79a3a2b9eb..0000000000 --- a/libs/ui-lib/lib/ocm/components/clusterConfiguration/operators/MceCheckbox.tsx +++ /dev/null @@ -1,90 +0,0 @@ -import React from 'react'; -import { FormGroup, HelperText, HelperTextItem, Tooltip } from '@patternfly/react-core'; -import { ExternalLinkAltIcon } from '@patternfly/react-icons/dist/js/icons/external-link-alt-icon'; -import { getFieldId, PopoverIcon, getMceDocsLink, ClusterOperatorProps } from '../../../../common'; -import { OcmCheckboxField } from '../../ui/OcmFormFields'; -import MceRequirements from './MceRequirements'; -import NewFeatureSupportLevelBadge from '../../../../common/components/newFeatureSupportLevels/NewFeatureSupportLevelBadge'; -import { SupportLevel } from '@openshift-assisted/types/./assisted-installer-service'; - -const MCE_FIELD_NAME = 'useMultiClusterEngine'; - -const MceLabel = ({ - disabledReason, - isVersionEqualsOrMajorThan4_15, - clusterId, - supportLevel, -}: { - disabledReason?: string; - isVersionEqualsOrMajorThan4_15: boolean; - clusterId: ClusterOperatorProps['clusterId']; - supportLevel?: SupportLevel; -}) => { - return ( - <> - - - } - /> - - - ); -}; - -const MceHelperText = ({ docsVersion }: { docsVersion: string }) => { - return ( - - - Create, import, and manage multiple clusters from this cluster.{' '} - - {'Learn more'} - - - - ); -}; - -const MceCheckbox = ({ - clusterId, - isVersionEqualsOrMajorThan4_15, - openshiftVersion, - supportLevel, - disabledReason, -}: { - isVersionEqualsOrMajorThan4_15: boolean; - openshiftVersion?: string; - clusterId: ClusterOperatorProps['clusterId']; - supportLevel?: SupportLevel | undefined; - disabledReason?: string; -}) => { - const fieldId = getFieldId(MCE_FIELD_NAME, 'input'); - return ( - - - } - isDisabled={!!disabledReason} - helperText={} - /> - - ); -}; - -export default MceCheckbox; diff --git a/libs/ui-lib/lib/ocm/components/clusterConfiguration/operators/MceRequirements.tsx b/libs/ui-lib/lib/ocm/components/clusterConfiguration/operators/MceRequirements.tsx deleted file mode 100644 index ec3deaa7ec..0000000000 --- a/libs/ui-lib/lib/ocm/components/clusterConfiguration/operators/MceRequirements.tsx +++ /dev/null @@ -1,61 +0,0 @@ -import React from 'react'; -import { useSelector } from 'react-redux'; -import { List, ListItem } from '@patternfly/react-core'; -import { useClusterPreflightRequirements } from '../../../hooks'; -import { ErrorState, LoadingState, OPERATOR_NAME_MCE, RenderIf } from '../../../../common'; -import { Cluster } from '@openshift-assisted/types/assisted-installer-service'; -import { selectIsCurrentClusterSNO } from '../../../store/slices/current-cluster/selectors'; -import { getOdfLvmsText } from './utils'; - -const MceRequirements = ({ - clusterId, - isVersionEqualsOrMajorThan4_15, -}: { - clusterId: Cluster['id']; - isVersionEqualsOrMajorThan4_15: boolean; -}) => { - const { preflightRequirements, error, isLoading } = useClusterPreflightRequirements(clusterId); - const isSingleNode = useSelector(selectIsCurrentClusterSNO); - - if (isLoading) { - return ; - } - if (error) { - return ; - } - - const mceRequirements = preflightRequirements?.operators?.find( - (operatorRequirements) => operatorRequirements.operatorName === OPERATOR_NAME_MCE, - ); - - const workerRequirements = mceRequirements?.requirements?.worker?.quantitative; - const masterRequirements = mceRequirements?.requirements?.master?.quantitative; - const odfLvmsText = getOdfLvmsText(isSingleNode, isVersionEqualsOrMajorThan4_15); - return ( - <> - - - - Each worker node requires an additional {workerRequirements?.ramMib || 360} MiB of - memory {workerRequirements?.diskSizeGb ? ',' : ' and'}{' '} - {workerRequirements?.cpuCores || 2} CPUs - {workerRequirements?.diskSizeGb - ? ` and ${workerRequirements?.diskSizeGb} storage space` - : ''} - - - - Each control plane node requires an additional {masterRequirements?.ramMib || 150} MiB of - memory {masterRequirements?.diskSizeGb ? ',' : ' and'} {masterRequirements?.cpuCores || 4}{' '} - CPUs - {masterRequirements?.diskSizeGb - ? ` and ${masterRequirements?.diskSizeGb} storage space` - : ''} - - {odfLvmsText} - - - ); -}; - -export default MceRequirements; diff --git a/libs/ui-lib/lib/ocm/components/clusterConfiguration/operators/MtvOperatorCheckbox.tsx b/libs/ui-lib/lib/ocm/components/clusterConfiguration/operators/MtvOperatorCheckbox.tsx deleted file mode 100644 index a9ff73f5f3..0000000000 --- a/libs/ui-lib/lib/ocm/components/clusterConfiguration/operators/MtvOperatorCheckbox.tsx +++ /dev/null @@ -1,94 +0,0 @@ -import React from 'react'; -import { FormGroup, HelperText, HelperTextItem, Tooltip } from '@patternfly/react-core'; -import { ExternalLinkAltIcon } from '@patternfly/react-icons/dist/js/icons/external-link-alt-icon'; -import { - getFieldId, - PopoverIcon, - MTV_LINK, - OperatorsValues, - ClusterOperatorProps, -} from '../../../../common'; -import { OcmCheckboxField } from '../../ui/OcmFormFields'; -import { useFormikContext } from 'formik'; -import MtvRequirements from './MtvRequirements'; -import { SupportLevel } from '@openshift-assisted/types/./assisted-installer-service'; -import NewFeatureSupportLevelBadge from '../../../../common/components/newFeatureSupportLevels/NewFeatureSupportLevelBadge'; -import { useNewFeatureSupportLevel } from '../../../../common/components/newFeatureSupportLevels'; - -const Mtv_FIELD_NAME = 'useMigrationToolkitforVirtualization'; - -const MtvLabel = ({ - disabledReason, - clusterId, - supportLevel, -}: { - disabledReason?: string; - clusterId: string; - supportLevel?: SupportLevel; -}) => ( - <> - - } - /> - - -); - -const MtvHelperText = () => { - return ( - - - This Toolkit (MTV) enables you to migrate virtual machines from VMware vSphere, Red Hat - Virtualization, or OpenStack to OpenShift Virtualization running on Red Hat OpenShift.{' '} - - {'Learn more'} - - - - ); -}; - -const MtvCheckbox = ({ - clusterId, - disabledReason, - supportLevel, -}: { - clusterId: ClusterOperatorProps['clusterId']; - disabledReason?: string; - supportLevel?: SupportLevel | undefined; -}) => { - const { setFieldValue } = useFormikContext(); - const fieldId = getFieldId(Mtv_FIELD_NAME, 'input'); - const featureSupportLevelData = useNewFeatureSupportLevel(); - - const selectCNVOperator = (checked: boolean) => { - if (featureSupportLevelData.isFeatureSupported('CNV')) - setFieldValue('useContainerNativeVirtualization', checked); - if (featureSupportLevelData.isFeatureSupported('LSO')) setFieldValue('useLso', checked); - }; - - return ( - - - } - isDisabled={!!disabledReason} - helperText={} - onChange={selectCNVOperator} - /> - - ); -}; - -export default MtvCheckbox; diff --git a/libs/ui-lib/lib/ocm/components/clusterConfiguration/operators/MtvRequirements.tsx b/libs/ui-lib/lib/ocm/components/clusterConfiguration/operators/MtvRequirements.tsx deleted file mode 100644 index 87c851be6e..0000000000 --- a/libs/ui-lib/lib/ocm/components/clusterConfiguration/operators/MtvRequirements.tsx +++ /dev/null @@ -1,52 +0,0 @@ -import React from 'react'; -import { useSelector } from 'react-redux'; -import { List, ListItem } from '@patternfly/react-core'; -import { useClusterPreflightRequirements } from '../../../hooks'; -import { ErrorState, LoadingState, OPERATOR_NAME_MTV, RenderIf } from '../../../../common'; -import { Cluster } from '@openshift-assisted/types/assisted-installer-service'; -import { selectIsCurrentClusterSNO } from '../../../store/slices/current-cluster/selectors'; - -const MtvRequirements = ({ clusterId }: { clusterId: Cluster['id'] }) => { - const { preflightRequirements, error, isLoading } = useClusterPreflightRequirements(clusterId); - const isSingleNode = useSelector(selectIsCurrentClusterSNO); - - if (isLoading) { - return ; - } - if (error) { - return ; - } - - const mtvRequirements = preflightRequirements?.operators?.find( - (operatorRequirements) => operatorRequirements.operatorName === OPERATOR_NAME_MTV, - ); - - const workerRequirements = mtvRequirements?.requirements?.worker?.quantitative; - const masterRequirements = mtvRequirements?.requirements?.master?.quantitative; - return ( - <> - - - - Each worker node requires an additional {workerRequirements?.ramMib || 360} MiB of - memory {workerRequirements?.diskSizeGb ? ',' : ' and'}{' '} - {workerRequirements?.cpuCores || 2} CPUs - {workerRequirements?.diskSizeGb - ? ` and ${workerRequirements?.diskSizeGb} storage space` - : ''} - - - - Each control plane node requires an additional {masterRequirements?.ramMib || 150} MiB of - memory {masterRequirements?.diskSizeGb ? ',' : ' and'} {masterRequirements?.cpuCores || 4}{' '} - CPUs - {masterRequirements?.diskSizeGb - ? ` and ${masterRequirements?.diskSizeGb} storage space` - : ''} - - - - ); -}; - -export default MtvRequirements; diff --git a/libs/ui-lib/lib/ocm/components/clusterConfiguration/operators/NmstateCheckbox.tsx b/libs/ui-lib/lib/ocm/components/clusterConfiguration/operators/NmstateCheckbox.tsx deleted file mode 100644 index ebebe02c78..0000000000 --- a/libs/ui-lib/lib/ocm/components/clusterConfiguration/operators/NmstateCheckbox.tsx +++ /dev/null @@ -1,82 +0,0 @@ -import React from 'react'; -import { FormGroup, HelperText, HelperTextItem, Tooltip } from '@patternfly/react-core'; -import { getFieldId, PopoverIcon, ClusterOperatorProps, getNmstateLink } from '../../../../common'; -import { OcmCheckboxField } from '../../ui/OcmFormFields'; -import NmstateRequirements from './NmstateRequirements'; -import { SupportLevel } from '@openshift-assisted/types/./assisted-installer-service'; -import NewFeatureSupportLevelBadge from '../../../../common/components/newFeatureSupportLevels/NewFeatureSupportLevelBadge'; -import ExternalLinkAltIcon from '@patternfly/react-icons/dist/js/icons/external-link-alt-icon'; - -const NMSTATE_FIELD_NAME = 'useNmstate'; - -const NmstateLabel = ({ - disabledReason, - clusterId, - supportLevel, -}: { - disabledReason?: string; - clusterId: ClusterOperatorProps['clusterId']; - supportLevel?: SupportLevel; -}) => { - return ( - <> - - } - /> - - - ); -}; - -const NmstateHelperText = ({ openshiftVersion }: { openshiftVersion?: string }) => { - return ( - - - Provides users with functionality to configure various network interface types, DNS, and - routing on cluster nodes.{' '} - - {'Learn more'} - - - - ); -}; - -const NmstateCheckbox = ({ - clusterId, - disabledReason, - supportLevel, - openshiftVersion, -}: { - clusterId: ClusterOperatorProps['clusterId']; - disabledReason?: string; - supportLevel?: SupportLevel | undefined; - openshiftVersion?: string; -}) => { - const fieldId = getFieldId(NMSTATE_FIELD_NAME, 'input'); - - return ( - - - } - helperText={} - isDisabled={!!disabledReason} - /> - - ); -}; - -export default NmstateCheckbox; diff --git a/libs/ui-lib/lib/ocm/components/clusterConfiguration/operators/NmstateRequirements.tsx b/libs/ui-lib/lib/ocm/components/clusterConfiguration/operators/NmstateRequirements.tsx deleted file mode 100644 index 5684fb87cf..0000000000 --- a/libs/ui-lib/lib/ocm/components/clusterConfiguration/operators/NmstateRequirements.tsx +++ /dev/null @@ -1,53 +0,0 @@ -import React from 'react'; -import { useSelector } from 'react-redux'; -import { List, ListItem } from '@patternfly/react-core'; -import { useClusterPreflightRequirements } from '../../../hooks'; -import { ErrorState, LoadingState, OPERATOR_NAME_NMSTATE, RenderIf } from '../../../../common'; -import { Cluster } from '@openshift-assisted/types/assisted-installer-service'; -import { selectIsCurrentClusterSNO } from '../../../store/slices/current-cluster/selectors'; - -const NmstateRequirements = ({ clusterId }: { clusterId: Cluster['id'] }) => { - const { preflightRequirements, error, isLoading } = useClusterPreflightRequirements(clusterId); - const isSingleNode = useSelector(selectIsCurrentClusterSNO); - - if (isLoading) { - return ; - } - if (error) { - return ; - } - - const NmstateRequirements = preflightRequirements?.operators?.find( - (operatorRequirements) => operatorRequirements.operatorName === OPERATOR_NAME_NMSTATE, - ); - - const workerRequirements = NmstateRequirements?.requirements?.worker?.quantitative; - const masterRequirements = NmstateRequirements?.requirements?.master?.quantitative; - - return ( - <> - - - - Each worker node requires an additional {workerRequirements?.ramMib || 360} MiB of - memory {workerRequirements?.diskSizeGb ? ',' : ''}{' '} - {workerRequirements?.cpuCores ? ` and ${workerRequirements?.cpuCores} CPUs` : ''} - {workerRequirements?.diskSizeGb - ? ` and ${workerRequirements?.diskSizeGb} storage space` - : ''} - - - - Each control plane node requires an additional {masterRequirements?.ramMib || 150} MiB of - memory {masterRequirements?.diskSizeGb ? ',' : ''}{' '} - {masterRequirements?.cpuCores ? ` and ${masterRequirements?.cpuCores} CPUs` : ''} - {masterRequirements?.diskSizeGb - ? ` and ${masterRequirements?.diskSizeGb} storage space` - : ''} - - - - ); -}; - -export default NmstateRequirements; diff --git a/libs/ui-lib/lib/ocm/components/clusterConfiguration/operators/NodeFeatureDiscoveryCheckbox.tsx b/libs/ui-lib/lib/ocm/components/clusterConfiguration/operators/NodeFeatureDiscoveryCheckbox.tsx deleted file mode 100644 index 3f3e9f6339..0000000000 --- a/libs/ui-lib/lib/ocm/components/clusterConfiguration/operators/NodeFeatureDiscoveryCheckbox.tsx +++ /dev/null @@ -1,73 +0,0 @@ -import React from 'react'; -import { FormGroup, HelperText, HelperTextItem, Tooltip } from '@patternfly/react-core'; -import { getFieldId, getNodeFeatureDiscoveryLink } from '../../../../common'; -import { OcmCheckboxField } from '../../ui/OcmFormFields'; -import NewFeatureSupportLevelBadge from '../../../../common/components/newFeatureSupportLevels/NewFeatureSupportLevelBadge'; -import { SupportLevel } from '@openshift-assisted/types/./assisted-installer-service'; -import ExternalLinkAltIcon from '@patternfly/react-icons/dist/js/icons/external-link-alt-icon'; - -const NODEFEATUREDISCOVERY_FIELD_NAME = 'useNodeFeatureDiscovery'; - -const NodeFeatureDiscoveryLabel = ({ - disabledReason, - supportLevel, -}: { - disabledReason?: string; - supportLevel?: SupportLevel; -}) => { - return ( - <> - - - - ); -}; - -const NodeFeatureDiscoveryHelperText = ({ openshiftVersion }: { openshiftVersion?: string }) => { - return ( - - - Manage the detection of hardware features and configuration by labeling nodes with - hardware-specific information.{' '} - - {'Learn more'} - - - - ); -}; - -const NodeFeatureDiscoveryCheckbox = ({ - disabledReason, - supportLevel, - openshiftVersion, -}: { - disabledReason?: string; - supportLevel?: SupportLevel | undefined; - openshiftVersion?: string; -}) => { - const fieldId = getFieldId(NODEFEATUREDISCOVERY_FIELD_NAME, 'input'); - - return ( - - - } - helperText={ - - } - isDisabled={!!disabledReason} - /> - - ); -}; - -export default NodeFeatureDiscoveryCheckbox; diff --git a/libs/ui-lib/lib/ocm/components/clusterConfiguration/operators/NodeHealthcheckCheckbox.tsx b/libs/ui-lib/lib/ocm/components/clusterConfiguration/operators/NodeHealthcheckCheckbox.tsx deleted file mode 100644 index 80ee53667f..0000000000 --- a/libs/ui-lib/lib/ocm/components/clusterConfiguration/operators/NodeHealthcheckCheckbox.tsx +++ /dev/null @@ -1,66 +0,0 @@ -import React from 'react'; -import { FormGroup, HelperText, HelperTextItem, Tooltip } from '@patternfly/react-core'; -import { getFieldId, NODE_HEALTHCHECK_LINK } from '../../../../common'; -import { ExternalLinkAltIcon } from '@patternfly/react-icons/dist/js/icons/external-link-alt-icon'; -import { OcmCheckboxField } from '../../ui/OcmFormFields'; -import NewFeatureSupportLevelBadge from '../../../../common/components/newFeatureSupportLevels/NewFeatureSupportLevelBadge'; -import { SupportLevel } from '@openshift-assisted/types/./assisted-installer-service'; - -const NODE_HEALTHCHECK_FIELD_NAME = 'useNodeHealthcheck'; -const NODE_HEALTHCHECK_FEATURE_ID = 'NODE_HEALTHCHECK'; - -const NodeHealthcheckLabel = ({ - disabledReason, - supportLevel, -}: { - disabledReason?: string; - supportLevel?: SupportLevel; -}) => { - return ( - <> - - - - ); -}; - -const NodeHealthcheckHelperText = () => { - return ( - - - Identify Unhealthy Nodes.{' '} - - {'Learn more'} - - - - ); -}; - -const NodeHealthcheckCheckbox = ({ - disabledReason, - supportLevel, -}: { - disabledReason?: string; - supportLevel?: SupportLevel | undefined; -}) => { - const fieldId = getFieldId(NODE_HEALTHCHECK_FIELD_NAME, 'input'); - - return ( - - } - helperText={} - isDisabled={!!disabledReason} - /> - - ); -}; - -export default NodeHealthcheckCheckbox; diff --git a/libs/ui-lib/lib/ocm/components/clusterConfiguration/operators/NodeMaintenanceOperatorCheckbox.tsx b/libs/ui-lib/lib/ocm/components/clusterConfiguration/operators/NodeMaintenanceOperatorCheckbox.tsx deleted file mode 100644 index 9dac6bb6ca..0000000000 --- a/libs/ui-lib/lib/ocm/components/clusterConfiguration/operators/NodeMaintenanceOperatorCheckbox.tsx +++ /dev/null @@ -1,66 +0,0 @@ -import React from 'react'; -import { FormGroup, HelperText, HelperTextItem, Tooltip } from '@patternfly/react-core'; -import { getFieldId, NODE_HEALTHCHECK_LINK } from '../../../../common'; -import { ExternalLinkAltIcon } from '@patternfly/react-icons/dist/js/icons/external-link-alt-icon'; -import { OcmCheckboxField } from '../../ui/OcmFormFields'; -import NewFeatureSupportLevelBadge from '../../../../common/components/newFeatureSupportLevels/NewFeatureSupportLevelBadge'; -import { SupportLevel } from '@openshift-assisted/types/./assisted-installer-service'; - -const NODE_MAINTENANCE_FIELD_NAME = 'useNodeMaintenance'; -const NODE_MAINTENANCE_FEATURE_ID = 'NODE_MAINTENANCE'; - -const NodeMaintenanceLabel = ({ - disabledReason, - supportLevel, -}: { - disabledReason?: string; - supportLevel?: SupportLevel; -}) => { - return ( - <> - - - - ); -}; - -const NodeMaintenanceHelperText = () => { - return ( - - - Place nodes in maintenance mode.{' '} - - {'Learn more'} - - - - ); -}; - -const NodeMaintenanceCheckbox = ({ - disabledReason, - supportLevel, -}: { - disabledReason?: string; - supportLevel?: SupportLevel | undefined; -}) => { - const fieldId = getFieldId(NODE_MAINTENANCE_FIELD_NAME, 'input'); - - return ( - - } - helperText={} - isDisabled={!!disabledReason} - /> - - ); -}; - -export default NodeMaintenanceCheckbox; diff --git a/libs/ui-lib/lib/ocm/components/clusterConfiguration/operators/NvidiaGpuCheckbox.tsx b/libs/ui-lib/lib/ocm/components/clusterConfiguration/operators/NvidiaGpuCheckbox.tsx deleted file mode 100644 index 8c3bd97593..0000000000 --- a/libs/ui-lib/lib/ocm/components/clusterConfiguration/operators/NvidiaGpuCheckbox.tsx +++ /dev/null @@ -1,70 +0,0 @@ -import React from 'react'; -import { FormGroup, HelperText, HelperTextItem, Tooltip } from '@patternfly/react-core'; -import { getFieldId, getNvidiaGpuLink, PopoverIcon } from '../../../../common'; -import { OcmCheckboxField } from '../../ui/OcmFormFields'; -import { SupportLevel } from '@openshift-assisted/types/./assisted-installer-service'; -import NewFeatureSupportLevelBadge from '../../../../common/components/newFeatureSupportLevels/NewFeatureSupportLevelBadge'; -import ExternalLinkAltIcon from '@patternfly/react-icons/dist/js/icons/external-link-alt-icon'; - -const NVIDIAGPU_FIELD_NAME = 'useNvidiaGpu'; - -const NvidiaGpuLabel = ({ - disabledReason, - supportLevel, -}: { - disabledReason?: string; - supportLevel?: SupportLevel; -}) => { - return ( - <> - - - - - ); -}; - -const NvidiaGpuHelperText = ({ openshiftVersion }: { openshiftVersion?: string }) => { - return ( - - - Automate the management of NVIDIA software components needed to provision and monitor GPUs.{' '} - - {'Learn more'} - - - - ); -}; - -const NvidiaGpuCheckbox = ({ - disabledReason, - supportLevel, - openshiftVersion, -}: { - disabledReason?: string; - supportLevel?: SupportLevel | undefined; - openshiftVersion?: string; -}) => { - const fieldId = getFieldId(NVIDIAGPU_FIELD_NAME, 'input'); - - return ( - - } - helperText={} - isDisabled={!!disabledReason} - /> - - ); -}; - -export default NvidiaGpuCheckbox; diff --git a/libs/ui-lib/lib/ocm/components/clusterConfiguration/operators/OdfCheckbox.tsx b/libs/ui-lib/lib/ocm/components/clusterConfiguration/operators/OdfCheckbox.tsx deleted file mode 100644 index 4ee9e94efb..0000000000 --- a/libs/ui-lib/lib/ocm/components/clusterConfiguration/operators/OdfCheckbox.tsx +++ /dev/null @@ -1,82 +0,0 @@ -import React from 'react'; -import { FormGroup, HelperText, HelperTextItem, Tooltip } from '@patternfly/react-core'; -import { ExternalLinkAltIcon } from '@patternfly/react-icons/dist/js/icons/external-link-alt-icon'; -import { - getFieldId, - PopoverIcon, - ODF_REQUIREMENTS_LINK, - ODF_LINK, - OperatorsValues, -} from '../../../../common'; -import { OcmCheckboxField } from '../../ui/OcmFormFields'; -import NewFeatureSupportLevelBadge from '../../../../common/components/newFeatureSupportLevels/NewFeatureSupportLevelBadge'; -import { SupportLevel } from '@openshift-assisted/types/./assisted-installer-service'; -import { useFormikContext } from 'formik'; -import { useNewFeatureSupportLevel } from '../../../../common/components/newFeatureSupportLevels'; - -const ODF_FIELD_NAME = 'useOpenShiftDataFoundation'; - -const OdfLabel = ({ - disabledReason, - supportLevel, -}: { - disabledReason?: string; - supportLevel?: SupportLevel; -}) => ( - <> - - - Learn more about the requirements for OpenShift Data Foundation . - - } - /> - - -); - -const OdfHelperText = () => { - return ( - - - Persistent software-defined storage for hybrid applications.{' '} - - {'Learn more'} - - - - ); -}; - -const OdfCheckbox = ({ - disabledReason, - supportLevel, -}: { - disabledReason?: string; - supportLevel?: SupportLevel | undefined; -}) => { - const { setFieldValue } = useFormikContext(); - const featureSupportLevelData = useNewFeatureSupportLevel(); - const fieldId = getFieldId(ODF_FIELD_NAME, 'input'); - const selectLsoOperator = (checked: boolean) => { - if (featureSupportLevelData.isFeatureSupported('LSO')) setFieldValue('useLso', checked); - }; - return ( - - } - isDisabled={!!disabledReason} - helperText={} - onChange={selectLsoOperator} - /> - - ); -}; - -export default OdfCheckbox; diff --git a/libs/ui-lib/lib/ocm/components/clusterConfiguration/operators/OpenShiftAICheckbox.tsx b/libs/ui-lib/lib/ocm/components/clusterConfiguration/operators/OpenShiftAICheckbox.tsx deleted file mode 100644 index 7e7a50dfb1..0000000000 --- a/libs/ui-lib/lib/ocm/components/clusterConfiguration/operators/OpenShiftAICheckbox.tsx +++ /dev/null @@ -1,67 +0,0 @@ -import React from 'react'; -import { FormGroup, HelperText, HelperTextItem, Tooltip } from '@patternfly/react-core'; -import { ExternalLinkAltIcon } from '@patternfly/react-icons/dist/js/icons/external-link-alt-icon'; -import { getFieldId, PopoverIcon, OPENSHIFT_AI_LINK } from '../../../../common'; -import OpenShiftAIRequirements from './OpenShiftAIRequirements'; -import { OcmCheckboxField } from '../../ui/OcmFormFields'; -import NewFeatureSupportLevelBadge from '../../../../common/components/newFeatureSupportLevels/NewFeatureSupportLevelBadge'; -import { SupportLevel } from '@openshift-assisted/types/assisted-installer-service'; - -const OPENSHIFT_AI_FIELD_NAME = 'useOpenShiftAI'; - -type OpenShiftAILabelProps = { - disabledReason?: string; - supportLevel?: SupportLevel; -}; - -const OpenShiftAILabel = ({ disabledReason, supportLevel }: OpenShiftAILabelProps) => { - return ( - <> - - } - /> - - - ); -}; - -const OpenShiftAIHelperText = () => { - return ( - - - Train, serve, monitor and manage AI/ML models and applications.{' '} - - {'Learn more'} - - - - ); -}; - -const OpenShiftAICheckbox = ({ - disabledReason, - supportLevel, -}: { - disabledReason?: string; - supportLevel?: SupportLevel | undefined; -}) => { - const fieldId = getFieldId(OPENSHIFT_AI_FIELD_NAME, 'input'); - - return ( - - } - isDisabled={!!disabledReason} - helperText={} - /> - - ); -}; - -export default OpenShiftAICheckbox; diff --git a/libs/ui-lib/lib/ocm/components/clusterConfiguration/operators/OpenShiftAIRequirements.tsx b/libs/ui-lib/lib/ocm/components/clusterConfiguration/operators/OpenShiftAIRequirements.tsx deleted file mode 100644 index 9aaba33ead..0000000000 --- a/libs/ui-lib/lib/ocm/components/clusterConfiguration/operators/OpenShiftAIRequirements.tsx +++ /dev/null @@ -1,22 +0,0 @@ -import React from 'react'; -import { List, ListItem } from '@patternfly/react-core'; -import { ExternalLinkAltIcon } from '@patternfly/react-icons/dist/js/icons/external-link-alt-icon'; -import { OPENSHIFT_AI_REQUIREMENTS_LINK } from '../../../../common'; - -const OpenShiftAIRequirements = () => { - return ( - <> - - At least two worker nodes. - - Each worker node requires 32 additional GiB of memory and 8 additional CPUs. - - - - Learn more . - - - ); -}; - -export default OpenShiftAIRequirements; diff --git a/libs/ui-lib/lib/ocm/components/clusterConfiguration/operators/OpenshiftAIAmdRequirements.tsx b/libs/ui-lib/lib/ocm/components/clusterConfiguration/operators/OpenshiftAIAmdRequirements.tsx deleted file mode 100644 index f9f74d14fa..0000000000 --- a/libs/ui-lib/lib/ocm/components/clusterConfiguration/operators/OpenshiftAIAmdRequirements.tsx +++ /dev/null @@ -1,36 +0,0 @@ -import React from 'react'; -import { List, ListItem } from '@patternfly/react-core'; -import { ExternalLinkAltIcon } from '@patternfly/react-icons/dist/js/icons/external-link-alt-icon'; -import { OPENSHIFT_AI_REQUIREMENTS_LINK } from '../../../../common'; - -const OpenshiftAIAmdRequirements = () => { - return ( - <> - - At least two worker nodes. - - Each worker node requires 32 additional GiB of memory and 8 additional CPUs. - - At least one supported AMD GPU. - Nodes that have AMD GPUs installed need to have secure boot disabled. - - Bundle operators: - - OpenShift AI - OpenShift Data Foundation - AMD GPU - Node Feature Discovery - Kernel Module Management - Pipelines - Service Mesh - Serverless - Authorino - - - Learn more . - - - ); -}; - -export default OpenshiftAIAmdRequirements; diff --git a/libs/ui-lib/lib/ocm/components/clusterConfiguration/operators/OpenshiftAINvidiaRequirements.tsx b/libs/ui-lib/lib/ocm/components/clusterConfiguration/operators/OpenshiftAINvidiaRequirements.tsx deleted file mode 100644 index 0f1c647355..0000000000 --- a/libs/ui-lib/lib/ocm/components/clusterConfiguration/operators/OpenshiftAINvidiaRequirements.tsx +++ /dev/null @@ -1,37 +0,0 @@ -import React from 'react'; -import { List, ListItem } from '@patternfly/react-core'; -import { ExternalLinkAltIcon } from '@patternfly/react-icons/dist/js/icons/external-link-alt-icon'; -import { OPENSHIFT_AI_REQUIREMENTS_LINK } from '../../../../common'; - -const OpenshiftAINvidiaRequirements = () => { - return ( - <> - - At least two worker nodes. - - Each worker node requires 32 additional GiB of memory and 8 additional CPUs. - - At least one supported NVIDIA GPU. - - Nodes that have NVIDIA GPUs installed need to have secure boot disabled. - - - Bundle operators: - - OpenShift AI - OpenShift Data Foundation - NVIDIA GPU - Node Feature Discovery - Pipelines - Service Mesh - Serverless - Authorino - - - Learn more . - - - ); -}; - -export default OpenshiftAINvidiaRequirements; diff --git a/libs/ui-lib/lib/ocm/components/clusterConfiguration/operators/OperatorCheckbox.tsx b/libs/ui-lib/lib/ocm/components/clusterConfiguration/operators/OperatorCheckbox.tsx new file mode 100644 index 0000000000..97f26a0f8a --- /dev/null +++ b/libs/ui-lib/lib/ocm/components/clusterConfiguration/operators/OperatorCheckbox.tsx @@ -0,0 +1,209 @@ +import * as React from 'react'; +import { + Checkbox, + FormGroup, + HelperText, + HelperTextItem, + List, + ListItem, + Stack, + StackItem, + Tooltip, +} from '@patternfly/react-core'; +import { useFormikContext } from 'formik'; +import { useSelector } from 'react-redux'; +import { + Bundle, + Cluster, + PreflightHardwareRequirements, +} from '@openshift-assisted/types/assisted-installer-service'; +import { + selectCurrentClusterPermissionsState, + selectIsCurrentClusterSNO, +} from '../../../store/slices/current-cluster/selectors'; +import NewFeatureSupportLevelBadge from '../../../../common/components/newFeatureSupportLevels/NewFeatureSupportLevelBadge'; +import { getFieldId, OperatorsValues, PopoverIcon } from '../../../../common'; +import { useNewFeatureSupportLevel } from '../../../../common/components/newFeatureSupportLevels'; +import { getNewOperators } from './utils'; +import { + OperatorSpec, + useOperatorSpecs, +} from '../../../../common/components/operators/operatorSpecs'; + +const OperatorRequirements = ({ + operatorId, + preflightRequirements, + Requirements, + cluster, +}: { + cluster: Cluster; + operatorId: string; + preflightRequirements: PreflightHardwareRequirements | undefined; + Requirements?: React.ComponentType<{ + cluster: Cluster; + }>; +}) => { + const isSingleNode = useSelector(selectIsCurrentClusterSNO); + + const operator = preflightRequirements?.operators?.find( + (operatorRequirements) => operatorRequirements.operatorName === operatorId, + ); + + const workerRequirements = operator?.requirements?.worker?.quantitative; + const masterRequirements = operator?.requirements?.master?.quantitative; + + const hasWorkerRequirements = !!Object.keys(workerRequirements || {}).length; + const hasMasterRequirements = !!Object.keys(masterRequirements || {}).length; + + if (!hasWorkerRequirements && !hasMasterRequirements && !Requirements) { + return ( + + ); + } + + return ( + + {Requirements && ( + + + + )} + + + {!isSingleNode && hasWorkerRequirements && ( + + Each worker node requires an additional {workerRequirements?.ramMib || 360} MiB of + memory {workerRequirements?.diskSizeGb ? ',' : ' and'}{' '} + {workerRequirements?.cpuCores || 2} CPUs + {workerRequirements?.diskSizeGb + ? ` and ${workerRequirements?.diskSizeGb} storage space` + : ''} + + )} + {hasMasterRequirements && ( + + Each control plane node requires an additional {masterRequirements?.ramMib || 150}{' '} + MiB of memory {masterRequirements?.diskSizeGb ? ',' : ' and'}{' '} + {masterRequirements?.cpuCores || 4} CPUs + {masterRequirements?.diskSizeGb + ? ` and ${masterRequirements?.diskSizeGb} storage space` + : ''} + + )} + + + + } + /> + ); +}; + +const OperatorCheckbox = ({ + bundles, + cluster, + operatorId, + title, + featureId, + notStandalone, + Description, + Requirements, + openshiftVersion, + preflightRequirements, +}: { + bundles: Bundle[]; + cluster: Cluster; + operatorId: string; + openshiftVersion?: string; + preflightRequirements: PreflightHardwareRequirements | undefined; +} & OperatorSpec) => { + const { getFeatureSupportLevel, getFeatureDisabledReason } = useNewFeatureSupportLevel(); + const opSpecs = useOperatorSpecs(); + + const { isViewerMode } = useSelector(selectCurrentClusterPermissionsState); + const { values, setFieldValue } = useFormikContext(); + const fieldId = getFieldId(operatorId, 'input'); + const supportLevel = getFeatureSupportLevel(featureId); + + const isInBundle = values.selectedBundles.some( + (sb) => !!bundles.find((b) => b.id === sb)?.operators?.includes(operatorId), + ); + + const isChecked = values.selectedOperators.includes(operatorId) || isInBundle; + + const parentOperator = preflightRequirements?.operators?.find((op) => + op.dependencies?.includes(operatorId), + ); + + let parentOperatorName = ''; + if ( + parentOperator?.operatorName && + values.selectedOperators.includes(parentOperator.operatorName) + ) { + parentOperatorName = opSpecs[parentOperator.operatorName]?.title || parentOperator.operatorName; + } + + const disabledReason = isInBundle + ? 'This operator is part of a bundle and cannot be deselected.' + : notStandalone + ? 'This operator cannot be installed as a standalone' + : parentOperatorName + ? `This operator is a dependency of ${parentOperatorName}` + : getFeatureDisabledReason(featureId); + + return ( + + + + + + + } + aria-describedby={`${fieldId}-helper`} + isChecked={isChecked} + onChange={(_, checked) => { + setFieldValue( + 'selectedOperators', + getNewOperators( + values.selectedOperators, + operatorId, + preflightRequirements, + checked, + opSpecs, + ), + ); + }} + isDisabled={isViewerMode || !!disabledReason} + description={ + !!Description && ( + + + + + + ) + } + /> + + ); +}; + +export default OperatorCheckbox; diff --git a/libs/ui-lib/lib/ocm/components/clusterConfiguration/operators/OscCheckbox.tsx b/libs/ui-lib/lib/ocm/components/clusterConfiguration/operators/OscCheckbox.tsx deleted file mode 100644 index f5212f57c3..0000000000 --- a/libs/ui-lib/lib/ocm/components/clusterConfiguration/operators/OscCheckbox.tsx +++ /dev/null @@ -1,73 +0,0 @@ -import React from 'react'; -import { FormGroup, HelperText, HelperTextItem, Tooltip } from '@patternfly/react-core'; -import { ExternalLinkAltIcon } from '@patternfly/react-icons/dist/js/icons/external-link-alt-icon'; -import { getFieldId, PopoverIcon, OSC_REQUIREMENTS_LINK, OSC_LINK } from '../../../../common'; -import { OcmCheckboxField } from '../../ui/OcmFormFields'; -import NewFeatureSupportLevelBadge from '../../../../common/components/newFeatureSupportLevels/NewFeatureSupportLevelBadge'; -import { SupportLevel } from '@openshift-assisted/types/assisted-installer-service'; - -const OSC_FIELD_NAME = 'useOsc'; - -type OscLabelProps = { - disabledReason?: string; - supportLevel?: SupportLevel; -}; - -const OscLabel = ({ disabledReason, supportLevel }: OscLabelProps) => { - return ( - <> - - - Learn more about the requirements for OpenShift sandboxed containers{' '} - . - - } - /> - - - ); -}; - -const OscHelperText = () => { - return ( - - - OpenShift sandboxed containers support for OpenShift Container Platform provides users with - built-in support for running Kata Containers as an additional optional runtime. It provides - an additional virtualization machine(VM) isolation layer for pods.{' '} - - {'Learn more'} - - - - ); -}; - -const OscCheckbox = ({ - disabledReason, - supportLevel, -}: { - disabledReason?: string; - supportLevel?: SupportLevel | undefined; -}) => { - const fieldId = getFieldId(OSC_FIELD_NAME, 'input'); - - return ( - - } - isDisabled={!!disabledReason} - helperText={} - /> - - ); -}; - -export default OscCheckbox; diff --git a/libs/ui-lib/lib/ocm/components/clusterConfiguration/operators/PipelinesChekbox.tsx b/libs/ui-lib/lib/ocm/components/clusterConfiguration/operators/PipelinesChekbox.tsx deleted file mode 100644 index 9ee35eddd7..0000000000 --- a/libs/ui-lib/lib/ocm/components/clusterConfiguration/operators/PipelinesChekbox.tsx +++ /dev/null @@ -1,63 +0,0 @@ -import React from 'react'; -import { FormGroup, HelperText, HelperTextItem, Tooltip } from '@patternfly/react-core'; -import { getFieldId, PIPELINES_OPERATOR_LINK } from '../../../../common'; -import { OcmCheckboxField } from '../../ui/OcmFormFields'; -import { SupportLevel } from '@openshift-assisted/types/./assisted-installer-service'; -import NewFeatureSupportLevelBadge from '../../../../common/components/newFeatureSupportLevels/NewFeatureSupportLevelBadge'; -import ExternalLinkAltIcon from '@patternfly/react-icons/dist/js/icons/external-link-alt-icon'; - -const PIPELINES_FIELD_NAME = 'usePipelines'; - -const PipelinesLabel = ({ - disabledReason, - supportLevel, -}: { - disabledReason?: string; - supportLevel?: SupportLevel; -}) => { - return ( - <> - - - - ); -}; - -const PipelinesHelperText = () => { - return ( - - - Cloud-native continuous integration and delivery (CI/CD) solution for building pipelines - using Tekton.{' '} - - {'Learn more'} - - - - ); -}; - -const PipelinesCheckbox = ({ - disabledReason, - supportLevel, -}: { - disabledReason?: string; - supportLevel?: SupportLevel | undefined; -}) => { - const fieldId = getFieldId(PIPELINES_FIELD_NAME, 'input'); - - return ( - - } - helperText={} - isDisabled={!!disabledReason} - /> - - ); -}; - -export default PipelinesCheckbox; diff --git a/libs/ui-lib/lib/ocm/components/clusterConfiguration/operators/SelfNodeRemediationCheckbox.tsx b/libs/ui-lib/lib/ocm/components/clusterConfiguration/operators/SelfNodeRemediationCheckbox.tsx deleted file mode 100644 index 070accf280..0000000000 --- a/libs/ui-lib/lib/ocm/components/clusterConfiguration/operators/SelfNodeRemediationCheckbox.tsx +++ /dev/null @@ -1,68 +0,0 @@ -import React from 'react'; -import { FormGroup, HelperText, HelperTextItem, Tooltip } from '@patternfly/react-core'; -import { getFieldId, SELF_NODE_REMEDIATION_LINK } from '../../../../common'; -import { ExternalLinkAltIcon } from '@patternfly/react-icons/dist/js/icons/external-link-alt-icon'; -import { OcmCheckboxField } from '../../ui/OcmFormFields'; -import NewFeatureSupportLevelBadge from '../../../../common/components/newFeatureSupportLevels/NewFeatureSupportLevelBadge'; -import { SupportLevel } from '@openshift-assisted/types/./assisted-installer-service'; - -const SELF_NODE_REMEDIATION_FIELD_NAME = 'useSelfNodeRemediation'; -const SELF_NODE_REMEDIATION_FEATURE_ID = 'SELF_NODE_REMEDIATION'; - -const SelfNodeRemediationLabel = ({ - disabledReason, - supportLevel, -}: { - disabledReason?: string; - supportLevel?: SupportLevel; -}) => { - return ( - <> - - - - ); -}; - -const SelfNodeRemediationHelperText = () => { - return ( - - - Allows nodes to reboot themselves when they become unhealthy.{' '} - - {'Learn more'} - - - - ); -}; - -const SelfNodeRemediationCheckbox = ({ - disabledReason, - supportLevel, -}: { - disabledReason?: string; - supportLevel?: SupportLevel | undefined; -}) => { - const fieldId = getFieldId(SELF_NODE_REMEDIATION_FIELD_NAME, 'input'); - - return ( - - - } - helperText={} - isDisabled={!!disabledReason} - /> - - ); -}; - -export default SelfNodeRemediationCheckbox; diff --git a/libs/ui-lib/lib/ocm/components/clusterConfiguration/operators/ServerlessCheckbox.tsx b/libs/ui-lib/lib/ocm/components/clusterConfiguration/operators/ServerlessCheckbox.tsx deleted file mode 100644 index 320b920c79..0000000000 --- a/libs/ui-lib/lib/ocm/components/clusterConfiguration/operators/ServerlessCheckbox.tsx +++ /dev/null @@ -1,62 +0,0 @@ -import React from 'react'; -import { FormGroup, HelperText, HelperTextItem, Tooltip } from '@patternfly/react-core'; -import { getFieldId, SERVERLESS_OPERATOR_LINK } from '../../../../common'; -import { OcmCheckboxField } from '../../ui/OcmFormFields'; -import { SupportLevel } from '@openshift-assisted/types/./assisted-installer-service'; -import NewFeatureSupportLevelBadge from '../../../../common/components/newFeatureSupportLevels/NewFeatureSupportLevelBadge'; -import ExternalLinkAltIcon from '@patternfly/react-icons/dist/js/icons/external-link-alt-icon'; - -const SERVERLESS_FIELD_NAME = 'useServerless'; - -const ServerlessLabel = ({ - disabledReason, - supportLevel, -}: { - disabledReason?: string; - supportLevel?: SupportLevel; -}) => { - return ( - <> - - - - ); -}; - -const ServerlessHelperText = () => { - return ( - - - Deploy workflow applications based on the CNCF Serverless Workflow specification.{' '} - - {'Learn more'} - - - - ); -}; - -const ServerlessCheckbox = ({ - disabledReason, - supportLevel, -}: { - disabledReason?: string; - supportLevel?: SupportLevel | undefined; -}) => { - const fieldId = getFieldId(SERVERLESS_FIELD_NAME, 'input'); - - return ( - - } - helperText={} - isDisabled={!!disabledReason} - /> - - ); -}; - -export default ServerlessCheckbox; diff --git a/libs/ui-lib/lib/ocm/components/clusterConfiguration/operators/ServicemeshCheckbox.tsx b/libs/ui-lib/lib/ocm/components/clusterConfiguration/operators/ServicemeshCheckbox.tsx deleted file mode 100644 index f00f1ae0d1..0000000000 --- a/libs/ui-lib/lib/ocm/components/clusterConfiguration/operators/ServicemeshCheckbox.tsx +++ /dev/null @@ -1,64 +0,0 @@ -import React from 'react'; -import { FormGroup, HelperText, HelperTextItem, Tooltip } from '@patternfly/react-core'; -import { getFieldId, getServiceMeshLink } from '../../../../common'; -import { OcmCheckboxField } from '../../ui/OcmFormFields'; -import { SupportLevel } from '@openshift-assisted/types/./assisted-installer-service'; -import NewFeatureSupportLevelBadge from '../../../../common/components/newFeatureSupportLevels/NewFeatureSupportLevelBadge'; -import ExternalLinkAltIcon from '@patternfly/react-icons/dist/js/icons/external-link-alt-icon'; - -const SERVICEMESH_FIELD_NAME = 'useServicemesh'; - -const ServiceMeshLabel = ({ - disabledReason, - supportLevel, -}: { - disabledReason?: string; - supportLevel?: SupportLevel; -}) => { - return ( - <> - - - - ); -}; - -const ServiceMeshHelperText = ({ openshiftVersion }: { openshiftVersion?: string }) => { - return ( - - - Platform that provides behavioral insight and operational control over a service mesh.{' '} - - {'Learn more'} - - - - ); -}; - -const ServiceMeshCheckbox = ({ - disabledReason, - supportLevel, - openshiftVersion, -}: { - disabledReason?: string; - supportLevel?: SupportLevel | undefined; - openshiftVersion?: string; -}) => { - const fieldId = getFieldId(SERVICEMESH_FIELD_NAME, 'input'); - - return ( - - } - helperText={} - isDisabled={!!disabledReason} - /> - - ); -}; - -export default ServiceMeshCheckbox; diff --git a/libs/ui-lib/lib/ocm/components/clusterConfiguration/operators/SupportedOperators.tsx b/libs/ui-lib/lib/ocm/components/clusterConfiguration/operators/SupportedOperators.tsx deleted file mode 100644 index 8e6c3c4178..0000000000 --- a/libs/ui-lib/lib/ocm/components/clusterConfiguration/operators/SupportedOperators.tsx +++ /dev/null @@ -1,140 +0,0 @@ -import React from 'react'; -import CnvCheckbox from './CnvCheckbox'; -import MtvOperatorCheckbox from './MtvOperatorCheckbox'; -import MceCheckbox from './MceCheckbox'; -import LvmCheckbox from './LvmCheckbox'; -import OdfCheckbox from './OdfCheckbox'; -import OpenShiftAICheckbox from './OpenShiftAICheckbox'; -import OscCheckbox from './OscCheckbox'; -import LsoCheckbox from './LsoCheckbox'; -import NodeFeatureDiscoveryCheckbox from './NodeFeatureDiscoveryCheckbox'; -import NmstateCheckbox from './NmstateCheckbox'; -import ServerlessCheckbox from './ServerlessCheckbox'; -import AuthorinoCheckbox from './AuthorinoCheckbox'; -import PipelinesCheckbox from './PipelinesChekbox'; -import ServiceMeshCheckbox from './ServicemeshCheckbox'; -import NvidiaGpuCheckbox from './NvidiaGpuCheckbox'; -import AmdGpuCheckbox from './AmdGpuCheckbox'; -import KmmCheckbox from './KmmCheckbox'; -import NodeHealthcheckCheckbox from './NodeHealthcheckCheckbox'; -import SelfNodeRemediationCheckbox from './SelfNodeRemediationCheckbox'; -import FenceAgentsRemediationCheckbox from './FenceAgentsRemediationCheckbox'; -import NodeMaintenanceCheckbox from './NodeMaintenanceOperatorCheckbox'; -import KubeDeschedulerCheckbox from './KubeDeschedulerCheckbox'; -import { - FeatureId, - OPERATOR_NAME_AMD_GPU, - OPERATOR_NAME_AUTHORINO, - OPERATOR_NAME_CNV, - OPERATOR_NAME_KMM, - OPERATOR_NAME_LSO, - OPERATOR_NAME_LVM, - OPERATOR_NAME_MCE, - OPERATOR_NAME_MTV, - OPERATOR_NAME_NMSTATE, - OPERATOR_NAME_NODE_FEATURE_DISCOVERY, - OPERATOR_NAME_NVIDIA_GPU, - OPERATOR_NAME_ODF, - OPERATOR_NAME_OPENSHIFT_AI, - OPERATOR_NAME_OSC, - OPERATOR_NAME_PIPELINES, - OPERATOR_NAME_SERVERLESS, - OPERATOR_NAME_SERVICEMESH, - OPERATOR_NAME_NODE_HEALTHCHECK, - OPERATOR_NAME_SELF_NODE_REMEDIATION, - OPERATOR_NAME_FENCE_AGENTS_REMEDIATION, - OPERATOR_NAME_NODE_MAINTENANCE, - OPERATOR_NAME_KUBE_DESCHEDULER, -} from '../../../../common'; -import { SupportLevel } from '@openshift-assisted/types/./assisted-installer-service'; - -interface OperatorProps { - clusterId: string; - openshiftVersion?: string; - isVersionEqualsOrMajorThan4_15: boolean; - isSNO: boolean; - disabledReason?: string; - supportLevel?: SupportLevel | undefined; -} - -export interface ComponentItem { - component: JSX.Element; - label: string; -} - -export const operatorComponentMap: Record JSX.Element> = { - cnv: (props) => ( - - ), - mtv: (props) => , - mce: (props) => , - lvm: (props) => , - odf: (props) => , - 'openshift-ai': (props) => , - osc: (props) => , - lso: (props) => , - 'node-feature-discovery': (props) => , - nmstate: (props) => , - serverless: (props) => , - authorino: (props) => , - pipelines: (props) => , - servicemesh: (props) => , - 'nvidia-gpu': (props) => , - 'amd-gpu': (props) => , - kmm: (props) => , - 'node-healthcheck': (props) => , - 'self-node-remediation': (props) => , - 'fence-agents-remediation': (props) => , - 'node-maintenance': (props) => , - 'kube-descheduler': (props) => , -}; - -export const mapOperatorsToFieldIds: { [key: string]: string } = { - [OPERATOR_NAME_ODF]: 'useOpenShiftDataFoundation', - [OPERATOR_NAME_LVM]: 'useOdfLogicalVolumeManager', - [OPERATOR_NAME_CNV]: 'useContainerNativeVirtualization', - [OPERATOR_NAME_MCE]: 'useMultiClusterEngine', - [OPERATOR_NAME_MTV]: 'useMigrationToolkitforVirtualization', - [OPERATOR_NAME_OPENSHIFT_AI]: 'useOpenShiftAI', - [OPERATOR_NAME_OSC]: 'useOsc', - [OPERATOR_NAME_NODE_FEATURE_DISCOVERY]: 'useNodeFeatureDiscovery', - [OPERATOR_NAME_NMSTATE]: 'useNmstate', - [OPERATOR_NAME_LSO]: 'useLso', - [OPERATOR_NAME_SERVERLESS]: 'useServerless', - [OPERATOR_NAME_AUTHORINO]: 'useAuthorino', - [OPERATOR_NAME_PIPELINES]: 'usePipelines', - [OPERATOR_NAME_SERVICEMESH]: 'useServicemesh', - [OPERATOR_NAME_NVIDIA_GPU]: 'useNvidiaGpu', - [OPERATOR_NAME_AMD_GPU]: 'useAmdGpu', - [OPERATOR_NAME_KMM]: 'useKmm', - [OPERATOR_NAME_NODE_HEALTHCHECK]: 'useNodeHealthcheck', - [OPERATOR_NAME_SELF_NODE_REMEDIATION]: 'useSelfNodeRemediation', - [OPERATOR_NAME_FENCE_AGENTS_REMEDIATION]: 'useFenceAgentsRemediation', - [OPERATOR_NAME_NODE_MAINTENANCE]: 'useNodeMaintenance', - [OPERATOR_NAME_KUBE_DESCHEDULER]: 'useKubeDescheduler', -}; - -export const mapOperatorIdToFeatureId: { [key: string]: FeatureId } = { - [OPERATOR_NAME_ODF]: 'ODF', - [OPERATOR_NAME_LVM]: 'LVM', - [OPERATOR_NAME_CNV]: 'CNV', - [OPERATOR_NAME_MCE]: 'MCE', - [OPERATOR_NAME_MTV]: 'MTV', - [OPERATOR_NAME_OPENSHIFT_AI]: 'OPENSHIFT_AI', - [OPERATOR_NAME_OSC]: 'OSC', - [OPERATOR_NAME_NODE_FEATURE_DISCOVERY]: 'NODE_FEATURE_DISCOVERY', - [OPERATOR_NAME_NMSTATE]: 'NMSTATE', - [OPERATOR_NAME_LSO]: 'LSO', - [OPERATOR_NAME_SERVERLESS]: 'SERVERLESS', - [OPERATOR_NAME_AUTHORINO]: 'AUTHORINO', - [OPERATOR_NAME_PIPELINES]: 'PIPELINES', - [OPERATOR_NAME_SERVICEMESH]: 'SERVICEMESH', - [OPERATOR_NAME_NVIDIA_GPU]: 'NVIDIA_GPU', - [OPERATOR_NAME_AMD_GPU]: 'AMD_GPU', - [OPERATOR_NAME_KMM]: 'KMM', - [OPERATOR_NAME_NODE_HEALTHCHECK]: 'NODE_HEALTHCHECK', - [OPERATOR_NAME_SELF_NODE_REMEDIATION]: 'SELF_NODE_REMEDIATION', - [OPERATOR_NAME_FENCE_AGENTS_REMEDIATION]: 'FENCE_AGENTS_REMEDIATION', - [OPERATOR_NAME_NODE_MAINTENANCE]: 'NODE_MAINTENANCE', - [OPERATOR_NAME_KUBE_DESCHEDULER]: 'KUBE_DESCHEDULER', -}; diff --git a/libs/ui-lib/lib/ocm/components/clusterConfiguration/operators/VirtualizationRequirements.tsx b/libs/ui-lib/lib/ocm/components/clusterConfiguration/operators/VirtualizationRequirements.tsx deleted file mode 100644 index 41144324ec..0000000000 --- a/libs/ui-lib/lib/ocm/components/clusterConfiguration/operators/VirtualizationRequirements.tsx +++ /dev/null @@ -1,35 +0,0 @@ -import React from 'react'; -import { List, ListItem } from '@patternfly/react-core'; - -const VirtualizationRequirements = () => { - return ( - <> - - - Enabled CPU virtualization support in BIOS (Intel-VT / AMD-V) on all nodes. - - - Each control plane node requires an additional 1024 MiB of memory and 3 CPUs. - - Each worker node requires an additional 1024 MiB of memory and 5 CPUs. - - Additional resources may be required to support the selected storage operator. - - - Bundle operators: - - OpenShift Virtualization - Migration Toolkit for Virtualization - Nmstate - Storage operator (either LSO/LVMS) - Node Healthcheck - Self Node Remediation - Fence Agents Remediation - Node Maintenance - Kube Descheduler - - - ); -}; - -export default VirtualizationRequirements; diff --git a/libs/ui-lib/lib/ocm/components/clusterConfiguration/operators/bundleSpecs.tsx b/libs/ui-lib/lib/ocm/components/clusterConfiguration/operators/bundleSpecs.tsx new file mode 100644 index 0000000000..b50742d225 --- /dev/null +++ b/libs/ui-lib/lib/ocm/components/clusterConfiguration/operators/bundleSpecs.tsx @@ -0,0 +1,64 @@ +import * as React from 'react'; +import { List, ListItem } from '@patternfly/react-core'; +import { FeatureId, OPENSHIFT_AI_REQUIREMENTS_LINK } from '../../../../common'; + +export type BundleSpec = { + featureId?: FeatureId; + noSNO?: boolean; + incompatibleBundles?: string[]; + Description: React.ComponentType; + docsLink?: string; +}; + +export const bundleSpecs: { [key: string]: BundleSpec } = { + virtualization: { + Description: () => ( + + + Enabled CPU virtualization support in BIOS (Intel-VT / AMD-V) on all nodes. + + + Each control plane node requires an additional 1024 MiB of memory and 3 CPUs. + + Each worker node requires an additional 1024 MiB of memory and 5 CPUs. + + Additional resources may be required to support the selected storage operator. + + + ), + }, + 'openshift-ai-nvidia': { + featureId: 'OPENSHIFT_AI', + noSNO: true, + incompatibleBundles: ['openshift-ai-amd'], + Description: () => ( + + At least two worker nodes. + + Each worker node requires 32 additional GiB of memory and 8 additional CPUs. + + At least one supported NVIDIA GPU. + + Nodes that have NVIDIA GPUs installed need to have secure boot disabled. + + + ), + docsLink: OPENSHIFT_AI_REQUIREMENTS_LINK, + }, + 'openshift-ai-amd': { + featureId: 'OPENSHIFT_AI', + noSNO: true, + incompatibleBundles: ['openshift-ai-nvidia'], + Description: () => ( + + At least two worker nodes. + + Each worker node requires 32 additional GiB of memory and 8 additional CPUs. + + At least one supported AMD GPU. + Nodes that have AMD GPUs installed need to have secure boot disabled. + + ), + docsLink: OPENSHIFT_AI_REQUIREMENTS_LINK, + }, +}; diff --git a/libs/ui-lib/lib/ocm/components/clusterConfiguration/operators/utils.tsx b/libs/ui-lib/lib/ocm/components/clusterConfiguration/operators/utils.tsx index f6c85c88cc..db81e9dcd3 100644 --- a/libs/ui-lib/lib/ocm/components/clusterConfiguration/operators/utils.tsx +++ b/libs/ui-lib/lib/ocm/components/clusterConfiguration/operators/utils.tsx @@ -1,38 +1,95 @@ import { - OPERATOR_NAME_LVM, - FeatureSupportLevelData, - ExposedOperatorName, - OPERATOR_NAME_LVMS, -} from '../../../../common'; -import { Cluster } from '@openshift-assisted/types/assisted-installer-service'; - -export const getActualLVMOperatorName = ({ - openshiftVersion, - featureSupportLevel, - operator = OPERATOR_NAME_LVM, -}: { - openshiftVersion: Cluster['openshiftVersion']; - featureSupportLevel: FeatureSupportLevelData; - operator?: ExposedOperatorName; -}) => { - if ( - operator === OPERATOR_NAME_LVM && - featureSupportLevel.getFeatureSupportLevel(openshiftVersion || '', 'LVM') === 'supported' - ) { - return OPERATOR_NAME_LVMS; + Bundle, + PreflightHardwareRequirements, +} from '@openshift-assisted/types/assisted-installer-service'; +import { OperatorSpec } from '../../../../common/components/operators/operatorSpecs'; + +const getOperatorDependencies = ( + operatorId: string, + preflightRequirements: PreflightHardwareRequirements | undefined, + dependencies: Set = new Set(), +) => { + const originalSize = dependencies.size; + const deps = + preflightRequirements?.operators?.find(({ operatorName }) => operatorName === operatorId) + ?.dependencies || []; + + deps.forEach((dep) => dependencies.add(dep)); + + if (originalSize !== dependencies.size) { + // add deps of new deps + deps.forEach((op) => { + getOperatorDependencies(op, preflightRequirements, dependencies); + }); + } + return [...dependencies]; +}; + +export const getNewOperators = ( + currentOperators: string[], + operatorId: string, + preflightRequirements: PreflightHardwareRequirements | undefined, + add: boolean, + opSpecs: { [key: string]: OperatorSpec }, +): string[] => { + const dependencies = getOperatorDependencies(operatorId, preflightRequirements); + + if (add) { + return [...new Set([...currentOperators, ...dependencies, operatorId])]; } - return operator; + const newOperators = currentOperators.filter((op) => op !== operatorId); + // uncheck unneeded not-standalone dependencies too + const notStandaloneDeps = dependencies + .filter((dep) => opSpecs[dep]?.notStandalone) + .filter((dep) => + // some other operator may still depend on the not-standalone operator + newOperators.every((op) => !getOperatorDependencies(op, preflightRequirements).includes(dep)), + ); + + return currentOperators.filter((op) => op !== operatorId && !notStandaloneDeps.includes(op)); +}; + +const getBundleOperators = ( + bundle: Bundle, + preflightRequirements: PreflightHardwareRequirements | undefined, +) => { + const bundleOperators = new Set(bundle.operators || []); + bundle.operators?.forEach((op) => { + getOperatorDependencies(op, preflightRequirements).forEach((dep) => bundleOperators.add(dep)); + }); + return [...bundleOperators]; }; -export const getOdfLvmsText = (isSingleNode: boolean, OPCVersionGreaterThan4_15: boolean) => { - if (OPCVersionGreaterThan4_15) { - return 'OpenShift Data Foundation (recommended for creating additional on-premise clusters), Logical Volume Manager Storage or another persistent storage service'; - } else { - if (isSingleNode) { - return 'Logical Volume Manager Storage or another persistent storage service'; - } else { - return 'OpenShift Data Foundation (recommended for creating additional on-premise clusters) or another persistent storage service'; - } +export const getNewBundleOperators = ( + currentOperators: string[], + currentBundles: string[], + allBundles: Bundle[], + newBundle: Bundle, + preflightRequirements: PreflightHardwareRequirements | undefined, + add: boolean, +): string[] => { + const newBundleOperators = getBundleOperators(newBundle, preflightRequirements); + + if (add) { + return [...new Set([...currentOperators, ...newBundleOperators])]; } + + const newBundles = currentBundles.filter((b) => b !== newBundle.id); + const operatorsToKeep = [ + ...newBundles.reduce((acc, bundleId) => { + const bundle = allBundles.find(({ id }) => id === bundleId); + if (bundle) { + const bundleOperators = getBundleOperators(bundle, preflightRequirements); + bundleOperators.forEach((op) => { + acc.add(op); + }); + } + return acc; + }, new Set()), + ]; + + const operatorsToRemove = newBundleOperators.filter((op) => !operatorsToKeep.includes(op)); + + return currentOperators.filter((op) => !operatorsToRemove.includes(op)); }; diff --git a/libs/ui-lib/lib/ocm/components/clusterConfiguration/review/ReviewOperatorsTable.tsx b/libs/ui-lib/lib/ocm/components/clusterConfiguration/review/ReviewOperatorsTable.tsx index 7d6a1c5b6b..59e28118f8 100644 --- a/libs/ui-lib/lib/ocm/components/clusterConfiguration/review/ReviewOperatorsTable.tsx +++ b/libs/ui-lib/lib/ocm/components/clusterConfiguration/review/ReviewOperatorsTable.tsx @@ -1,53 +1,56 @@ import React from 'react'; import { Table, TableVariant, Tbody, Td, Tr } from '@patternfly/react-table'; -import { - ExposedOperatorNames, - hasEnabledOperators, - genericTableRowKey, - operatorLabels, -} from '../../../../common'; -import { useTranslation } from '../../../../common/hooks/use-translation-wrapper'; -import { useNewFeatureSupportLevel } from '../../../../common/components/newFeatureSupportLevels'; +import { genericTableRowKey } from '../../../../common'; import { Cluster } from '@openshift-assisted/types/assisted-installer-service'; +import { TableSummaryExpandable } from './TableSummaryExpandable'; +import { useOperatorSpecs } from '../../../../common/components/operators/operatorSpecs'; export const ReviewOperatorsTable = ({ cluster }: { cluster: Cluster }) => { - const { t } = useTranslation(); - const featureSupportLevel = useNewFeatureSupportLevel(); - const operatorNames = operatorLabels(t, featureSupportLevel); + const opSpecs = useOperatorSpecs(); + const rows = React.useMemo( + () => + cluster.monitoredOperators + ?.filter(({ name }) => !!name && !!opSpecs[name]) + .map((op) => { + const opId = op.name as string; + return { + rowId: `operator-${opId}`, + cells: [ + { title: opSpecs[opId].title }, + { + title: 'Enabled', + props: { 'data-testid': `operator-${opId}` }, + }, + ], + }; + }), + [cluster.monitoredOperators, opSpecs], + ); - const rows = React.useMemo(() => { - return ExposedOperatorNames.filter((operator) => - hasEnabledOperators(cluster.monitoredOperators, operator), - ).map((operator) => ({ - rowId: `operator-${operator}`, - cells: [ - { title: operatorNames[operator] }, - { - title: 'Enabled', - props: { 'data-testid': `operator-${operator}` }, - }, - ], - })); - }, [cluster.monitoredOperators, operatorNames]); + if (!rows?.length) { + return null; + } return ( - - - {rows.map((row, i) => ( - - {row.cells.map((cell, j) => ( - - ))} - - ))} - -
- {cell.title} -
+ + + + {rows.map((row, i) => ( + + {row.cells.map((cell, j) => ( + + ))} + + ))} + +
+ {cell.title} +
+
); }; diff --git a/libs/ui-lib/lib/ocm/components/clusterConfiguration/review/ReviewSummary.tsx b/libs/ui-lib/lib/ocm/components/clusterConfiguration/review/ReviewSummary.tsx index accedfae58..92c19653ba 100644 --- a/libs/ui-lib/lib/ocm/components/clusterConfiguration/review/ReviewSummary.tsx +++ b/libs/ui-lib/lib/ocm/components/clusterConfiguration/review/ReviewSummary.tsx @@ -3,15 +3,10 @@ import { ExpandableSection } from '@patternfly/react-core'; import { DetailItem, DetailList, - hasEnabledOperators, isClusterPlatformTypeVM, - OPERATOR_NAME_CNV, - OPERATOR_NAME_LVM, - OPERATOR_NAME_ODF, ReviewHostsInventory, SupportedPlatformType, RenderIf, - OPERATOR_NAME_MCE, } from '../../../../common'; import { ReviewClusterDetailTable, @@ -26,11 +21,6 @@ import useClusterCustomManifests from '../../../hooks/useClusterCustomManifests' import { Cluster } from '@openshift-assisted/types/assisted-installer-service'; export const ReviewSummaryContent = ({ cluster }: { cluster: Cluster }) => { - const showOperatorsSummary = - hasEnabledOperators(cluster.monitoredOperators, OPERATOR_NAME_CNV) || - hasEnabledOperators(cluster.monitoredOperators, OPERATOR_NAME_ODF) || - hasEnabledOperators(cluster.monitoredOperators, OPERATOR_NAME_LVM) || - hasEnabledOperators(cluster.monitoredOperators, OPERATOR_NAME_MCE); const { customManifests } = useClusterCustomManifests(cluster.id, false); return ( <> @@ -38,11 +28,7 @@ export const ReviewSummaryContent = ({ cluster }: { cluster: Cluster }) => { - {showOperatorsSummary && ( - - - - )} + <> diff --git a/libs/ui-lib/lib/ocm/components/clusterDetail/ProgressBarAlerts.tsx b/libs/ui-lib/lib/ocm/components/clusterDetail/ProgressBarAlerts.tsx index e5f8c51dee..207c765cd6 100644 --- a/libs/ui-lib/lib/ocm/components/clusterDetail/ProgressBarAlerts.tsx +++ b/libs/ui-lib/lib/ocm/components/clusterDetail/ProgressBarAlerts.tsx @@ -1,25 +1,18 @@ import React from 'react'; import { Alert, AlertActionLink, Text, TextContent } from '@patternfly/react-core'; import { pluralize } from 'humanize-plus'; -import { TFunction } from 'i18next'; import { RenderIf, toSentence, canDownloadClusterLogs, getReportIssueLink, - operatorLabels, useAlerts, - OperatorName, } from '../../../common'; -import { useTranslation } from '../../../common/hooks/use-translation-wrapper'; import { downloadClusterInstallationLogs } from './utils'; import { useModalDialogsContext } from '../hosts/ModalDialogsContext'; import { isInOcm } from '../../../common/api'; -import { - NewFeatureSupportLevelData, - useNewFeatureSupportLevel, -} from '../../../common/components/newFeatureSupportLevels'; import { Cluster, MonitoredOperator } from '@openshift-assisted/types/assisted-installer-service'; +import { useOperatorSpecs } from '../../../common/components/operators/operatorSpecs'; type InstallationProgressWarningProps = { cluster: Cluster; @@ -32,26 +25,6 @@ type InstallationProgressWarningProps = { isCriticalNumberOfWorkersFailed?: boolean; }; -const getFailedOperatorsNames = ( - failedOperators: MonitoredOperator[], - openshiftVersion: Cluster['openshiftVersion'], - featureSupportLevel: NewFeatureSupportLevelData, - t: TFunction, -): string => { - let failedOperatorsNames = ''; - const translatedOperatorLabels = operatorLabels(t, featureSupportLevel); - for (let i = 0; i < failedOperators.length; i++) { - const operatorName = (failedOperators[i].name as OperatorName) || ''; - const operatorLabel: string = (operatorName && translatedOperatorLabels[operatorName]) || ''; - if (i > 0) { - if (i === failedOperators.length - 1) failedOperatorsNames += ' and '; - else failedOperatorsNames += ', '; - } - failedOperatorsNames += `${operatorLabel} (${operatorName.toUpperCase()})`; - } - return failedOperatorsNames; -}; - const getFailedHostsMessage = ( failedHosts: number, totalHosts: number, @@ -93,8 +66,7 @@ export const HostInstallationWarning = ({ message, }: InstallationProgressWarningProps) => { const { addAlert, clearAlerts } = useAlerts(); - const { t } = useTranslation(); - const featureSupportLevel = useNewFeatureSupportLevel(); + const opSpecs = useOperatorSpecs(); return ( <> @@ -121,12 +93,7 @@ export const HostInstallationWarning = ({ 0}>

- {getFailedOperatorsNames( - failedOperators, - cluster.openshiftVersion, - featureSupportLevel, - t, - )}{' '} + {failedOperators.map(({ name }) => opSpecs[name || '']?.title || name).join(' and ')}{' '} failed to install. Due to this, the cluster will be degraded, but you can try to install the operator from the Operator Hub. Please check the installation log for more information. diff --git a/libs/ui-lib/lib/ocm/components/clusterWizard/ClusterWizardContextProvider.tsx b/libs/ui-lib/lib/ocm/components/clusterWizard/ClusterWizardContextProvider.tsx index 5aaace5f11..67ca2c5e0c 100644 --- a/libs/ui-lib/lib/ocm/components/clusterWizard/ClusterWizardContextProvider.tsx +++ b/libs/ui-lib/lib/ocm/components/clusterWizard/ClusterWizardContextProvider.tsx @@ -114,7 +114,6 @@ const ClusterWizardContextProvider = ({ staticIpInfo, cluster?.status, cluster?.hosts, - isSingleClusterFeatureEnabled, customManifestsStepNeedsToBeFilled, ); diff --git a/libs/ui-lib/lib/ocm/components/clusterWizard/Operators.tsx b/libs/ui-lib/lib/ocm/components/clusterWizard/Operators.tsx index 7c579a4c65..d618d53fe1 100644 --- a/libs/ui-lib/lib/ocm/components/clusterWizard/Operators.tsx +++ b/libs/ui-lib/lib/ocm/components/clusterWizard/Operators.tsx @@ -2,33 +2,13 @@ import React from 'react'; import { useDispatch } from 'react-redux'; import { useLocation, useNavigate } from 'react-router-dom-v5-compat'; import { Formik, FormikConfig, useFormikContext } from 'formik'; +import { Cluster } from '@openshift-assisted/types/assisted-installer-service'; import { ClusterWizardStep, getFormikErrorFields, - OPERATOR_NAME_AMD_GPU, - OPERATOR_NAME_AUTHORINO, - OPERATOR_NAME_CNV, - OPERATOR_NAME_KMM, - OPERATOR_NAME_LSO, - OPERATOR_NAME_LVM, - OPERATOR_NAME_MCE, - OPERATOR_NAME_MTV, - OPERATOR_NAME_NMSTATE, - OPERATOR_NAME_NODE_FEATURE_DISCOVERY, - OPERATOR_NAME_NVIDIA_GPU, - OPERATOR_NAME_ODF, - OPERATOR_NAME_OPENSHIFT_AI, - OPERATOR_NAME_OSC, - OPERATOR_NAME_PIPELINES, - OPERATOR_NAME_SERVERLESS, - OPERATOR_NAME_SERVICEMESH, - OPERATOR_NAME_NODE_HEALTHCHECK, - OPERATOR_NAME_SELF_NODE_REMEDIATION, - OPERATOR_NAME_FENCE_AGENTS_REMEDIATION, - OPERATOR_NAME_NODE_MAINTENANCE, - OPERATOR_NAME_KUBE_DESCHEDULER, OperatorsValues, - selectMonitoredOperators, + selectOlmOperators, + UISettingsValues, useAlerts, useFormikAutoSave, } from '../../../common'; @@ -36,40 +16,18 @@ import { useClusterWizardContext } from './ClusterWizardContext'; import ClusterWizardFooter from '../clusterWizard/ClusterWizardFooter'; import ClusterWizardNavigation from '../clusterWizard/ClusterWizardNavigation'; import { OperatorsStep } from './OperatorsStep'; -import { ClustersService, OperatorsService } from '../../services'; +import { ClustersService } from '../../services'; import { setServerUpdateError, updateCluster } from '../../store/slices/current-cluster/slice'; import { getApiErrorMessage, handleApiError, isUnknownServerError } from '../../../common/api'; import { canNextOperators } from './wizardTransition'; -import { Cluster, MonitoredOperator } from '@openshift-assisted/types/assisted-installer-service'; -export const getOperatorsInitialValues = ( - monitoredOperators: MonitoredOperator[], +const getOperatorsInitialValues = ( + uiSettings: UISettingsValues | undefined, + cluster: Cluster, ): OperatorsValues => { - const isOperatorEnabled = (operatorNames: string[]) => - !!monitoredOperators.find((operator) => operatorNames.includes(operator.name || '')); return { - useOpenShiftDataFoundation: isOperatorEnabled([OPERATOR_NAME_ODF]), - useOdfLogicalVolumeManager: isOperatorEnabled([OPERATOR_NAME_LVM]), - useContainerNativeVirtualization: isOperatorEnabled([OPERATOR_NAME_CNV]), - useMultiClusterEngine: isOperatorEnabled([OPERATOR_NAME_MCE]), - useMigrationToolkitforVirtualization: isOperatorEnabled([OPERATOR_NAME_MTV]), - useOpenShiftAI: isOperatorEnabled([OPERATOR_NAME_OPENSHIFT_AI]), - useOsc: isOperatorEnabled([OPERATOR_NAME_OSC]), - useNodeFeatureDiscovery: isOperatorEnabled([OPERATOR_NAME_NODE_FEATURE_DISCOVERY]), - useNmstate: isOperatorEnabled([OPERATOR_NAME_NMSTATE]), - useLso: isOperatorEnabled([OPERATOR_NAME_LSO]), - useServerless: isOperatorEnabled([OPERATOR_NAME_SERVERLESS]), - useAuthorino: isOperatorEnabled([OPERATOR_NAME_AUTHORINO]), - usePipelines: isOperatorEnabled([OPERATOR_NAME_PIPELINES]), - useServicemesh: isOperatorEnabled([OPERATOR_NAME_SERVICEMESH]), - useNvidiaGpu: isOperatorEnabled([OPERATOR_NAME_NVIDIA_GPU]), - useAmdGpu: isOperatorEnabled([OPERATOR_NAME_AMD_GPU]), - useKmm: isOperatorEnabled([OPERATOR_NAME_KMM]), - useNodeHealthcheck: isOperatorEnabled([OPERATOR_NAME_NODE_HEALTHCHECK]), - useSelfNodeRemediation: isOperatorEnabled([OPERATOR_NAME_SELF_NODE_REMEDIATION]), - useFenceAgentsRemediation: isOperatorEnabled([OPERATOR_NAME_FENCE_AGENTS_REMEDIATION]), - useNodeMaintenance: isOperatorEnabled([OPERATOR_NAME_NODE_MAINTENANCE]), - useKubeDescheduler: isOperatorEnabled([OPERATOR_NAME_KUBE_DESCHEDULER]), + selectedBundles: uiSettings?.bundlesSelected || [], + selectedOperators: selectOlmOperators(cluster).map((o) => o.name || ''), }; }; @@ -110,47 +68,29 @@ const OperatorsForm = ({ cluster }: { cluster: Cluster }) => { /> } > - + ); }; const Operators = ({ cluster }: { cluster: Cluster }) => { const dispatch = useDispatch(); + const { updateUISettings, uiSettings } = useClusterWizardContext(); const { addAlert, clearAlerts } = useAlerts(); - const olmOperators = selectMonitoredOperators(cluster.monitoredOperators); - - const initialValues = React.useMemo( - () => getOperatorsInitialValues(olmOperators), - // eslint-disable-next-line react-hooks/exhaustive-deps - [], // just once, Formik does not reinitialize - ); - - const handleSubmit: FormikConfig['onSubmit'] = async ( - values, - { setFieldValue }, - ) => { + const handleSubmit: FormikConfig['onSubmit'] = async (values) => { clearAlerts(); - const enabledOperators = OperatorsService.getOLMOperators(values, cluster); + const enabledOperators = values.selectedOperators.map((so) => ({ + name: so, + })); try { const { data: updatedCluster } = await ClustersService.update(cluster.id, cluster.tags, { olmOperators: enabledOperators, }); - const needSyncOperators = OperatorsService.syncOperators( - enabledOperators, - updatedCluster.monitoredOperators, - ); - Object.keys(needSyncOperators).forEach((operatorName) => { - setFieldValue(operatorName, true); - }); + await updateUISettings({ bundlesSelected: values.selectedBundles }); dispatch(updateCluster(updatedCluster)); } catch (e) { @@ -164,7 +104,7 @@ const Operators = ({ cluster }: { cluster: Cluster }) => { }; return ( - + ); diff --git a/libs/ui-lib/lib/ocm/components/clusterWizard/OperatorsBundle.css b/libs/ui-lib/lib/ocm/components/clusterWizard/OperatorsBundle.css new file mode 100644 index 0000000000..05b08d9674 --- /dev/null +++ b/libs/ui-lib/lib/ocm/components/clusterWizard/OperatorsBundle.css @@ -0,0 +1,6 @@ +.ai-bundle-card { + --pf-v5-c-card--m-selectable--m-selected--BackgroundColor: var( + --pf-v5-global--BackgroundColor--100 + ); + --pf-v5-c-card--m-selectable--hover--BackgroundColor: var(--pf-v5-global--BackgroundColor--100); +} diff --git a/libs/ui-lib/lib/ocm/components/clusterWizard/OperatorsBundle.tsx b/libs/ui-lib/lib/ocm/components/clusterWizard/OperatorsBundle.tsx new file mode 100644 index 0000000000..3b4d34939c --- /dev/null +++ b/libs/ui-lib/lib/ocm/components/clusterWizard/OperatorsBundle.tsx @@ -0,0 +1,215 @@ +import * as React from 'react'; +import { + Card, + CardBody, + CardHeader, + CardTitle, + Gallery, + GalleryItem, + List, + ListItem, + Split, + SplitItem, + Stack, + StackItem, + Title, + Tooltip, +} from '@patternfly/react-core'; +import { + Bundle, + PreflightHardwareRequirements, +} from '@openshift-assisted/types/assisted-installer-service'; +import NewFeatureSupportLevelBadge from '../../../common/components/newFeatureSupportLevels/NewFeatureSupportLevelBadge'; +import { ExternalLink, OperatorsValues, PopoverIcon, singleClusterBundles } from '../../../common'; +import { useFormikContext } from 'formik'; +import { useNewFeatureSupportLevel } from '../../../common/components/newFeatureSupportLevels'; +import { useFeature } from '../../hooks/use-feature'; +import { useSelector } from 'react-redux'; +import { selectIsCurrentClusterSNO } from '../../store/slices/current-cluster/selectors'; +import { getNewBundleOperators } from '../clusterConfiguration/operators/utils'; +import { bundleSpecs } from '../clusterConfiguration/operators/bundleSpecs'; +import { useOperatorSpecs } from '../../../common/components/operators/operatorSpecs'; + +import './OperatorsBundle.css'; + +const BundleLabel = ({ bundle }: { bundle: Bundle }) => { + const opSpecs = useOperatorSpecs(); + const bundleSpec = bundleSpecs[bundle.id || '']; + + return ( + <> + {bundle.title} + Requirements and dependencies} + bodyContent={ + + {bundleSpec?.Description && ( + + + + )} + {bundle.operators?.length && ( + <> + Bundle operators: + + + {bundle.operators.map((op) => ( + {opSpecs[op]?.title || op} + ))} + + + + )} + {bundleSpec?.docsLink && ( + + Learn more + + )} + + } + /> + + ); +}; + +const BundleCard = ({ + bundle, + bundles, + preflightRequirements, +}: { + bundle: Bundle; + bundles: Bundle[]; + preflightRequirements: PreflightHardwareRequirements | undefined; +}) => { + const { values, setFieldValue } = useFormikContext(); + const isSNO = useSelector(selectIsCurrentClusterSNO); + const { isFeatureSupported } = useNewFeatureSupportLevel(); + const opSpecs = useOperatorSpecs(); + + const hasUnsupportedOperators = !!bundle.operators?.some((op) => { + const operatorSpec = opSpecs[op]; + if (!operatorSpec) { + return false; + } + return !isFeatureSupported(operatorSpec.featureId); + }); + + const bundleSpec = bundleSpecs[bundle.id || '']; + + const incompatibleBundle = bundleSpec?.incompatibleBundles?.find((b) => + values.selectedBundles.includes(b), + ); + + const disabledReason = hasUnsupportedOperators + ? 'Some operators in this bundle are not supported with the current configuration.' + : isSNO && bundleSpec?.noSNO + ? 'This bundle is not available when deploying a Single Node OpenShift.' + : incompatibleBundle + ? `Bundle cannot be installed together with ${ + bundles.find(({ id }) => id === incompatibleBundle)?.title || incompatibleBundle + }` + : undefined; + + const onSelect = (checked: boolean) => { + const newBundles = checked + ? [...values.selectedBundles, bundle.id || ''] + : values.selectedBundles.filter((sb) => sb !== bundle.id); + setFieldValue('selectedBundles', newBundles); + const newOperators = getNewBundleOperators( + values.selectedOperators, + newBundles, + bundles, + bundle, + preflightRequirements, + checked, + ); + setFieldValue('selectedOperators', newOperators); + }; + + const isSelected = values.selectedBundles.includes(bundle.id || ''); + const checkboxId = `bundle-${bundle.id || ''}`; + return ( +

{bundle.description}
+ + {bundleSpec.featureId && ( + + + + + + + + + )} + + + + + ); +}; + +const OperatorsBundle = ({ + bundles, + preflightRequirements, +}: { + bundles: Bundle[]; + preflightRequirements: PreflightHardwareRequirements | undefined; +}) => { + const isSingleClusterFeatureEnabled = useFeature('ASSISTED_INSTALLER_SINGLE_CLUSTER_FEATURE'); + + return ( + + + + Bundles + + + + + {(isSingleClusterFeatureEnabled + ? bundles.filter((b) => b.id && singleClusterBundles.includes(b.id)) + : bundles + ).map((bundle) => ( + + + + ))} + + + + ); +}; + +export default OperatorsBundle; diff --git a/libs/ui-lib/lib/ocm/components/clusterWizard/OperatorsSelect.tsx b/libs/ui-lib/lib/ocm/components/clusterWizard/OperatorsSelect.tsx new file mode 100644 index 0000000000..046898ef20 --- /dev/null +++ b/libs/ui-lib/lib/ocm/components/clusterWizard/OperatorsSelect.tsx @@ -0,0 +1,108 @@ +import * as React from 'react'; +import { ExpandableSection, Stack, StackItem } from '@patternfly/react-core'; +import { + Bundle, + Cluster, + PreflightHardwareRequirements, +} from '@openshift-assisted/types/assisted-installer-service'; +import { useFormikContext } from 'formik'; +import { + getApiErrorMessage, + handleApiError, + LoadingState, + OperatorsValues, + singleClusterOperators, + useAlerts, + useStateSafely, +} from '../../../common'; +import { OperatorsService } from '../../services'; +import { useFeature } from '../../hooks/use-feature'; +import OperatorCheckbox from '../clusterConfiguration/operators/OperatorCheckbox'; +import { useOperatorSpecs } from '../../../common/components/operators/operatorSpecs'; + +const OperatorsSelect = ({ + cluster, + bundles, + preflightRequirements, +}: { + cluster: Cluster; + bundles: Bundle[]; + preflightRequirements: PreflightHardwareRequirements | undefined; +}) => { + const [isLoading, setIsLoading] = useStateSafely(true); + const { addAlert } = useAlerts(); + const [isExpanded, setIsExpanded] = React.useState(false); + const [supportedOperators, setSupportedOperators] = useStateSafely([]); + const isSingleClusterFeatureEnabled = useFeature('ASSISTED_INSTALLER_SINGLE_CLUSTER_FEATURE'); + const { values } = useFormikContext(); + + React.useEffect(() => { + const fetchSupportedOperators = async () => { + try { + const fetchedOperators = await OperatorsService.getSupportedOperators(); + setSupportedOperators(fetchedOperators); + } catch (error) { + handleApiError(error, () => + addAlert({ title: 'Failed to fetch operators', message: getApiErrorMessage(error) }), + ); + } finally { + setIsLoading(false); + } + }; + + void fetchSupportedOperators(); + }, [addAlert, setSupportedOperators, setIsLoading]); + + const opSpecs = useOperatorSpecs(); + + const operators = React.useMemo(() => { + return supportedOperators + .sort((a, b) => { + const aTitle = opSpecs[a]?.title || a; + const bTitle = opSpecs[b]?.title || b; + return aTitle.localeCompare(bTitle); + }) + .filter((op) => { + if (!isSingleClusterFeatureEnabled) { + return true; + } + return singleClusterOperators.includes(op); + }); + }, [isSingleClusterFeatureEnabled, supportedOperators, opSpecs]); + + if (isLoading) { + return ; + } + + return ( + setIsExpanded(!isExpanded)} + isExpanded={isExpanded} + data-testid="single-operators-section" + > + + {operators.map((operatorKey) => { + if (!opSpecs[operatorKey]) { + return null; + } + + return ( + + + + ); + })} + + + ); +}; + +export default OperatorsSelect; diff --git a/libs/ui-lib/lib/ocm/components/clusterWizard/OperatorsStep.tsx b/libs/ui-lib/lib/ocm/components/clusterWizard/OperatorsStep.tsx index 3a78c730de..4c94f947c8 100644 --- a/libs/ui-lib/lib/ocm/components/clusterWizard/OperatorsStep.tsx +++ b/libs/ui-lib/lib/ocm/components/clusterWizard/OperatorsStep.tsx @@ -1,470 +1,63 @@ -import React, { useEffect, useState } from 'react'; -import { useSelector } from 'react-redux'; -import { - Card, - CardBody, - CardTitle, - Checkbox, - ExpandableSection, - Flex, - FlexItem, - Gallery, - GalleryItem, - Stack, - StackItem, - TextInput, - Title, - Tooltip, -} from '@patternfly/react-core'; +import React from 'react'; +import { Stack, StackItem } from '@patternfly/react-core'; +import { Bundle } from '@openshift-assisted/types/assisted-installer-service'; import { ClusterOperatorProps, ClusterWizardStepHeader, - numberOfEnabledOperators, - OPERATOR_NAME_CNV, - OPERATOR_NAME_ODF, - OperatorsValues, - PopoverIcon, + getApiErrorMessage, + handleApiError, + LoadingState, + useAlerts, } from '../../../common'; -import { selectIsCurrentClusterSNO } from '../../store/slices/current-cluster/selectors'; -import { isOCPVersionEqualsOrMajor } from '../utils'; +import OperatorsBundle from './OperatorsBundle'; +import OperatorsSelect from './OperatorsSelect'; import BundleService from '../../services/BundleService'; -import { Bundle } from '@openshift-assisted/types/./assisted-installer-service'; -import { OperatorsService } from '../../services'; -import { - mapOperatorIdToFeatureId, - mapOperatorsToFieldIds, - operatorComponentMap, -} from '../clusterConfiguration/operators/SupportedOperators'; -import { useClusterWizardContext } from './ClusterWizardContext'; -import { useFormikContext } from 'formik'; -import NewFeatureSupportLevelBadge from '../../../common/components/newFeatureSupportLevels/NewFeatureSupportLevelBadge'; -import { useNewFeatureSupportLevel } from '../../../common/components/newFeatureSupportLevels'; -import { - getCnvIncompatibleWithLvmReason, - getLvmIncompatibleWithCnvReason, - getLvmsIncompatibleWithOdfReason, - getLvmsIncompatibleWithOpenShiftAIReason, - getOdfIncompatibleWithLvmsReason, - getOpenShiftAIIncompatibleWithLvmsReason, -} from '../featureSupportLevels/featureStateUtils'; -import OpenshiftAINvidiaRequirements from '../clusterConfiguration/operators/OpenshiftAINvidiaRequirements'; -import VirtualizationRequirements from '../clusterConfiguration/operators/VirtualizationRequirements'; -import OpenshiftAIAmdRequirements from '../clusterConfiguration/operators/OpenshiftAIAmdRequirements'; - -const operatorsThatCanNotBeInstalledAlone = [ - 'nvdia-gpu', - 'pipelines', - 'servicemesh', - 'serverless', - 'authorino', - 'lso', - 'amd-gpu', - 'nvidia-gpu', - 'node-healthcheck', - 'self-node-remediation', - 'fence-agents-remediation', - 'node-maintenance', - 'kube-descheduler', -]; - -export const OperatorsStep = (props: ClusterOperatorProps) => { - const isSNO = useSelector(selectIsCurrentClusterSNO); - const isVersionEqualsOrMajorThan4_15 = isOCPVersionEqualsOrMajor( - props.openshiftVersion || '', - '4.15', - ); - - const [bundles, setBundles] = useState([]); - const [isExpanded, setIsExpanded] = useState(false); - // eslint-disable-next-line @typescript-eslint/no-unused-vars - const [supportedOperators, setSupportedOperators] = useState([]); - const [searchTerm, setSearchTerm] = useState(''); - const [selectedOperators, setSelectedOperators] = useState([]); - const [selectedBundles, setSelectedBundles] = useState<{ [key: string]: boolean }>({}); - const [bundleOperators, setBundleOperators] = useState([]); - const { updateUISettings, uiSettings } = useClusterWizardContext(); - const { values, setFieldValue } = useFormikContext(); - const featureSupportLevelData = useNewFeatureSupportLevel(); - - useEffect(() => { +import { useClusterPreflightRequirements } from '../../hooks'; + +export const OperatorsStep = ({ cluster }: ClusterOperatorProps) => { + const { addAlert } = useAlerts(); + const [bundlesLoading, setBundlesLoading] = React.useState(true); + const [bundles, setBundles] = React.useState([]); + const { preflightRequirements, isLoading } = useClusterPreflightRequirements(cluster.id); + React.useEffect(() => { const fetchBundles = async () => { try { const fetchedBundles = await BundleService.listBundles(); - setBundles(fetchedBundles); } catch (error) { - // eslint-disable-next-line no-console - console.error('Error getting bundles:', error); + handleApiError(error, () => + addAlert({ + title: 'Failed to fetch operator bundles', + message: getApiErrorMessage(error), + }), + ); + } finally { + setBundlesLoading(false); } }; void fetchBundles(); - }, []); - - useEffect(() => { - const fetchSupportedOperators = async () => { - try { - const fetchedOperators = await OperatorsService.getSupportedOperators(); - const sortedOperators = fetchedOperators.sort((a, b) => a.localeCompare(b)); + }, [addAlert]); - setSupportedOperators(sortedOperators); - } catch (error) { - // eslint-disable-next-line no-console - console.error('Error getting operators:', error); - } - }; - - void fetchSupportedOperators(); - }, []); - - const filteredBundles = bundles.filter( - (bundle) => - bundle.title?.toLowerCase().includes(searchTerm.toLowerCase()) || - bundle.description?.toLowerCase().includes(searchTerm.toLowerCase()), - ); - - useEffect(() => { - if (uiSettings?.bundlesSelected) { - setSelectedBundles( - uiSettings.bundlesSelected.reduce((acc, id) => ({ ...acc, [id]: true }), {}), - ); - - setBundleOperators(() => { - const newSelection: string[] = []; - bundles - .filter((bundle) => uiSettings?.bundlesSelected?.includes(bundle.id ?? '')) - .forEach((bundle) => { - bundle.operators?.forEach((op) => { - newSelection.push(op); - }); - }); - - return newSelection; - }); - } - }, [bundles, uiSettings?.bundlesSelected]); - - useEffect(() => { - const newSelection: string[] = []; - - props.monitoredOperators?.forEach((operator) => { - if (operator.operatorType === 'olm') { - newSelection.push(operator.name ?? ''); - } - }); - - setSelectedOperators(newSelection); - newSelection.forEach((op) => { - const fieldId = mapOperatorsToFieldIds[op]; // Obtener el ID del campo correspondiente - setFieldValue(fieldId, true); - }); - }, [props.monitoredOperators, setFieldValue]); - - const handleBundleSelection = async (bundleId: string, operators: string[], checked: boolean) => { - let bundlesSelected = uiSettings?.bundlesSelected ? [...uiSettings.bundlesSelected] : []; - let newBundleOperators = [...bundleOperators]; - - if (checked) { - // Agregar el bundle si se marca - bundlesSelected.push(bundleId); - operators.forEach((op) => { - if (!newBundleOperators.includes(op)) { - newBundleOperators.push(op); - const fieldId = mapOperatorsToFieldIds[op]; // Obtener el ID del campo correspondiente - setFieldValue(fieldId, checked); - if (op === OPERATOR_NAME_CNV || op === OPERATOR_NAME_ODF) { - if (featureSupportLevelData.isFeatureSupported('LSO')) setFieldValue('useLso', checked); - } - } - }); - } else { - // Eliminar el bundle si se desmarca - bundlesSelected = bundlesSelected.filter((id) => id !== bundleId); - newBundleOperators = newBundleOperators.filter((op) => !operators.includes(op)); - operators.forEach((op) => { - const fieldId = mapOperatorsToFieldIds[op]; // Obtener el ID del campo correspondiente - setFieldValue(fieldId, checked); - if (op === OPERATOR_NAME_CNV || op === OPERATOR_NAME_ODF) { - if (featureSupportLevelData.isFeatureSupported('LSO')) setFieldValue('useLso', checked); - } - }); - } - - await updateUISettings({ bundlesSelected }); - - setSelectedBundles((prev) => ({ - ...prev, - [bundleId]: checked, - })); - - setBundleOperators(newBundleOperators); - }; - - const getBundleLabel = (bundle: Bundle) => { - let requirements: React.ReactElement | null = null; - switch (bundle.id) { - case 'virtualization': - requirements = ; - break; - case 'openshift-ai-nvidia': - requirements = ; - break; - case 'openshift-ai-amd': - requirements = ; - break; - } - - return ( - <> - {bundle.title} - {requirements && ( - - {'Requirements and dependencies'} - {requirements} - - } - /> - )} - - ); - }; - - const bundleHasOperatorsNotSupported = React.useCallback( - (operators: string[] | undefined) => { - return ( - operators?.some( - (operator) => - !featureSupportLevelData.isFeatureSupported(mapOperatorIdToFeatureId[operator]), - ) ?? false - ); - }, - [featureSupportLevelData], - ); - - const getDisabledReasonForOperator = React.useCallback( - (operatorKey: string, values: OperatorsValues) => { - let disabledReason = featureSupportLevelData.getFeatureDisabledReason( - mapOperatorIdToFeatureId[operatorKey], - ); - if (operatorKey === 'cnv') { - if (!disabledReason) { - const lvmSupport = featureSupportLevelData.getFeatureSupportLevel('LVM'); - disabledReason = getCnvIncompatibleWithLvmReason(values, lvmSupport); - } - } - if (operatorKey === 'lvm') { - if (!disabledReason) { - const lvmSupport = featureSupportLevelData.getFeatureSupportLevel('LVM'); - disabledReason = getLvmIncompatibleWithCnvReason(values, lvmSupport); - } - if (!disabledReason) { - disabledReason = getLvmsIncompatibleWithOdfReason(values); - } - if (!disabledReason) { - disabledReason = getLvmsIncompatibleWithOpenShiftAIReason(values); - } - } - if (operatorKey === 'odf') { - if (!disabledReason) { - disabledReason = getOdfIncompatibleWithLvmsReason(values); - } - } - if (operatorKey === 'openshift-ai') { - if (!disabledReason) { - disabledReason = getOpenShiftAIIncompatibleWithLvmsReason(values); - } - } - return disabledReason; - }, - [featureSupportLevelData], - ); - - const bundleHasOperatorsNotCompatibles = React.useCallback( - (operators: string[] | undefined, values: OperatorsValues, isSelected: boolean) => { - if (!isSelected) { - return ( - operators?.some((operatorKey) => { - const disabledReason = getDisabledReasonForOperator(operatorKey, values); - return disabledReason !== undefined; - }) ?? false - ); - } else return false; - }, - [getDisabledReasonForOperator], - ); + if (isLoading || bundlesLoading) { + return ; + } return ( - - - Operators - - - + Operators - - Bundles - + + + + - {/* Mostrar bundles como tarjetas */} - - {filteredBundles.map((bundle) => { - const isSnoAndBlockedBundle = - isSNO && (bundle.id === 'openshift-ai-nvidia' || bundle.id === 'openshift-ai-amd'); - const hasUnsupportedOperators = bundleHasOperatorsNotSupported(bundle.operators); - const hasIncompatibleOperators = bundleHasOperatorsNotCompatibles( - bundle.operators, - values, - bundle.id ? selectedBundles[bundle.id] : false, - ); - const hasAnotherAIBundleSelected = - (bundle.id === 'openshift-ai-nvidia' && selectedBundles['openshift-ai-amd']) || - (bundle.id === 'openshift-ai-amd' && selectedBundles['openshift-ai-nvidia']); - - const tooltipContent = hasUnsupportedOperators - ? 'Some operators in this bundle are not supported with the current configuration.' - : isSnoAndBlockedBundle - ? 'This bundle is not available when deploying a Single Node OpenShift.' - : hasIncompatibleOperators - ? 'Some operators in this bundle can not be installed with some single operators selected.' - : hasAnotherAIBundleSelected - ? 'This bundle cannot be selected because you already have another OpenShift AI bundle selected.' - : ''; - return ( - - - - ); - })} - - setIsExpanded(!isExpanded)} - isExpanded={isExpanded} - data-testid="single-operators-section" - > - - {supportedOperators.map((operatorKey) => { - const isOperatorSelected = bundleOperators.includes(operatorKey); - const isOperatorNotAllowedAlone = - operatorsThatCanNotBeInstalledAlone.includes(operatorKey); - const disabledReason = getDisabledReasonForOperator(operatorKey, values); - const featureSupportLevel = featureSupportLevelData.getFeatureSupportLevel( - mapOperatorIdToFeatureId[operatorKey], - ); - const OperatorComponent = operatorComponentMap[operatorKey]; - if (!OperatorComponent) { - return null; - } - return ( - - - - ); - })} - - ); }; diff --git a/libs/ui-lib/lib/ocm/components/clusterWizard/wizardTransition.ts b/libs/ui-lib/lib/ocm/components/clusterWizard/wizardTransition.ts index 050cd58577..721e2ef2ab 100644 --- a/libs/ui-lib/lib/ocm/components/clusterWizard/wizardTransition.ts +++ b/libs/ui-lib/lib/ocm/components/clusterWizard/wizardTransition.ts @@ -73,14 +73,10 @@ export const getClusterWizardFirstStep = ( staticIpInfo: StaticIpInfo | undefined, state?: ClusterWizardFlowStateType, hosts?: Host[] | undefined, - isSingleClusterFeatureEnabled?: boolean, customManifestsStepNeedsToBeFilled?: boolean, ): ClusterWizardStepsType => { // Just for the first time when the cluster is created if (locationState === ClusterWizardFlowStateNew && !staticIpInfo) { - if (isSingleClusterFeatureEnabled) { - return 'host-discovery'; - } return 'operators'; } diff --git a/libs/ui-lib/lib/ocm/components/clusters/utils.ts b/libs/ui-lib/lib/ocm/components/clusters/utils.ts index c024141059..ab5f85ab12 100644 --- a/libs/ui-lib/lib/ocm/components/clusters/utils.ts +++ b/libs/ui-lib/lib/ocm/components/clusters/utils.ts @@ -3,9 +3,7 @@ import { LogsState, MonitoredOperator, MonitoredOperatorsList, - OperatorCreateParams, } from '@openshift-assisted/types/assisted-installer-service'; -import { selectOlmOperators } from '../../../common'; // The Day2 cluster export const isAddHostsCluster = (cluster: Cluster) => cluster.kind === 'AddHostsCluster'; @@ -24,23 +22,6 @@ export const calculateCollectedLogsCount = (cluster: Cluster) => { export const getBuiltInOperators = (monitoredOperators: MonitoredOperatorsList = []) => monitoredOperators.filter((operator: MonitoredOperator) => operator.operatorType === 'builtin'); -export const getOlmOperatorCreateParams = (cluster?: Cluster): OperatorCreateParams[] => - selectOlmOperators(cluster).map((operator) => ({ - name: operator.name, - properties: operator.properties, - })); - -export const getOlmOperatorCreateParamsByName = (cluster?: Cluster) => - getOlmOperatorCreateParams(cluster).reduce( - (result: { [key: string]: OperatorCreateParams }, operator) => { - if (operator.name) { - result[operator.name] = operator; - } - return result; - }, - {}, - ); - export const canAbortInstallation = (cluster: Cluster) => { const allowedClusterStates: Cluster['status'][] = [ 'preparing-for-installation', diff --git a/libs/ui-lib/lib/ocm/components/featureSupportLevels/FeatureSupportLevelProvider.tsx b/libs/ui-lib/lib/ocm/components/featureSupportLevels/FeatureSupportLevelProvider.tsx index 0bb5fba276..332dccc04c 100644 --- a/libs/ui-lib/lib/ocm/components/featureSupportLevels/FeatureSupportLevelProvider.tsx +++ b/libs/ui-lib/lib/ocm/components/featureSupportLevels/FeatureSupportLevelProvider.tsx @@ -90,7 +90,6 @@ export const NewFeatureSupportLevelProvider: React.FC { - // eslint-disable-next-line react-hooks/exhaustive-deps if (supportLevelDataNew) { // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore diff --git a/libs/ui-lib/lib/ocm/components/featureSupportLevels/featureStateUtils.ts b/libs/ui-lib/lib/ocm/components/featureSupportLevels/featureStateUtils.ts index 9582bfecdd..411265f74b 100644 --- a/libs/ui-lib/lib/ocm/components/featureSupportLevels/featureStateUtils.ts +++ b/libs/ui-lib/lib/ocm/components/featureSupportLevels/featureStateUtils.ts @@ -4,7 +4,11 @@ import { CpuArchitecture, FeatureId, isSNO, - OperatorsValues, + OPERATOR_NAME_CNV, + OPERATOR_NAME_LVM, + OPERATOR_NAME_ODF, + OPERATOR_NAME_OPENSHIFT_AI, + OPERATOR_NAME_OSC, SupportedCpuArchitecture, } from '../../../common'; import { @@ -13,43 +17,10 @@ import { SupportLevel, } from '@openshift-assisted/types/assisted-installer-service'; import { ExternalPlatformLabels } from '../clusterConfiguration/platformIntegration/constants'; - -const CNV_OPERATOR_LABEL = 'OpenShift Virtualization'; -const LVMS_OPERATOR_LABEL = 'Logical Volume Manager Storage'; -const LVM_OPERATOR_LABEL = 'Logical Volume Manager'; -const ODF_OPERATOR_LABEL = 'OpenShift Data Foundation'; -const OPENSHIFT_AI_OPERATOR_LABEL = 'OpenShift AI'; -const MTV_OPERATOR_LABEL = 'Migration Toolkit for Virtualization'; -const OSC_OPERATOR_LABEL = 'OpenShift sandboxed containers'; +import { getOperatorSpecs } from '../../../common/components/operators/operatorSpecs'; export const clusterExistsReason = 'This option is not editable after the draft cluster is created'; -export const getCnvIncompatibleWithLvmReason = ( - operatorValues: OperatorsValues, - lvmSupport: SupportLevel | undefined, -) => { - const mustDisableCnv = - !operatorValues.useContainerNativeVirtualization && - operatorValues.useOdfLogicalVolumeManager && - lvmSupport !== 'supported'; - // In versions with none or limited support for LVM (< 4.12), it's not possible to select CNV + LVM - return mustDisableCnv - ? `Currently, you cannot install ${CNV_OPERATOR_LABEL} operator at the same time as ${LVM_OPERATOR_LABEL} operator.` - : undefined; -}; - -export const getLvmIncompatibleWithCnvReason = ( - operatorValues: OperatorsValues, - lvmSupport: SupportLevel | undefined, -) => { - const hasSelectedCnv = operatorValues.useContainerNativeVirtualization; - // In versions with none or limited support for LVM (< 4.12), it's not possible to select CNV + LVM - if (hasSelectedCnv && lvmSupport !== 'supported') { - return `Currently, you cannot install ${LVM_OPERATOR_LABEL} operator at the same time as ${CNV_OPERATOR_LABEL} operator.`; - } - return undefined; -}; - const getSNODisabledReason = (cluster: Cluster | undefined, isSupported: boolean) => { if (cluster) { return clusterExistsReason; @@ -75,18 +46,22 @@ const getOdfDisabledReason = ( return undefined; } + const opSpecs = getOperatorSpecs(); + + const operatorTitle = opSpecs[OPERATOR_NAME_ODF]?.title || ''; + const isArm = activeFeatureConfiguration?.underlyingCpuArchitecture === CpuArchitecture.ARM; if (isArm && isSNO(cluster)) { - return `${ODF_OPERATOR_LABEL} is not available when using Single Node OpenShift or ARM CPU architecture.`; + return `${operatorTitle} is not available when using Single Node OpenShift or ARM CPU architecture.`; } if (isArm) { - return `${ODF_OPERATOR_LABEL} is not available when ARM CPU architecture is selected.`; + return `${operatorTitle} is not available when ARM CPU architecture is selected.`; } if (isSNO(cluster)) { - return `${ODF_OPERATOR_LABEL} is not available when deploying a Single Node OpenShift.`; + return `${operatorTitle} is not available when deploying a Single Node OpenShift.`; } if (!isSupported) { - return `The installer cannot currently enable ${ODF_OPERATOR_LABEL} with the selected OpenShift version, but it can be enabled later through the OpenShift Console once the installation is complete.`; + return `The installer cannot currently enable ${operatorTitle} with the selected OpenShift version, but it can be enabled later through the OpenShift Console once the installation is complete.`; } return undefined; }; @@ -99,8 +74,11 @@ const getCnvDisabledReason = ( if (!activeFeatureConfiguration) { return undefined; } + + const opSpecs = getOperatorSpecs(); + const operatorTitle = opSpecs[OPERATOR_NAME_CNV]?.title || ''; if (platformType === 'nutanix') { - return `${CNV_OPERATOR_LABEL} is not available when Nutanix platform type is selected.`; + return `${operatorTitle} is not available when Nutanix platform type is selected.`; } if (!isSupported) { const cpuArchitectureLabel = @@ -108,7 +86,7 @@ const getCnvDisabledReason = ( activeFeatureConfiguration.underlyingCpuArchitecture as SupportedCpuArchitecture ].label; - return `${CNV_OPERATOR_LABEL} is not available when ${ + return `${operatorTitle} is not available when ${ cpuArchitectureLabel ? cpuArchitectureLabel : activeFeatureConfiguration.underlyingCpuArchitecture @@ -126,12 +104,14 @@ const getLvmDisabledReason = ( if (!activeFeatureConfiguration) { return undefined; } - const operatorLabel = isSupported ? LVMS_OPERATOR_LABEL : LVM_OPERATOR_LABEL; + + const opSpecs = getOperatorSpecs(); + const operatorTitle = opSpecs[OPERATOR_NAME_LVM]?.title; if (platformType === 'nutanix') { - return `${operatorLabel} is not supported when Nutanix platform type is selected.`; + return `${operatorTitle} is not supported when Nutanix platform type is selected.`; } if (!isSupported) { - return `${operatorLabel} is not supported in this OpenShift version.`; + return `${operatorTitle} is not supported in this OpenShift version.`; } return undefined; }; @@ -144,8 +124,11 @@ const getOscDisabledReason = ( if (!cluster) { return undefined; } + + const opSpecs = getOperatorSpecs(); + const operatorTitle = opSpecs[OPERATOR_NAME_OSC]?.title || ''; if (!isSupported) { - return `${OSC_OPERATOR_LABEL} is not supported in this OpenShift version.`; + return `${operatorTitle} is not supported in this OpenShift version.`; } return undefined; }; @@ -307,37 +290,6 @@ export const isFeatureSupportedAndAvailable = (supportLevel: SupportLevel | unde export const hostsNetworkConfigurationDisabledReason = "DHCP only is the supported hosts' network configuration when external partner integrations is selected"; -export const getOdfIncompatibleWithLvmsReason = (operatorValues: OperatorsValues) => { - const mustDisableOdf = operatorValues.useOdfLogicalVolumeManager; - // In versions >= 4.15, it's not possible to select ODF + LVMS - return mustDisableOdf - ? `Currently, you cannot install ${ODF_OPERATOR_LABEL} operator at the same time as ${LVMS_OPERATOR_LABEL} operator.` - : undefined; -}; - -export const getOpenShiftAIIncompatibleWithLvmsReason = (operatorValues: OperatorsValues) => { - // Currently OpenShift AI requires ODF, and that is incompatible with LVM. - const mustDisableOpenShiftAI = operatorValues.useOdfLogicalVolumeManager; - return mustDisableOpenShiftAI - ? `Currently the ${OPENSHIFT_AI_OPERATOR_LABEL} requires ${ODF_OPERATOR_LABEL}, and you cannot install that at the same time as ${LVMS_OPERATOR_LABEL} operator.` - : undefined; -}; - -export const getLvmsIncompatibleWithOdfReason = (operatorValues: OperatorsValues) => { - const mustDisableLvms = operatorValues.useOpenShiftDataFoundation; - // In versions >= 4.15, it's not possible to select ODF + LVMS - return mustDisableLvms - ? `Currently, you cannot install ${LVMS_OPERATOR_LABEL} operator at the same time as ${ODF_OPERATOR_LABEL} operator.` - : undefined; -}; - -export const getLvmsIncompatibleWithOpenShiftAIReason = (operatorValues: OperatorsValues) => { - const mustDisableLvms = operatorValues.useOpenShiftAI; - return mustDisableLvms - ? `Currently, you cannot install ${LVMS_OPERATOR_LABEL} operator at the same time as ${OPENSHIFT_AI_OPERATOR_LABEL} operator.` - : undefined; -}; - const getOpenShiftAIDisabledReason = ( cluster: Cluster | undefined, activeFeatureConfiguration: ActiveFeatureConfiguration | undefined, @@ -347,24 +299,17 @@ const getOpenShiftAIDisabledReason = ( return undefined; } + const opSpecs = getOperatorSpecs(); + const operatorTitle = opSpecs[OPERATOR_NAME_OPENSHIFT_AI]?.title || ''; const isArm = activeFeatureConfiguration?.underlyingCpuArchitecture === CpuArchitecture.ARM; if (isArm) { - return `${OPENSHIFT_AI_OPERATOR_LABEL} is not available when ARM CPU architecture is selected.`; + return `${operatorTitle} is not available when ARM CPU architecture is selected.`; } if (isSNO(cluster)) { - return `${OPENSHIFT_AI_OPERATOR_LABEL} is not available when deploying a Single Node OpenShift.`; + return `${operatorTitle} is not available when deploying a Single Node OpenShift.`; } if (!isSupported) { - return `The installer cannot currently enable ${OPENSHIFT_AI_OPERATOR_LABEL} with the selected OpenShift version, but it can be enabled later through the OpenShift Console once the installation is complete.`; + return `The installer cannot currently enable ${operatorTitle} with the selected OpenShift version, but it can be enabled later through the OpenShift Console once the installation is complete.`; } return undefined; }; - -export const getCnvDisabledWithMtvReason = (operatorValues: OperatorsValues) => { - const mustDisableCnv = - operatorValues.useContainerNativeVirtualization && - !operatorValues.useMigrationToolkitforVirtualization; - return mustDisableCnv - ? `Currently, you need to install ${CNV_OPERATOR_LABEL} operator at the same time as ${MTV_OPERATOR_LABEL} operator.` - : undefined; -}; diff --git a/libs/ui-lib/lib/ocm/components/utils.ts b/libs/ui-lib/lib/ocm/components/utils.ts index 764764e575..ef4b670068 100644 --- a/libs/ui-lib/lib/ocm/components/utils.ts +++ b/libs/ui-lib/lib/ocm/components/utils.ts @@ -5,15 +5,3 @@ export const isOciPlatformType = (cluster: Cluster): boolean => { cluster.platform?.type === 'external' && cluster.platform?.external?.platformName === 'oci' ); }; - -export const getMajorMinorVersion = (version = '') => { - const match = /[0-9].[0-9][0-9]?/g.exec(version); - return match?.[0] || ''; -}; - -export const isOCPVersionEqualsOrMajor = ( - openshiftVersion: string, - ocpVersionToCompare: string, -): boolean => { - return parseFloat(getMajorMinorVersion(openshiftVersion)) >= parseFloat(ocpVersionToCompare); -}; diff --git a/libs/ui-lib/lib/ocm/services/OperatorsService.tsx b/libs/ui-lib/lib/ocm/services/OperatorsService.tsx index d65c013b34..e3b0c758fb 100644 --- a/libs/ui-lib/lib/ocm/services/OperatorsService.tsx +++ b/libs/ui-lib/lib/ocm/services/OperatorsService.tsx @@ -1,106 +1,7 @@ -import { - OperatorsValues, - OPERATOR_NAME_CNV, - OPERATOR_NAME_ODF, - OPERATOR_NAME_LSO, - OPERATOR_NAME_LVM, - OperatorName, - OPERATOR_NAME_MCE, - OPERATOR_NAME_MTV, - OPERATOR_NAME_OPENSHIFT_AI, - OPERATOR_NAME_OSC, - OPERATOR_NAME_NODE_FEATURE_DISCOVERY, - OPERATOR_NAME_NMSTATE, - OPERATOR_NAME_SERVERLESS, - OPERATOR_NAME_AUTHORINO, - OPERATOR_NAME_PIPELINES, - OPERATOR_NAME_SERVICEMESH, - OPERATOR_NAME_NVIDIA_GPU, - OPERATOR_NAME_AMD_GPU, - OPERATOR_NAME_KMM, - OPERATOR_NAME_NODE_HEALTHCHECK, - OPERATOR_NAME_SELF_NODE_REMEDIATION, - OPERATOR_NAME_FENCE_AGENTS_REMEDIATION, - OPERATOR_NAME_NODE_MAINTENANCE, - OPERATOR_NAME_KUBE_DESCHEDULER, -} from '../../common'; -import { getOlmOperatorCreateParamsByName } from '../components/clusters/utils'; -import { - Cluster, - OperatorCreateParams, -} from '@openshift-assisted/types/assisted-installer-service'; import OperatorsAPI from '../../common/api/assisted-service/OperatorsAPI'; const OperatorsService = { - getOLMOperators(values: OperatorsValues, cluster: Cluster): OperatorCreateParams[] { - const enabledOlmOperatorsByName = getOlmOperatorCreateParamsByName(cluster); - - // TODO: change OperatorName to ExposedOperatorName once the LSO option is exposed to the user - const setOperator = (name: OperatorName, enabled: boolean) => { - if (enabled) { - enabledOlmOperatorsByName[name] = { name }; - } else { - delete enabledOlmOperatorsByName[name]; - } - }; - - setOperator(OPERATOR_NAME_LVM, values.useOdfLogicalVolumeManager); - setOperator(OPERATOR_NAME_CNV, values.useContainerNativeVirtualization); - setOperator(OPERATOR_NAME_ODF, values.useOpenShiftDataFoundation); - setOperator(OPERATOR_NAME_MCE, values.useMultiClusterEngine); - setOperator(OPERATOR_NAME_MTV, values.useMigrationToolkitforVirtualization); - setOperator(OPERATOR_NAME_OPENSHIFT_AI, values.useOpenShiftAI); - setOperator(OPERATOR_NAME_OSC, values.useOsc); - setOperator(OPERATOR_NAME_NODE_FEATURE_DISCOVERY, values.useNodeFeatureDiscovery); - setOperator(OPERATOR_NAME_NMSTATE, values.useNmstate); - setOperator(OPERATOR_NAME_LSO, values.useLso); - setOperator(OPERATOR_NAME_SERVERLESS, values.useServerless); - setOperator(OPERATOR_NAME_AUTHORINO, values.useAuthorino); - setOperator(OPERATOR_NAME_PIPELINES, values.usePipelines); - setOperator(OPERATOR_NAME_SERVICEMESH, values.useServicemesh); - setOperator(OPERATOR_NAME_NVIDIA_GPU, values.useNvidiaGpu); - setOperator(OPERATOR_NAME_AMD_GPU, values.useAmdGpu); - setOperator(OPERATOR_NAME_KMM, values.useKmm); - setOperator(OPERATOR_NAME_NODE_HEALTHCHECK, values.useNodeHealthcheck); - setOperator(OPERATOR_NAME_SELF_NODE_REMEDIATION, values.useNodeMaintenance); - setOperator(OPERATOR_NAME_FENCE_AGENTS_REMEDIATION, values.useFenceAgentsRemediation); - setOperator(OPERATOR_NAME_NODE_MAINTENANCE, values.useNodeMaintenance); - setOperator(OPERATOR_NAME_KUBE_DESCHEDULER, values.useKubeDescheduler); - - return Object.values(enabledOlmOperatorsByName); - }, - - /** - * Depending on the OpenShift version and certain selected operators, the - * Backend can activate some other operators. - * We need to synchronise them back to the form - */ - syncOperators( - uiOperators: OperatorCreateParams[], - updatedOperators: Cluster['monitoredOperators'], - ): Partial { - const updates: Partial = {}; - - // LVM operator can be automatically selected depending on OpenShift version + other operators - const lvmPrevInactive = uiOperators?.find((op) => op.name === OPERATOR_NAME_LVM) === undefined; - const lvmNowActive = - updatedOperators?.find((op) => op.name === OPERATOR_NAME_LVM) !== undefined; - if (lvmPrevInactive && lvmNowActive) { - updates.useOdfLogicalVolumeManager = true; - } - - // ODF operator will be automatically selected when the OpenShift AI operator is selected: - const odfPrevInactive = uiOperators?.find((op) => op.name === OPERATOR_NAME_ODF) === undefined; - const odfNowActive = - updatedOperators?.find((op) => op.name === OPERATOR_NAME_ODF) !== undefined; - if (odfPrevInactive && odfNowActive) { - updates.useOpenShiftDataFoundation = true; - } - - return updates; - }, - - async getSupportedOperators(): Promise { + getSupportedOperators: async (): Promise => { const { data: operators } = await OperatorsAPI.list(); return operators; },