Skip to content

Commit

Permalink
MGMT-19058: Add OpenShif API operator
Browse files Browse the repository at this point in the history
This patch adds to the operators page of the cluster creation wizard a
new check-box that will be used to enable the installation of the
OpenShift API operator.

Related: https://issues.redhat.com/browse/MGMT-19058
Related: https://issues.redhat.com/browse/MGMT-19057
Signed-off-by: Juan Hernandez <[email protected]>
  • Loading branch information
jhernand committed Nov 14, 2024
1 parent 4c249ab commit 4890a8b
Show file tree
Hide file tree
Showing 11 changed files with 182 additions and 5 deletions.
6 changes: 6 additions & 0 deletions libs/locales/lib/en/translation.json
Original file line number Diff line number Diff line change
Expand Up @@ -569,6 +569,7 @@
"ai:No skip missing disk": "No skip missing disk",
"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:Node Feature Discovery": "Node Feature Discovery",
"ai:Nodepool": "Nodepool",
"ai:Nodepool conditions": "Nodepool conditions",
"ai:Nodepool name": "Nodepool name",
Expand All @@ -587,6 +588,7 @@
"ai:NTP synchronization failure": "NTP synchronization failure",
"ai:Number of characters between dots (.) must be 1-63": "Number of characters between dots (.) must be 1-63",
"ai:Number of hosts": "Number of hosts",
"ai:NVIDIA GPU": "NVIDIA GPU",
"ai:OCS requirements": "OCS requirements",
"ai:ODF requirements": "ODF requirements",
"ai:OLM catalog components will be deployed onto the guest cluster.": "OLM catalog components will be deployed onto the guest cluster.",
Expand All @@ -600,6 +602,7 @@
"ai:Open Virtual Networking (OVN)": "Open Virtual Networking (OVN)",
"ai:Opening file": "Opening file",
"ai:OpenShift": "OpenShift",
"ai:OpenShift AI": "OpenShift AI",
"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.",
Expand All @@ -621,6 +624,7 @@
"ai:Pending input": "Pending input",
"ai:Pending user action": "Pending user action",
"ai:Pending validations:": "Pending validations:",
"ai:Pipelines": "Pipelines",
"ai:Platform network settings": "Platform network settings",
"ai:Platform requirements": "Platform requirements",
"ai:Please note that this version is not maintained anymore.": "Please note that this version is not maintained anymore.",
Expand Down Expand Up @@ -702,7 +706,9 @@
"ai:Selected image does not support arm64": "Selected image does not support arm64",
"ai:Serial": "Serial",
"ai:Serial number": "Serial number",
"ai:Serverless": "Serverless",
"ai:Service CIDR": "Service CIDR",
"ai:Service Mesh": "Service Mesh",
"ai:Service network CIDR": "Service network CIDR",
"ai:Service networks": "Service networks",
"ai:Set <bold>{{agent_location_label_key}}</bold> label in Agent resource to specify its location.": "Set <bold>{{agent_location_label_key}}</bold> label in Agent resource to specify its location.",
Expand Down
24 changes: 24 additions & 0 deletions libs/ui-lib/lib/common/config/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -270,6 +270,12 @@ 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';
export const OPERATOR_NAME_NVIDIA_GPU = 'nvidia-gpu';
export const OPERATOR_NAME_PIPELINES = 'pipelines';
export const OPERATOR_NAME_SERVICEMESH = 'servicemesh';
export const OPERATOR_NAME_SERVERLESS = 'serverless';
export const OPERATOR_NAME_OPENSHIFT_AI = 'openshift-ai';

const OperatorNames = [
OPERATOR_NAME_CNV,
Expand All @@ -279,6 +285,12 @@ const OperatorNames = [
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,
];
export const ExposedOperatorNames = [
OPERATOR_NAME_CNV,
Expand All @@ -287,6 +299,12 @@ export const ExposedOperatorNames = [
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,
];

export type OperatorName = (typeof OperatorNames)[number];
Expand Down Expand Up @@ -322,6 +340,12 @@ export const operatorLabels = (
? 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'),
};
};

Expand Down
6 changes: 6 additions & 0 deletions libs/ui-lib/lib/common/config/docs_links.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,9 @@ export const HOST_REQUIREMENTS_LINK = 'https://access.redhat.com/solutions/48856
export const ODF_REQUIREMENTS_LINK =
'https://docs.redhat.com/en/documentation/red_hat_openshift_data_foundation';

export const OPENSHIFT_AI_REQUIREMENTS_LINK =
'https://docs.redhat.com/en/documentation/red_hat_openshift_ai_cloud_service/1/html/installing_the_openshift_ai_cloud_service/requirements-for-openshift-ai_install#requirements-for-openshift-ai_install';

export const CNV_LINK = 'https://cloud.redhat.com/learn/topics/virtualization/';

export const ODF_LINK = 'https://www.redhat.com/en/resources/openshift-data-foundation-datasheet';
Expand Down Expand Up @@ -98,3 +101,6 @@ export const getCiscoIntersightLink = (downloadIsoUrl: string) =>

export const MTV_LINK =
'https://docs.redhat.com/en/documentation/migration_toolkit_for_virtualization/2.7/';

export const OPENSHIFT_AI_LINK =
'https://www.redhat.com/en/technologies/cloud-computing/openshift/openshift-ai';
1 change: 1 addition & 0 deletions libs/ui-lib/lib/common/types/clusters.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ export type OperatorsValues = V2ClusterUpdateParams & {
useContainerNativeVirtualization: boolean;
useMultiClusterEngine: boolean;
useMigrationToolkitforVirtualization: boolean;
useOpenShiftAI: boolean;
};

export type SupportedPlatformType = Extract<PlatformType, 'vsphere' | 'nutanix' | 'external'>;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
import React, { useState } 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 { useFormikContext } from 'formik';
import {
getFieldId,
PopoverIcon,
OPENSHIFT_AI_REQUIREMENTS_LINK,
OPENSHIFT_AI_LINK,
OperatorsValues,
} 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';

const OPENSHIFT_AI_FIELD_NAME = 'useOpenShiftAI';

type OpenShiftAILabelProps = {
disabledReason?: string;
supportLevel?: SupportLevel;
};

const OpenShiftAILabel = ({
disabledReason,
supportLevel,
}: OpenShiftAILabelProps) => {
return (
<>
<Tooltip hidden={!disabledReason} content={disabledReason}>
<span>Install OpenShift AI </span>
</Tooltip>
<PopoverIcon
component={'a'}
headerContent="Additional requirements"
bodyContent={
<a href={OPENSHIFT_AI_REQUIREMENTS_LINK} target="_blank" rel="noopener noreferrer">
Learn more about the requirements for OpenShift AI <ExternalLinkAltIcon />.
</a>
}
/>
<NewFeatureSupportLevelBadge featureId="OPENSHIFT_AI" supportLevel={supportLevel} />
</>
);
};

const OpenShiftAIHelperText = () => {
return (
<HelperText>
<HelperTextItem variant="indeterminate">
Train, serve, monitor and manage AI/ML models and applications.{' '}
<a href={OPENSHIFT_AI_LINK} target="_blank" rel="noopener noreferrer">
{'Learn more'} <ExternalLinkAltIcon />
</a>
</HelperTextItem>
</HelperText>
);
};

const OpenShiftAICheckbox = () => {
const featureSupportLevel = useNewFeatureSupportLevel();
const { values } = useFormikContext<OperatorsValues>();
const fieldId = getFieldId(OPENSHIFT_AI_FIELD_NAME, 'input');
const [disabledReason, setDisabledReason] = useState<string | undefined>();

React.useEffect(() => {
const disabledReason = featureSupportLevel.getFeatureDisabledReason('OPENSHIFT_AI');
setDisabledReason(disabledReason);
}, [values, featureSupportLevel]);

return (
<FormGroup isInline fieldId={fieldId}>
<OcmCheckboxField
name={OPENSHIFT_AI_FIELD_NAME}
label={
<OpenShiftAILabel
disabledReason={disabledReason}
supportLevel={featureSupportLevel.getFeatureSupportLevel('OPENSHIFT_AI')}
/>
}
isDisabled={!!disabledReason}
helperText={<OpenShiftAIHelperText />}
/>
</FormGroup>
);
};

export default OpenShiftAICheckbox;
2 changes: 2 additions & 0 deletions libs/ui-lib/lib/ocm/components/clusterWizard/Operators.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import {
OPERATOR_NAME_MCE,
OPERATOR_NAME_MTV,
OPERATOR_NAME_ODF,
OPERATOR_NAME_OPENSHIFT_AI,
OperatorsValues,
selectMonitoredOperators,
useAlerts,
Expand All @@ -36,6 +37,7 @@ export const getOperatorsInitialValues = (
useContainerNativeVirtualization: isOperatorEnabled([OPERATOR_NAME_CNV]),
useMultiClusterEngine: isOperatorEnabled([OPERATOR_NAME_MCE]),
useMigrationToolkitforVirtualization: isOperatorEnabled([OPERATOR_NAME_MTV]),
useOpenShiftAI: isOperatorEnabled([OPERATOR_NAME_OPENSHIFT_AI])
};
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import MceCheckbox from '../clusterConfiguration/operators/MceCheckbox';
import { selectIsCurrentClusterSNO } from '../../store/slices/current-cluster/selectors';
import { isOCPVersionEqualsOrMajor } from '../utils';
import MtvOperatorCheckbox from '../clusterConfiguration/operators/MtvOperatorCheckbox';
import OpenShiftAICheckbox from '../clusterConfiguration/operators/OpenShiftAICheckbox';

export const OperatorsStep = (props: ClusterOperatorProps) => {
const isSNO = useSelector(selectIsCurrentClusterSNO);
Expand Down Expand Up @@ -44,6 +45,7 @@ export const OperatorsStep = (props: ClusterOperatorProps) => {
<StackItem>{isSNO ? <LvmCheckbox {...props} /> : <OdfCheckbox />}</StackItem>
)}
<MtvOperatorCheckbox clusterId={props.clusterId} />
<OpenShiftAICheckbox />
</Stack>
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,11 @@ const validationInfoHostsDisovery: ValidationsInfo = {
message: 'a validation message',
status: 'success',
},
{
id: 'openshift-ai-requirements-satisfied',
message: 'a validation message',
status: 'success',
},
{
id: 'api-vips-defined',
message: 'a validation unrelated to the canNextHostDiscovery',
Expand Down Expand Up @@ -170,6 +175,11 @@ const flatValidationHostsDiscovery: HostValidation[] = [
message: 'a host validation message',
status: 'success',
},
{
id: 'openshift-ai-requirements-satisfied',
message: 'a host validation message',
status: 'success',
},
];

const flatValidationHostsStorage: HostValidation[] = [
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,7 @@ const hostDiscoveryStepValidationsMap: WizardStepValidationMap = {
'odf-requirements-satisfied',
'lvm-requirements-satisfied',
'cnv-requirements-satisfied',
'openshift-ai-requirements-satisfied',
],
},
host: {
Expand All @@ -183,6 +184,7 @@ const hostDiscoveryStepValidationsMap: WizardStepValidationMap = {
'odf-requirements-satisfied',
'lvm-requirements-satisfied',
'cnv-requirements-satisfied',
'openshift-ai-requirements-satisfied',
],
},
softValidationIds: ['no-skip-installation-disk', 'no-skip-missing-disk'],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ 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';

export const clusterExistsReason = 'This option is not editable after the draft cluster is created';

Expand Down Expand Up @@ -185,6 +186,9 @@ export const getNewFeatureDisabledReason = (
case 'ODF': {
return getOdfDisabledReason(cluster, activeFeatureConfiguration, isSupported);
}
case 'OPENSHIFT_AI': {
return getOpenShiftAIDisabledReason(cluster, activeFeatureConfiguration, isSupported);
}
case 'NETWORK_TYPE_SELECTION': {
return getNetworkTypeSelectionDisabledReason(cluster);
}
Expand Down Expand Up @@ -254,3 +258,25 @@ export const getLvmsIncompatibleWithOdfReason = (operatorValues: OperatorsValues
? `Currently, you cannot install ${LVMS_OPERATOR_LABEL} operator at the same time as ${ODF_OPERATOR_LABEL} operator.`
: undefined;
};

const getOpenShiftAIDisabledReason = (
cluster: Cluster | undefined,
activeFeatureConfiguration: ActiveFeatureConfiguration | undefined,
isSupported: boolean,
) => {
if (!cluster) {
return undefined;
}

const isArm = activeFeatureConfiguration?.underlyingCpuArchitecture === CpuArchitecture.ARM;
if (isArm) {
return `${OPENSHIFT_AI_OPERATOR_LABEL} 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.`;
}
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 undefined;
}
20 changes: 15 additions & 5 deletions libs/ui-lib/lib/ocm/services/OperatorsService.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {
OperatorName,
OPERATOR_NAME_MCE,
OPERATOR_NAME_MTV,
OPERATOR_NAME_OPENSHIFT_AI,
} from '../../common';
import { getOlmOperatorCreateParamsByName } from '../components/clusters/utils';
import { getKeys } from '../../common/utils';
Expand Down Expand Up @@ -37,6 +38,7 @@ const OperatorsService = {
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);

// TODO: remove following once the LSO option is exposed to the user
if (!hasActiveOperators(values)) {
Expand All @@ -55,14 +57,22 @@ const OperatorsService = {
uiOperators: OperatorCreateParams[],
updatedOperators: Cluster['monitoredOperators'],
): Partial<OperatorsValues> {
// LVM operator can be automatically selected depending on Openshift version + other operators
const prevInactive = uiOperators?.find((op) => op.name === OPERATOR_NAME_LVM) === undefined;
const nowActive = updatedOperators?.find((op) => op.name === OPERATOR_NAME_LVM) !== undefined;

const updates: Partial<OperatorsValues> = {};
if (prevInactive && nowActive) {

// 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;
},
};
Expand Down

0 comments on commit 4890a8b

Please sign in to comment.