diff --git a/libs/locales/lib/en/translation.json b/libs/locales/lib/en/translation.json index 0eafbc6141..ecc2fa5cd9 100644 --- a/libs/locales/lib/en/translation.json +++ b/libs/locales/lib/en/translation.json @@ -970,6 +970,7 @@ "ai:Unable to SSH into your hosts through the network?": "Unable to SSH into your hosts through the network?", "ai:Unique hostname": "Unique hostname", "ai:Unknown": "Unknown", + "ai:Unmonitored": "Unmonitored", "ai:Unreachable": "Unreachable", "ai:Update": "Update", "ai:Upload": "Upload", diff --git a/libs/types/assisted-installer-service.d.ts b/libs/types/assisted-installer-service.d.ts index 58c7f5e366..e13a478270 100644 --- a/libs/types/assisted-installer-service.d.ts +++ b/libs/types/assisted-installer-service.d.ts @@ -111,6 +111,7 @@ export interface Cluster { /** * Indicates the type of this object. Will be 'Cluster' if this is a complete object, * 'AddHostsCluster' for cluster that add hosts to existing OCP cluster, + * 'DisconnectedCluster' for clusters with embedded ignition for offline installation, * */ kind: 'Cluster' | 'AddHostsCluster' | 'DisconnectedCluster'; @@ -215,7 +216,8 @@ export interface Cluster { | 'installed' | 'adding-hosts' | 'cancelled' - | 'installing-pending-user-action'; + | 'installing-pending-user-action' + | 'unmonitored'; /** * Additional information pertaining to the status of the OpenShift cluster. */ @@ -357,8 +359,15 @@ export interface Cluster { featureUsage?: string; /** * The desired network type used. + * - OVNKubernetes: Default CNI for OpenShift (recommended) + * - OpenShiftSDN: Legacy SDN (deprecated in newer versions) + * - CiscoACI: Cisco ACI CNI (requires custom manifests) + * - Cilium: Isovalent Cilium CNI (requires custom manifests) + * - Calico: Tigera Calico CNI (requires custom manifests) + * - None: No CNI - user must provide custom CNI manifests + * */ - networkType?: 'OpenShiftSDN' | 'OVNKubernetes'; + networkType?: 'OpenShiftSDN' | 'OVNKubernetes' | 'CiscoACI' | 'Cilium' | 'Calico' | 'None'; /** * Cluster networks that are associated with this cluster. */ @@ -505,8 +514,17 @@ export interface ClusterCreateParams { | 'all'; /** * The desired network type used. + * - OVNKubernetes: Default CNI for OpenShift (recommended) + * - OpenShiftSDN: Legacy SDN (deprecated in newer versions) + * - CiscoACI: Cisco ACI CNI (requires custom manifests) + * - Cilium: Isovalent Cilium CNI (requires custom manifests) + * - Calico: Tigera Calico CNI (requires custom manifests) + * - None: No CNI - user must provide custom CNI manifests + * Note: Third-party CNIs (CiscoACI, Cilium, Calico, None) require uploading + * CNI manifests via the custom manifests API before installation. + * */ - networkType?: 'OpenShiftSDN' | 'OVNKubernetes'; + networkType?: 'OpenShiftSDN' | 'OVNKubernetes' | 'CiscoACI' | 'Cilium' | 'Calico' | 'None'; /** * Schedule workloads on masters */ @@ -666,6 +684,7 @@ export type ClusterValidationId = | 'mtv-requirements-satisfied' | 'osc-requirements-satisfied' | 'network-type-valid' + | 'custom-manifests-requirements-satisfied' | 'platform-requirements-satisfied' | 'node-feature-discovery-requirements-satisfied' | 'nvidia-gpu-requirements-satisfied' @@ -823,6 +842,16 @@ export interface DhcpAllocationResponse { */ ingressVipLease?: string; } +export interface DisconnectedClusterCreateParams { + /** + * Name of the OpenShift cluster. + */ + name: string; + /** + * Version of the OpenShift cluster. + */ + openshiftVersion: string; +} export interface Disk { /** * Determine the disk's unique identifier which is the by-id field if it exists and fallback to the by-path field otherwise @@ -1053,9 +1082,16 @@ export interface Event { props?: string; } export type EventList = Event[]; +export interface Feature { + 'feature-support-level-id': FeatureSupportLevelId; + supportLevel: SupportLevel; + reason?: IncompatibilityReason; + incompatibilities: FeatureSupportLevelId[]; +} export type FeatureSupportLevelId = | 'SNO' | 'TNA' + | 'TNF' | 'VIP_AUTO_ALLOC' | 'CUSTOM_MANIFEST' | 'SINGLE_NODE_EXPANSION' @@ -1081,6 +1117,10 @@ export type FeatureSupportLevelId = | 'EXTERNAL_PLATFORM' | 'OVN_NETWORK_TYPE' | 'SDN_NETWORK_TYPE' + | 'CILIUM_NETWORK_TYPE' + | 'CALICO_NETWORK_TYPE' + | 'CISCO_ACI_NETWORK_TYPE' + | 'NONE_NETWORK_TYPE' | 'NODE_FEATURE_DISCOVERY' | 'NVIDIA_GPU' | 'PIPELINES' @@ -1102,8 +1142,27 @@ export type FeatureSupportLevelId = | 'NUMA_RESOURCES' | 'OADP' | 'METALLB' + | 'DUAL_STACK_PRIMARY_IPV6' | 'LOKI' | 'OPENSHIFT_LOGGING'; +export interface FencingCredentialsParams { + /** + * The URL of the host's BMC, for example https://bmc1.example.com. + */ + address: string; + /** + * The username to connect to the host's BMC. + */ + username: string; + /** + * The password to connect to the host's BMC. + */ + password: string; + /** + * Whether to enable or disable certificate verification when connecting to the host's BMC. + */ + certificateVerification?: 'Enabled' | 'Disabled'; +} /** * Cluster finalizing stage managed by controller */ @@ -1318,6 +1377,10 @@ export interface Host { * formatting. */ skipFormattingDisks?: string; + /** + * The host's BMC credentials that will be used in TNF. + */ + fencingCredentials?: string; } export interface HostCreateParams { hostId: string; // uuid @@ -1525,6 +1588,10 @@ export interface HostRegistrationResponse { * formatting. */ skipFormattingDisks?: string; + /** + * The host's BMC credentials that will be used in TNF. + */ + fencingCredentials?: string; /** * Command for starting the next step runner */ @@ -1546,6 +1613,7 @@ export type HostStage = | 'Waiting for controller' | 'Installing' | 'Writing image to disk' + | 'Copying registry data to disk' | 'Rebooting' | 'Waiting for ignition' | 'Configuring' @@ -1603,6 +1671,10 @@ export interface HostUpdateParams { * Labels to be added to the corresponding node. */ nodeLabels?: NodeLabelParams[]; + /** + * The host's BMC credentials that will be used in TNF. + */ + fencingCredentials?: FencingCredentialsParams; } export type HostValidationId = | 'connected' @@ -1753,6 +1825,11 @@ export interface ImportClusterParams { */ openshiftClusterId: string; // uuid } +export type IncompatibilityReason = + | 'cpuArchitecture' + | 'platform' + | 'openshiftVersion' + | 'ociExternalIntegrationDisabled'; export interface InfraEnv { /** * Indicates the type of this object. @@ -1794,6 +1871,12 @@ export interface InfraEnv { * static network configuration string in the format expected by discovery ignition generation. */ staticNetworkConfig?: string; + /** + * The IP address of the host that will act as the rendezvous (bootstrap) node for agent-based installations. + * This is optional for disconnected-iso image type and specifies which host will run the assisted service + * during the bootstrap phase. All other hosts will connect to this IP to coordinate the installation. + */ + rendezvousIp?: string; // ^(?:$|(?:(?:(?:25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])\.){3}(?:25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])|(?:([0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:(:[0-9a-fA-F]{1,4}){1,6}|:(:[0-9a-fA-F]{1,4}){1,7}|:|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1?[0-9])?[0-9])\.){3}(25[0-5]|(2[0-4]|1?[0-9])?[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1?[0-9])?[0-9])\.){3}(25[0-5]|(2[0-4]|1?[0-9])?[0-9]))))$ type: ImageType; /** * Json formatted string containing the user overrides for the initial ignition config. @@ -1830,14 +1913,6 @@ export interface InfraEnv { * certificates in this bundle. */ additionalTrustBundle?: string; - /** - * The IP address that hosts will use to communicate with the bootstrap node during installation. - */ - rendezvousIp?: string; - /** - * The type of network configuration for hosts: 'dhcp' for DHCP only, 'static' for static IP configuration. - */ - hostsNetworkConfigurationType?: 'dhcp' | 'static'; /** * The pull secret obtained from Red Hat OpenShift Cluster Manager at console.redhat.com/openshift/install/pull-secret. */ @@ -1862,6 +1937,12 @@ export interface InfraEnvCreateParams { */ pullSecret: string; staticNetworkConfig?: HostStaticNetworkConfig[]; + /** + * The IP address of the host that will act as the rendezvous (bootstrap) node for agent-based installations. + * This is optional for disconnected-iso image type and specifies which host will run the assisted service + * during the bootstrap phase. All other hosts will connect to this IP to coordinate the installation. + */ + rendezvousIp?: string; // ^(?:$|(?:(?:(?:25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])\.){3}(?:25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])|(?:([0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:(:[0-9a-fA-F]{1,4}){1,6}|:(:[0-9a-fA-F]{1,4}){1,7}|:|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1?[0-9])?[0-9])\.){3}(25[0-5]|(2[0-4]|1?[0-9])?[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1?[0-9])?[0-9])\.){3}(25[0-5]|(2[0-4]|1?[0-9])?[0-9]))))$ imageType?: ImageType; /** * JSON formatted string containing the user overrides for the initial ignition config. @@ -1887,14 +1968,6 @@ export interface InfraEnvCreateParams { * certificates in this bundle. */ additionalTrustBundle?: string; - /** - * The IP address that hosts will use to communicate with the bootstrap node during installation. - */ - rendezvousIp?: string; - /** - * The type of network configuration for hosts: 'dhcp' for DHCP only, 'static' for static IP configuration. - */ - hostsNetworkConfigurationType?: 'dhcp' | 'static'; } export type InfraEnvList = InfraEnv[]; export interface InfraEnvUpdateParams { @@ -1912,6 +1985,12 @@ export interface InfraEnvUpdateParams { */ pullSecret?: string; staticNetworkConfig?: HostStaticNetworkConfig[]; + /** + * The IP address of the host that will act as the rendezvous (bootstrap) node for agent-based installations. + * This is optional for disconnected-iso image type and specifies which host will run the assisted service + * during the bootstrap phase. All other hosts will connect to this IP to coordinate the installation. + */ + rendezvousIp?: string; // ^(?:$|(?:(?:(?:25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])\.){3}(?:25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])|(?:([0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:(:[0-9a-fA-F]{1,4}){1,6}|:(:[0-9a-fA-F]{1,4}){1,7}|:|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1?[0-9])?[0-9])\.){3}(25[0-5]|(2[0-4]|1?[0-9])?[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1?[0-9])?[0-9])\.){3}(25[0-5]|(2[0-4]|1?[0-9])?[0-9]))))$ imageType?: ImageType; /** * JSON formatted string containing the user overrides for the initial ignition config. @@ -1926,14 +2005,6 @@ export interface InfraEnvUpdateParams { * Version of the OS image */ openshiftVersion?: string; - /** - * The IP address that hosts will use to communicate with the bootstrap node during installation. - */ - rendezvousIp?: string; - /** - * The type of network configuration for hosts: 'dhcp' for DHCP only, 'static' for static IP configuration. - */ - hostsNetworkConfigurationType?: 'dhcp' | 'static'; } export interface InfraError { /** @@ -2204,7 +2275,7 @@ export type MacInterfaceMap = { /** * mac address present on the host */ - macAddress?: string; // ^([0-9A-Fa-f]{2}[:]){5}([0-9A-Fa-f]{2})$ + macAddress: string; // ^([0-9A-Fa-f]{2}[:]){5}([0-9A-Fa-f]{2})$ /** * nic name used in the yaml, which relates 1:1 to the mac address */ @@ -2368,6 +2439,17 @@ export interface OpenshiftVersion { export interface OpenshiftVersions { [name: string]: OpenshiftVersion; } +export interface Operator { + 'feature-support-level-id': FeatureSupportLevelId; + supportLevel: SupportLevel; + reason?: IncompatibilityReason; + incompatibilities: FeatureSupportLevelId[]; + /** + * Name of the operator + */ + name: string; + dependencies: FeatureSupportLevelId[]; +} export interface OperatorCreateParams { name?: string; /** @@ -2875,8 +2957,17 @@ export interface V2ClusterUpdateParams { | 'all'; /** * The desired network type used. + * - OVNKubernetes: Default CNI for OpenShift (recommended) + * - OpenShiftSDN: Legacy SDN (deprecated in newer versions) + * - CiscoACI: Cisco ACI CNI (requires custom manifests) + * - Cilium: Isovalent Cilium CNI (requires custom manifests) + * - Calico: Tigera Calico CNI (requires custom manifests) + * - None: No CNI - user must provide custom CNI manifests + * Note: Third-party CNIs (CiscoACI, Cilium, Calico, None) require uploading + * CNI manifests via the custom manifests API before installation. + * */ - networkType?: 'OpenShiftSDN' | 'OVNKubernetes'; + networkType?: 'OpenShiftSDN' | 'OVNKubernetes' | 'CiscoACI' | 'Cilium' | 'Calico' | 'None'; /** * Schedule workloads on masters */ @@ -2933,6 +3024,13 @@ export interface V2OpenshiftVersions { version?: string; onlyLatest?: boolean; } +export interface V2OperatorsBundles { + openshiftVersion?: string; + cpuArchitecture?: 'x86_64' | 'aarch64' | 'arm64' | 'ppc64le' | 's390x' | 'multi'; + platformType?: 'baremetal' | 'none' | 'nutanix' | 'vsphere' | 'external'; + externalPlatformName?: string; + featureIds?: 'SNO'[]; +} export interface V2SupportLevelsArchitectures { openshiftVersion: string; } @@ -2942,6 +3040,12 @@ export interface V2SupportLevelsFeatures { platformType?: 'baremetal' | 'none' | 'nutanix' | 'vsphere' | 'external'; externalPlatformName?: string; } +export interface V2SupportLevelsFeaturesDetailed { + openshiftVersion: string; + cpuArchitecture?: 'x86_64' | 'arm64' | 'ppc64le' | 's390x' | 'multi'; + platformType?: 'baremetal' | 'none' | 'nutanix' | 'vsphere' | 'external'; + externalPlatformName?: string; +} /** * Single VIP verification result. */ diff --git a/libs/ui-lib-tests/cypress/fixtures/custom-manifests/1-cluster-created.ts b/libs/ui-lib-tests/cypress/fixtures/custom-manifests/1-cluster-created.ts index 027716a2ff..5407be9954 100644 --- a/libs/ui-lib-tests/cypress/fixtures/custom-manifests/1-cluster-created.ts +++ b/libs/ui-lib-tests/cypress/fixtures/custom-manifests/1-cluster-created.ts @@ -38,7 +38,7 @@ const customManifestsCluster = { // We're adding this field to easily debug which mock is returning the response feature_usage: JSON.stringify(featureUsage), control_plane_count: 3, - network_type: 'OpenShiftSDN', + network_type: 'Cilium', user_managed_networking: false, vip_dhcp_allocation: true, e2e_mock_source: '5-cluster-ready', diff --git a/libs/ui-lib-tests/cypress/integration/create-multinode/2-networking.cy.ts b/libs/ui-lib-tests/cypress/integration/create-multinode/2-networking.cy.ts index 27b4f43daf..cb7dc5ce06 100644 --- a/libs/ui-lib-tests/cypress/integration/create-multinode/2-networking.cy.ts +++ b/libs/ui-lib-tests/cypress/integration/create-multinode/2-networking.cy.ts @@ -60,8 +60,7 @@ describe(`Assisted Installer Multinode Networking`, () => { it('Should have the correct default network type', () => { networkingPage.getAdvancedNetwork().click(); - networkingPage.getSdnNetworkingField().should('be.enabled').and('not.be.checked'); - networkingPage.getOvnNetworkingField().should('be.enabled').and('be.checked'); + networkingPage.getNetworkTypeToggle().should('contain.text', 'Open Virtual Networking (OVN)'); }); }); }); diff --git a/libs/ui-lib-tests/cypress/integration/create-sno/2-networking.cy.ts b/libs/ui-lib-tests/cypress/integration/create-sno/2-networking.cy.ts index 74cc946037..f8d4139a52 100644 --- a/libs/ui-lib-tests/cypress/integration/create-sno/2-networking.cy.ts +++ b/libs/ui-lib-tests/cypress/integration/create-sno/2-networking.cy.ts @@ -36,8 +36,7 @@ describe(`Assisted Installer SNO Networking`, () => { it('Should have the correct default network type', () => { networkingPage.getAdvancedNetwork().click(); - networkingPage.getSdnNetworkingField().should('not.be.enabled').and('not.be.checked'); - networkingPage.getOvnNetworkingField().should('not.be.enabled').and('be.checked'); + networkingPage.getNetworkTypeToggle().should('contain.text', 'Open Virtual Networking (OVN)'); }); }); }); diff --git a/libs/ui-lib-tests/cypress/integration/custom-manifests/1-create-cluster-with-custom-manifests.cy.ts b/libs/ui-lib-tests/cypress/integration/custom-manifests/1-create-cluster-with-custom-manifests.cy.ts index 516ad082ec..c3c720f6a5 100644 --- a/libs/ui-lib-tests/cypress/integration/custom-manifests/1-create-cluster-with-custom-manifests.cy.ts +++ b/libs/ui-lib-tests/cypress/integration/custom-manifests/1-create-cluster-with-custom-manifests.cy.ts @@ -21,18 +21,9 @@ describe(`Assisted Installer Cluster Installation with Custom Manifests`, () => clusterDetailsPage.inputOpenshiftVersion(); clusterDetailsPage.inputPullSecret(); - clusterDetailsPage.getCustomManifestCheckbox().should('be.visible').check(); - clusterDetailsPage.getCustomManifestCheckbox().should('be.checked'); - commonActions - .getInfoAlert() - .should('contain.text', 'This is an advanced configuration feature.'); - commonActions.getWizardStepNav('Custom manifests').should('exist'); commonActions.getNextButton().click(); - cy.wait('@update-ui-settings').then(({ request }) => { - expect(request.body).to.deep.equal('AI_UI:{"addCustomManifests":true}'); - }); }); }); }); diff --git a/libs/ui-lib-tests/cypress/integration/custom-manifests/2-modifying-existing-custom-manifest.cy.ts b/libs/ui-lib-tests/cypress/integration/custom-manifests/2-modifying-existing-custom-manifest.cy.ts index 338b052456..378ec849e9 100644 --- a/libs/ui-lib-tests/cypress/integration/custom-manifests/2-modifying-existing-custom-manifest.cy.ts +++ b/libs/ui-lib-tests/cypress/integration/custom-manifests/2-modifying-existing-custom-manifest.cy.ts @@ -105,10 +105,9 @@ describe(`Assisted Installer Custom manifests step`, () => { 'Must have a yaml, yml, json, yaml.patch or yml.patch extension and can not contain /.', ); - CustomManifestsForm.validationAlert().should( - 'contain.text', - 'Custom manifests configuration contains missing or invalid fields', - ); + CustomManifestsForm.validationAlert() + .should('be.visible') + .and('contain.text', 'Custom manifests configuration contains missing or invalid fields'); commonActions.verifyNextIsDisabled(); }); @@ -130,10 +129,9 @@ describe(`Assisted Installer Custom manifests step`, () => { 'Must have a yaml, yml, json, yaml.patch or yml.patch extension and can not contain /.', ); - CustomManifestsForm.validationAlert().should( - 'contain.text', - 'Custom manifests configuration contains missing or invalid fields', - ); + CustomManifestsForm.validationAlert() + .should('be.visible') + .and('contain.text', 'Custom manifests configuration contains missing or invalid fields'); commonActions.verifyNextIsDisabled(); }); }); diff --git a/libs/ui-lib-tests/cypress/integration/dualstack/2-networking.cy.ts b/libs/ui-lib-tests/cypress/integration/dualstack/2-networking.cy.ts index 30566b900e..6f5160b0e1 100644 --- a/libs/ui-lib-tests/cypress/integration/dualstack/2-networking.cy.ts +++ b/libs/ui-lib-tests/cypress/integration/dualstack/2-networking.cy.ts @@ -39,8 +39,7 @@ describe(`Assisted Installer Dualstack Networking`, () => { networkingPage.getStackTypeDualStack().should('be.enabled').and('be.checked'); networkingPage.getClusterManagedNetworking().should('be.disabled').and('be.checked'); networkingPage.getVipDhcp().should('be.disabled').and('not.be.checked'); - networkingPage.getOvnNetworkingField().should('not.be.enabled').and('be.checked'); - networkingPage.getSdnNetworkingField().should('not.be.enabled').and('not.be.checked'); + networkingPage.getNetworkTypeToggle().should('contain.text', 'Open Virtual Networking (OVN)'); networkingPage .getClusterSubnetCidrIpv4() .should('contain.text', '192.168.122.0/24 (192.168.122.0 - 192.168.122.255)'); @@ -71,8 +70,7 @@ describe(`Assisted Installer Dualstack Networking`, () => { networkingPage.getClusterManagedNetworking().should('be.enabled').and('be.checked'); networkingPage.getStackTypeSingleStack().should('be.enabled').and('be.checked'); networkingPage.getVipDhcp().should('be.disabled').and('not.be.checked'); - networkingPage.getOvnNetworkingField().should('be.enabled').and('be.checked'); - networkingPage.getSdnNetworkingField().should('be.enabled').and('not.be.checked'); + networkingPage.getNetworkTypeToggle().should('contain.text', 'Open Virtual Networking (OVN)'); cy.wait('@update-cluster').then(({ request }) => { expect(request.body, 'Networking request body').to.deep.equal(ipv4NetworkingRequest); diff --git a/libs/ui-lib-tests/cypress/integration/use-cases/create-cluster/with-external-partner-integrations.cy.ts b/libs/ui-lib-tests/cypress/integration/use-cases/create-cluster/with-external-partner-integrations.cy.ts index c5f2b0af82..500a37567d 100644 --- a/libs/ui-lib-tests/cypress/integration/use-cases/create-cluster/with-external-partner-integrations.cy.ts +++ b/libs/ui-lib-tests/cypress/integration/use-cases/create-cluster/with-external-partner-integrations.cy.ts @@ -47,15 +47,6 @@ describe('Create a new cluster with external partner integrations', () => { }); }); - it('Selecting oracle as external partner integration enables custom manifests as well', () => { - ClusterDetailsForm.openshiftVersionField.selectVersion('4.14'); - ClusterDetailsForm.externalPartnerIntegrationsField.selectPlatform('Oracle'); - ClusterDetailsForm.customManifestsField - .findCheckbox() - .should('be.checked') - .and('be.disabled'); - }); - it('Validate that oracle as external partner integration is unselected in dropdown after OCP < v4.14 is selected', () => { ClusterDetailsForm.openshiftVersionField.selectVersion('4.14'); ClusterDetailsForm.externalPartnerIntegrationsField.selectPlatform('Oracle'); diff --git a/libs/ui-lib-tests/cypress/support/interceptors.ts b/libs/ui-lib-tests/cypress/support/interceptors.ts index 72bd385979..25fd13b1d3 100644 --- a/libs/ui-lib-tests/cypress/support/interceptors.ts +++ b/libs/ui-lib-tests/cypress/support/interceptors.ts @@ -135,9 +135,9 @@ const mockCustomManifestFileResponse: HttpRequestInterceptor = (req) => { const mockUISettingsResponse: HttpRequestInterceptor = (req) => { if (hasWizardSignal('CUSTOM_MANIFEST_ADDED')) { - req.reply('AI_UI:{"addCustomManifests":true,"customManifestsAdded":true}'); + req.reply('AI_UI:{"customManifestsAdded":true}'); } else if (hasWizardSignal('ONLY_DUMMY_CUSTOM_MANIFEST_ADDED')) { - req.reply('AI_UI:{"addCustomManifests":true}'); + req.reply('AI_UI:{}'); } else { req.reply('""'); } diff --git a/libs/ui-lib-tests/cypress/support/variables/networking.ts b/libs/ui-lib-tests/cypress/support/variables/networking.ts index 017d455776..c75f301fff 100644 --- a/libs/ui-lib-tests/cypress/support/variables/networking.ts +++ b/libs/ui-lib-tests/cypress/support/variables/networking.ts @@ -19,8 +19,7 @@ Cypress.env( Cypress.env('serviceNetworkCidrFieldHelperId', '#form-input-serviceNetworks-0-cidr-field-helper'); Cypress.env('userManagedNetworkingRadioText', 'User-Managed Networking'); Cypress.env('openVirtualNetworkingRadioText', 'Open Virtual Networking'); -Cypress.env('openshiftSdnInputValue', 'input[value="OpenShiftSDN"]'); -Cypress.env('ovnKubernetesRadioId', `#form-radio-networkType-OVNKubernetes-field`); +Cypress.env('networkTypeToggleId', '#form-input-networkType-field'); Cypress.env('hostSubnetFieldId', '#form-input-hostSubnet-field'); Cypress.env('clusterNetworkCidrFieldId', '#form-input-clusterNetworks-0-cidr-field'); Cypress.env('clusterNetworks0HostPrefixFieldId', '#form-input-clusterNetworks-0-hostPrefix-field'); diff --git a/libs/ui-lib-tests/cypress/views/forms/CustomManifests/CustomManifestsForm.ts b/libs/ui-lib-tests/cypress/views/forms/CustomManifests/CustomManifestsForm.ts index 3bb99f933c..2f7cf8afe6 100644 --- a/libs/ui-lib-tests/cypress/views/forms/CustomManifests/CustomManifestsForm.ts +++ b/libs/ui-lib-tests/cypress/views/forms/CustomManifests/CustomManifestsForm.ts @@ -13,7 +13,7 @@ export const CustomManifestsForm = { }, validationAlert: () => { - return cy.get('.pf-v6-c-alert.pf-m-danger'); + return cy.contains('.pf-v6-c-alert', 'Custom manifests configuration contains missing or invalid fields'); }, addManifest: () => { return CustomManifestsForm.body().findByTestId('add-manifest'); diff --git a/libs/ui-lib-tests/cypress/views/networkingPage.ts b/libs/ui-lib-tests/cypress/views/networkingPage.ts index 135587ffad..fc8f0cb669 100644 --- a/libs/ui-lib-tests/cypress/views/networkingPage.ts +++ b/libs/ui-lib-tests/cypress/views/networkingPage.ts @@ -277,11 +277,8 @@ export const networkingPage = { }); }); }, - getSdnNetworkingField: () => { - return cy.get(Cypress.env('openshiftSdnInputValue')).scrollIntoView(); - }, - getOvnNetworkingField: () => { - return cy.get(Cypress.env('ovnKubernetesRadioId')).scrollIntoView(); + getNetworkTypeToggle: () => { + return cy.get(Cypress.env('networkTypeToggleId')).scrollIntoView(); }, setOvnNetworking: () => { cy.get(`.pf-v6-c-radio__label:contains(${Cypress.env('openVirtualNetworkingRadioText')})`) diff --git a/libs/ui-lib/lib/common/config/constants.ts b/libs/ui-lib/lib/common/config/constants.ts index fe7ccb59c8..708c6b487a 100644 --- a/libs/ui-lib/lib/common/config/constants.ts +++ b/libs/ui-lib/lib/common/config/constants.ts @@ -53,6 +53,7 @@ export const clusterStatusLabels = (t: TFunction): { [key in Cluster['status']]: error: t('ai:Error'), installed: t('ai:Installed'), 'adding-hosts': t('ai:Adding hosts'), + unmonitored: t('ai:Unmonitored'), }); export const clusterFieldLabels = (t: TFunction): { [key in string]: string } => ({ @@ -334,6 +335,19 @@ export const NO_SUBNET_SET = 'NO_SUBNET_SET'; export const NETWORK_TYPE_OVN = 'OVNKubernetes'; export const NETWORK_TYPE_SDN = 'OpenShiftSDN'; +export const NETWORK_TYPE_CISCO_ACI = 'CiscoACI'; +export const NETWORK_TYPE_CILIUM = 'Cilium'; +export const NETWORK_TYPE_CALICO = 'Calico'; +export const NETWORK_TYPE_NONE = 'None'; + +export const NETWORK_TYPE_LABELS: Record = { + [NETWORK_TYPE_OVN]: 'Open Virtual Networking (OVN)', + [NETWORK_TYPE_SDN]: 'Software-Defined Networking (SDN)', + [NETWORK_TYPE_CISCO_ACI]: 'Cisco ACI', + [NETWORK_TYPE_CILIUM]: 'Isovalent Cilium', + [NETWORK_TYPE_CALICO]: 'Tigera Calico', + [NETWORK_TYPE_NONE]: 'None (Custom CNI)', +}; export const IPV4_STACK = 'singleStack'; export const DUAL_STACK = 'dualStack'; diff --git a/libs/ui-lib/lib/common/config/docs_links.ts b/libs/ui-lib/lib/common/config/docs_links.ts index d756870d06..b375517c5c 100644 --- a/libs/ui-lib/lib/common/config/docs_links.ts +++ b/libs/ui-lib/lib/common/config/docs_links.ts @@ -54,6 +54,8 @@ export const getOpenShiftNetworkingDocsLink = (ocpVersion?: string) => { return `https://docs.redhat.com/en/documentation/openshift_container_platform/${validOcpVersion}/html/installing_on_bare_metal/${variant}#installation-network-user-infra_installing-bare-metal`; }; +export const RED_HAT_CNI_SUPPORT_MATRIX_LINK = 'https://access.redhat.com/articles/5436171'; + export const SSH_GENERATION_DOC_LINK = 'https://www.redhat.com/sysadmin/configure-ssh-keygen'; //Hosts status diff --git a/libs/ui-lib/lib/common/types/ui-settings.ts b/libs/ui-lib/lib/common/types/ui-settings.ts index 13bcc62bf2..53c826f9b7 100644 --- a/libs/ui-lib/lib/common/types/ui-settings.ts +++ b/libs/ui-lib/lib/common/types/ui-settings.ts @@ -1,5 +1,4 @@ export type UISettingsValues = { - addCustomManifests?: boolean; customManifestsAdded?: boolean; customManifestsUpdated?: boolean; bundlesSelected?: string[]; diff --git a/libs/ui-lib/lib/ocm/components/clusterConfiguration/CustomManifestCheckbox.tsx b/libs/ui-lib/lib/ocm/components/clusterConfiguration/CustomManifestCheckbox.tsx deleted file mode 100644 index 9b02e0f6d6..0000000000 --- a/libs/ui-lib/lib/ocm/components/clusterConfiguration/CustomManifestCheckbox.tsx +++ /dev/null @@ -1,114 +0,0 @@ -import * as React from 'react'; -import { - Alert, - FormGroup, - FormHelperText, - HelperText, - HelperTextItem, -} from '@patternfly/react-core'; -import { useField } from 'formik'; -import { getFieldId, PopoverIcon } from '../../../common'; -import { OcmCheckbox } from '../ui/OcmFormFields'; -import { useClusterWizardContext } from '../clusterWizard/ClusterWizardContext'; -import DeleteCustomManifestModal from './manifestsConfiguration/DeleteCustomManifestModal'; -import { ClustersService } from '../../services'; -import { ClustersAPI } from '../../services/apis'; - -const Label = () => { - return ( - <> - Include custom manifests{' '} - - Incorporate third-party manifests that are not supported in Assisted Installer and APIs. -

- } - /> - - ); -}; - -type CustomManifestCheckboxProps = { clusterId: string; isDisabled: boolean }; - -const CustomManifestCheckbox = ({ clusterId, isDisabled }: CustomManifestCheckboxProps) => { - const [{ name }, { value }, { setValue }] = useField('addCustomManifest'); - const fieldId = getFieldId(name, 'input'); - const clusterWizardContext = useClusterWizardContext(); - const [isDeleteCustomManifestsOpen, setDeleteCustomManifestsOpen] = React.useState(false); - - const cleanCustomManifests = React.useCallback(async () => { - const { data: manifests } = await ClustersAPI.getManifests(clusterId); - void ClustersService.removeClusterManifests(manifests, clusterId); - - setValue(false); - clusterWizardContext.setCustomManifestsStep(false); - setDeleteCustomManifestsOpen(false); - await clusterWizardContext.updateUISettings({ - addCustomManifests: false, - customManifestsAdded: false, - }); - }, [clusterWizardContext, setValue, clusterId]); - - const onChange = React.useCallback( - (checked: boolean) => { - if (!checked && clusterWizardContext.uiSettings?.customManifestsAdded) { - setDeleteCustomManifestsOpen(true); - } - - setValue(checked); - clusterWizardContext.setCustomManifestsStep(checked); - }, - [setValue, clusterWizardContext, setDeleteCustomManifestsOpen], - ); - - const onClose = React.useCallback(() => { - setValue(true); - clusterWizardContext.setCustomManifestsStep(true); - setDeleteCustomManifestsOpen(false); - }, [clusterWizardContext, setValue]); - - return ( - <> - - } - aria-describedby={`${fieldId}-helper`} - description={ - - - - - Additional manifests will be applied at the install time for advanced - configuration of the cluster. - - - - - } - onChange={(_event, value) => onChange(value)} - className="with-tooltip" - isChecked={value} - isDisabled={isDisabled} - /> - void cleanCustomManifests()} - /> - - {value && ( - - Custom manifests will be added to the wizard as a new step. - - )} - - ); -}; - -export default CustomManifestCheckbox; diff --git a/libs/ui-lib/lib/ocm/components/clusterConfiguration/OcmClusterDetailsFormFields.tsx b/libs/ui-lib/lib/ocm/components/clusterConfiguration/OcmClusterDetailsFormFields.tsx index 0268d9b488..374ef7b40b 100644 --- a/libs/ui-lib/lib/ocm/components/clusterConfiguration/OcmClusterDetailsFormFields.tsx +++ b/libs/ui-lib/lib/ocm/components/clusterConfiguration/OcmClusterDetailsFormFields.tsx @@ -21,7 +21,6 @@ import { useTranslation } from '../../../common/hooks/use-translation-wrapper'; import { OcmRichInputField } from '../ui/OcmFormFields'; import OcmOpenShiftVersion from './OcmOpenShiftVersion'; import OcmOpenShiftVersionSelect from './OcmOpenShiftVersionSelect'; -import CustomManifestCheckbox from './CustomManifestCheckbox'; import CpuArchitectureDropdown from './CpuArchitectureDropdown'; import { OcmBaseDomainField } from './OcmBaseDomainField'; import useSupportLevelsAPI from '../../hooks/useSupportLevelsAPI'; @@ -35,7 +34,6 @@ import { ManagedDomain, PlatformType, } from '@openshift-assisted/types/assisted-installer-service'; -import { useClusterWizardContext } from '../clusterWizard/ClusterWizardContext'; import { useFeature } from '../../hooks/use-feature'; import ControlPlaneNodesDropdown, { ControlPlaneNodesLabel, @@ -68,7 +66,6 @@ export const OcmClusterDetailsFormFields = ({ const { openshiftVersion, platform } = values; const { getCpuArchitectures } = useOpenShiftVersionsContext(); const cpuArchitecturesByVersionImage = getCpuArchitectures(openshiftVersion); - const clusterWizardContext = useClusterWizardContext(); const featureSupportLevelData = useSupportLevelsAPI( 'features', values.openshiftVersion, @@ -91,12 +88,10 @@ export const OcmClusterDetailsFormFields = ({ (selectedPlatform: PlatformType) => { const isOracleSelected = selectedPlatform === 'external'; if (isOracleSelected) { - setFieldValue('addCustomManifest', isOracleSelected, false); - clusterWizardContext.setCustomManifestsStep(isOracleSelected); setFieldValue('hostsNetworkConfigurationType', HostsNetworkConfigurationType.DHCP); } }, - [clusterWizardContext, setFieldValue], + [setFieldValue], ); React.useEffect(() => { @@ -213,8 +208,6 @@ export const OcmClusterDetailsFormFields = ({ ) : ( )} - - {!isSingleClusterFeatureEnabled && ( ( - - - Custom manifests - - Upload additional manifests that will be applied at the install time for advanced - configuration of the cluster. - - - { + const clusterWizardContext = useClusterWizardContext(); + const isRequired = isThirdPartyCNI(cluster.networkType) || isOciPlatformType(cluster); + const [useCustomManifests, setUseCustomManifests] = React.useState(isRequired); + const [isDeleteModalOpen, setDeleteModalOpen] = React.useState(false); + + React.useEffect(() => { + if (isRequired || clusterWizardContext.uiSettings?.customManifestsAdded) { + setUseCustomManifests(true); + } + }, [isRequired, clusterWizardContext]); + + // The form unmounts when the switch is off, but its last state persists in the parent. + React.useEffect(() => { + if (!useCustomManifests) { + onFormStateChange({ + isValid: true, + isSubmitting: false, + isAutoSaveRunning: false, + errors: {}, + touched: {}, + isEmpty: true, + }); + } + }, [useCustomManifests, onFormStateChange]); + + const handleSwitchChange = React.useCallback( + (_event: React.FormEvent, on: boolean) => { + if (!on && clusterWizardContext.uiSettings?.customManifestsAdded) { + setDeleteModalOpen(true); + } else { + setUseCustomManifests(on); + if (!on) { + void clusterWizardContext.updateUISettings({ customManifestsAdded: false }); + } } - /> - - -); + }, + [clusterWizardContext], + ); + + const handleCloseDeleteModal = React.useCallback(() => { + setDeleteModalOpen(false); + }, []); + + const cleanCustomManifests = React.useCallback(async () => { + if (!clusterWizardContext.uiSettings?.customManifestsAdded || !cluster.id) return; + const { data: manifests } = await ClustersAPI.getManifests(cluster.id); + await ClustersService.removeClusterManifests(manifests, cluster.id); + setUseCustomManifests(false); + setDeleteModalOpen(false); + await clusterWizardContext.updateUISettings({ customManifestsAdded: false }); + }, [cluster.id, clusterWizardContext]); + + const tooltipWhenDisabled = isRequired + ? 'Custom manifests are required when using a third-party CNI or Oracle Cloud Infrastructure.' + : ''; + + const showExternalPlatformReminder = isOciPlatformType(cluster); + const showThirdPartyCnIReminder = isThirdPartyCNI(cluster.networkType); + const showCombinedReminder = showExternalPlatformReminder && showThirdPartyCnIReminder; + let reminderAlerts: React.ReactNode = null; + if (showCombinedReminder) { + reminderAlerts = ( + + + + Make sure to upload the required custom and CNI manifests in this step. + + + + ); + } else if (showExternalPlatformReminder) { + reminderAlerts = ( + + + + Make sure to upload the required custom manifests in this step. + + + + ); + } else if (showThirdPartyCnIReminder) { + reminderAlerts = ( + + + + Make sure to upload the required CNI manifests in this step. + + + + ); + } + + return ( + + + Custom manifests + + Upload additional manifests that will be applied at the install time for advanced + configuration of the cluster. + + + + + + {!useCustomManifests &&