Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ import {
Grid,
} from '@patternfly/react-core';
import { FieldArray, useFormikContext } from 'formik';
import { Address6 } from 'ip-address';
import {
DUAL_STACK,
PREFIX_MAX_RESTRICTION,
Expand All @@ -33,62 +32,10 @@ const serviceCidrHelperText =
'Enter only 1 IP address pool. If you need to access the services from an external network, configure load balancers and routers to manage the traffic.';

const AdvancedNetworkFields = () => {
const { values, errors, setFieldValue } = useFormikContext<NetworkConfigurationValues>();
const { values, errors } = useFormikContext<NetworkConfigurationValues>();

const isDualStack = values.stackType === DUAL_STACK;

// Reorder Cluster Networks and Service Networks when Machine Network primary changes
React.useEffect(() => {
if (!isDualStack || !values.machineNetworks?.[0]?.cidr) return;

const primaryMachineNetworkCidr = values.machineNetworks[0].cidr;
const isPrimaryIPv6 = Address6.isValid(primaryMachineNetworkCidr);

// Check Cluster Networks
if (values.clusterNetworks && values.clusterNetworks.length >= 2) {
const firstClusterCidr = values.clusterNetworks[0].cidr;
if (!firstClusterCidr) return;

const isFirstClusterIPv6 = Address6.isValid(firstClusterCidr);

// If primary machine is IPv6 but first cluster is IPv4, swap them
if (isPrimaryIPv6 && !isFirstClusterIPv6) {
const reordered = [values.clusterNetworks[1], values.clusterNetworks[0]];
setFieldValue('clusterNetworks', reordered, false);
}
// If primary machine is IPv4 but first cluster is IPv6, swap them
else if (!isPrimaryIPv6 && isFirstClusterIPv6) {
const reordered = [values.clusterNetworks[1], values.clusterNetworks[0]];
setFieldValue('clusterNetworks', reordered, false);
}
}

// Check Service Networks
if (values.serviceNetworks && values.serviceNetworks.length >= 2) {
const firstServiceCidr = values.serviceNetworks[0].cidr;
if (!firstServiceCidr) return;

const isFirstServiceIPv6 = Address6.isValid(firstServiceCidr);

// If primary machine is IPv6 but first service is IPv4, swap them
if (isPrimaryIPv6 && !isFirstServiceIPv6) {
const reordered = [values.serviceNetworks[1], values.serviceNetworks[0]];
setFieldValue('serviceNetworks', reordered, false);
}
// If primary machine is IPv4 but first service is IPv6, swap them
else if (!isPrimaryIPv6 && isFirstServiceIPv6) {
const reordered = [values.serviceNetworks[1], values.serviceNetworks[0]];
setFieldValue('serviceNetworks', reordered, false);
}
}
}, [
isDualStack,
values.machineNetworks,
values.clusterNetworks,
values.serviceNetworks,
setFieldValue,
]);

const clusterNetworkCidrPrefix = (index: number) =>
parseInt(
((values.clusterNetworks && values.clusterNetworks[index].cidr) || '').split('/')[1],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import {
} from '../../../../common';
import { selectCurrentClusterPermissionsState } from '../../../store/slices/current-cluster/selectors';
import { SubnetsDropdown } from './SubnetsDropdown';
import { reorderNetworksForPrimary } from './reorderNetworks';
import { Cluster, MachineNetwork } from '@openshift-assisted/types/assisted-installer-service';

const subnetSort = (subA: HostSubnet, subB: HostSubnet) =>
Expand All @@ -26,7 +27,7 @@ const useAutoSelectSingleAvailableSubnet = (
) => {
useEffect(() => {
if (autoSelectNetwork) {
setFieldValue('machineNetworks', [{ cidr, clusterId }], true);
setFieldValue('machineNetworks', [{ cidr, clusterId }], false);
}
}, [autoSelectNetwork, cidr, clusterId, setFieldValue]);
};
Expand Down Expand Up @@ -90,13 +91,19 @@ export const AvailableSubnetsControl = ({
if (Address6.isValid(first)) {
const nextIPv4 = IPv4Subnets[0]?.subnet || NO_SUBNET_SET;
const replacement = nextIPv4 !== first ? nextIPv4 : NO_SUBNET_SET;
setFieldValue('machineNetworks.1.cidr', replacement, true);
if (replacement !== second) {
setFieldValue('machineNetworks.1.cidr', replacement, false);
}
} else if (Address4.isValid(first)) {
const nextIPv6 = IPv6Subnets[0]?.subnet || NO_SUBNET_SET;
const replacement = nextIPv6 !== first ? nextIPv6 : NO_SUBNET_SET;
setFieldValue('machineNetworks.1.cidr', replacement, true);
if (replacement !== second) {
setFieldValue('machineNetworks.1.cidr', replacement, false);
}
} else {
setFieldValue('machineNetworks.1.cidr', NO_SUBNET_SET, true);
if (second !== NO_SUBNET_SET) {
setFieldValue('machineNetworks.1.cidr', NO_SUBNET_SET, false);
}
}
}
}, [isDualStack, values.machineNetworks, IPv4Subnets, IPv6Subnets, setFieldValue]);
Expand Down Expand Up @@ -124,6 +131,9 @@ export const AvailableSubnetsControl = ({
name={`machineNetworks.${index}.cidr`}
machineSubnets={machineSubnets}
isDisabled={isDisabled}
onAfterSelect={(newSelection) =>
reorderNetworksForPrimary(newSelection, values, setFieldValue)
}
data-testid={`subnets-dropdown-toggle-${index ? 'ipv6' : 'ipv4'}`}
/>
</StackItem>
Expand All @@ -135,6 +145,9 @@ export const AvailableSubnetsControl = ({
name={`machineNetworks.0.cidr`}
machineSubnets={IPv4Subnets}
isDisabled={isDisabled}
onAfterSelect={(newSelection) =>
reorderNetworksForPrimary(newSelection, values, setFieldValue)
}
data-testid={'subnets-dropdown-toggle-primary'}
/>
</StackItem>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import { ConfirmationModal, PopoverIcon } from '../../../../common/components/ui
import { useDefaultConfiguration } from '../ClusterDefaultConfigurationContext';
import { selectCurrentClusterPermissionsState } from '../../../store/slices/current-cluster/selectors';
import { OcmRadioField } from '../../ui/OcmFormFields';
import { reorderNetworksByCurrentPrimary } from './reorderNetworks';
import {
Cluster,
ClusterNetwork,
Expand Down Expand Up @@ -215,6 +216,8 @@ export const StackTypeControlGroup = ({
false,
);
}
// Ensure cluster/service networks ordering matches the new primary family
reorderNetworksByCurrentPrimary(values, setFieldValue);
};

const setStackType = (e: React.ChangeEvent<HTMLInputElement>) => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ type SubnetsDropdownProps = {
name: string;
machineSubnets: HostSubnet[];
isDisabled: boolean;
onAfterSelect?: (selectedValue: string) => void;
};

const toFormSelectOptions = (subnets: HostSubnet[]) => {
Expand Down Expand Up @@ -51,6 +52,7 @@ export const SubnetsDropdown = ({
name,
machineSubnets,
isDisabled,
onAfterSelect,
...props
}: SubnetsDropdownProps & MenuToggleProps) => {
const [field, , { setValue }] = useField(name);
Expand Down Expand Up @@ -142,8 +144,12 @@ export const SubnetsDropdown = ({
}, [machineSubnets, ipv4Subnets, ipv6Subnets, itemsSubnets]);

const onSelect = (event?: React.MouseEvent<Element, MouseEvent>): void => {
setValue(event?.currentTarget.id);
const nextValue = event?.currentTarget.id;
setValue(nextValue);
setOpen(false);
if (onAfterSelect && nextValue) {
onAfterSelect(nextValue);
}
};

const currentItem = itemsSubnets.find((i) => i.label === currentDisplayValue);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
import { Address6 } from 'ip-address';
import { DUAL_STACK, NetworkConfigurationValues } from '../../../../common';
import type { FormikHelpers } from 'formik';

const reorderByPrimaryCidr = (
primaryCidr: string,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

values: NetworkConfigurationValues,
setFieldValue: FormikHelpers<NetworkConfigurationValues>['setFieldValue'],
) => {
const isDualStack = values.stackType === DUAL_STACK;
if (!isDualStack || !primaryCidr) return;

const isPrimaryIPv6 = Address6.isValid(primaryCidr);

// Reorder Cluster Networks
if (Array.isArray(values.clusterNetworks) && values.clusterNetworks.length >= 2) {
const [first, second] = values.clusterNetworks;
const firstClusterCidr = first?.cidr;
const secondClusterCidr = second?.cidr;
if (firstClusterCidr && secondClusterCidr) {
const isFirstClusterIPv6 = Address6.isValid(firstClusterCidr);
const isSecondClusterIPv6 = Address6.isValid(secondClusterCidr);
if (isFirstClusterIPv6 !== isSecondClusterIPv6) {
if (
(isPrimaryIPv6 && !isFirstClusterIPv6 && isSecondClusterIPv6) ||
(!isPrimaryIPv6 && isFirstClusterIPv6 && !isSecondClusterIPv6)
) {
const reordered = [values.clusterNetworks[1], values.clusterNetworks[0]];
setFieldValue('clusterNetworks', reordered, false);
}
}
}
}

// Reorder Service Networks
if (Array.isArray(values.serviceNetworks) && values.serviceNetworks.length >= 2) {
const [first, second] = values.serviceNetworks;
const firstServiceCidr = first?.cidr;
const secondServiceCidr = second?.cidr;
if (firstServiceCidr && secondServiceCidr) {
const isFirstServiceIPv6 = Address6.isValid(firstServiceCidr);
const isSecondServiceIPv6 = Address6.isValid(secondServiceCidr);
if (isFirstServiceIPv6 !== isSecondServiceIPv6) {
if (
(isPrimaryIPv6 && !isFirstServiceIPv6 && isSecondServiceIPv6) ||
(!isPrimaryIPv6 && isFirstServiceIPv6 && !isSecondServiceIPv6)
) {
const reordered = [values.serviceNetworks[1], values.serviceNetworks[0]];
setFieldValue('serviceNetworks', reordered, false);
}
}
}
}
};

export const reorderNetworksForPrimary = (
nextPrimaryCidr: string,
values: NetworkConfigurationValues,
setFieldValue: FormikHelpers<NetworkConfigurationValues>['setFieldValue'],
) => {
if (!nextPrimaryCidr) return;
reorderByPrimaryCidr(nextPrimaryCidr, values, setFieldValue);
};

export const reorderNetworksByCurrentPrimary = (
values: NetworkConfigurationValues,
setFieldValue: FormikHelpers<NetworkConfigurationValues>['setFieldValue'],
) => {
const primaryCidr = values.machineNetworks?.[0]?.cidr;
if (!primaryCidr) return;
reorderByPrimaryCidr(primaryCidr, values, setFieldValue);
};
Loading