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
3 changes: 2 additions & 1 deletion libs/locales/lib/en/translation.json
Original file line number Diff line number Diff line change
Expand Up @@ -870,7 +870,6 @@
"ai:Tang servers' URLs or thumbprints": "Tang servers' URLs or thumbprints",
"ai:Technology Preview": "Technology Preview",
"ai:Technology preview features provide early access to upcoming product innovations, enabling you to test functionality and provide feedback during the development process.": "Technology preview features provide early access to upcoming product innovations, enabling you to test functionality and provide feedback during the development process.",
"ai:The {{label}} Ingress and API IP addresses cannot be the same.": "The {{label}} Ingress and API IP addresses cannot be the same.",
"ai:The agent is not bound to a cluster.": "The agent is not bound to a cluster.",
"ai:The agent ran successfully": "The agent ran successfully",
"ai:The block must not overlap with existing physical networks. To access the Pods from an external network, configure load balancers and routers to manage the traffic.": "The block must not overlap with existing physical networks. To access the Pods from an external network, configure load balancers and routers to manage the traffic.",
Expand Down Expand Up @@ -902,10 +901,12 @@
"ai:The IP address that hosts will use to communicate with the bootstrap node during installation.": "The IP address that hosts will use to communicate with the bootstrap node during installation.",
"ai:The MAC address of the host's network connected NIC that will be used to provision the host.": "The MAC address of the host's network connected NIC that will be used to provision the host.",
"ai:The output displays the following:": "The output displays the following:",
"ai:The primary Ingress and API IP addresses cannot be the same.": "The primary Ingress and API IP addresses cannot be the same.",
"ai:The resource has been saved and the YAML is now read only.": "The resource has been saved and the YAML is now read only.",
"ai:The resource you are changing is already in use by hosts in the infrastructure environment. A change will require booting the hosts with a new discovery ISO file.": "The resource you are changing is already in use by hosts in the infrastructure environment. A change will require booting the hosts with a new discovery ISO file.",
"ai:The resource you are changing is already in use by hosts in the infrastructure environment. A change will require booting the hosts with a new discovery ISO file. Hosts will be rebooted automatically after the change is applied if using BMC.": "The resource you are changing is already in use by hosts in the infrastructure environment. A change will require booting the hosts with a new discovery ISO file. Hosts will be rebooted automatically after the change is applied if using BMC.",
"ai:The resource you are changing is already in use by hosts in the infrastructure environment. The hosts will be rebooted automatically after the change is applied.": "The resource you are changing is already in use by hosts in the infrastructure environment. The hosts will be rebooted automatically after the change is applied.",
"ai:The secondary Ingress and API IP addresses cannot be the same.": "The secondary Ingress and API IP addresses cannot be the same.",
"ai:The specified CIDR is invalid because its resulting routing prefix matches the unspecified address.": "The specified CIDR is invalid because its resulting routing prefix matches the unspecified address.",
"ai:The storage sizes will be used to store different files and data for cluster creation.": "The storage sizes will be used to store different files and data for cluster creation.",
"ai:The subnet prefix length to assign to each individual node.": "The subnet prefix length to assign to each individual node.",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -243,7 +243,6 @@ export const VirtualIPControlGroup = ({
}
name="apiVips.1.ip"
maxLength={45}
isRequired
isDisabled={isVipInputDisabled || isViewerMode}
labelInfo={t('ai:Secondary')}
/>
Expand Down Expand Up @@ -275,7 +274,6 @@ export const VirtualIPControlGroup = ({
</>
}
maxLength={45}
isRequired
isDisabled={isVipInputDisabled || isViewerMode}
labelInfo={t('ai:Secondary')}
/>
Expand Down
27 changes: 16 additions & 11 deletions libs/ui-lib/lib/common/validationSchemas/addressValidation.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,7 @@ import { getAddress } from '../components/ui/formik/utils';
import { getSubnet } from '../components/clusterConfiguration/utils';
import { HostSubnets, NetworkConfigurationValues } from '../types';
import { IP_V4_ZERO, IP_V6_ZERO, MAC_REGEX } from './regexes';
import {
alwaysRequired,
getArrayIndexFromPath,
isIPorDN,
isIPv4Address,
isIPv6Address,
} from './utils';
import { getArrayIndexFromPath, isIPorDN, isIPv4Address, isIPv6Address } from './utils';
import { NO_SUBNET_SET } from '../config';
import { overlap } from 'cidr-tools';
import parseUrl from 'parse-url';
Expand All @@ -28,6 +22,7 @@ export const ipValidationSchema = (t: TFunction) =>

export const ipNoSuffixValidationSchema = (t: TFunction) =>
Yup.string().test('ip-validation-no-suffix', t('ai:Not a valid IP address'), (value?: string) => {
if (!value) return true;
const address = getAddress(value || '');
return !!address && address.address === address.addressMinusSuffix;
});
Expand Down Expand Up @@ -173,15 +168,25 @@ export const vipNoSuffixValidationSchema = (
}
if (apiVip === ingressVip) {
return this.createError({
message: t('ai:The {{label}} Ingress and API IP addresses cannot be the same.', {
label: index === 0 ? t('ai:primary') : t('ai:secondary'),
}),
message:
index === 0
? t('ai:The primary Ingress and API IP addresses cannot be the same.')
: t('ai:The secondary Ingress and API IP addresses cannot be the same.'),
});
}
return true;
});
const requiredField = (message: string) =>
Yup.string().test('required-field', message, function (value?: string) {
const index = getArrayIndexFromPath(this.path || '');
const api1 = (values.apiVips?.[1]?.ip ?? '').toString().trim();
const ingress1 = (values.ingressVips?.[1]?.ip ?? '').toString().trim();
// Dual-stack: secondary VIPs (index 1) are optional; skip required when both are empty
if (index === 1 && api1 === '' && ingress1 === '') return true;
return !!value;
});

return alwaysRequired(t('ai:Required field'))
return requiredField(t('ai:Required field'))
.concat(ipNoSuffixValidationSchema(t))
.concat(vipFamilyMatchSchema)
.concat(vipRangeValidationSchema(hostSubnets, values, false, t))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -236,9 +236,13 @@ const NetworkConfigurationPage = ({ cluster }: { cluster: Cluster }) => {
const isMultiNodeCluster = !isSNO(cluster);
const isUserManagedNetworking = values.managedNetworkingType === 'userManaged';

// Only send VIPs that have a non-empty IP;
const apiVips = (values.apiVips || []).filter((v) => (v?.ip ?? '').trim() !== '');
const ingressVips = (values.ingressVips || []).filter((v) => (v?.ip ?? '').trim() !== '');

const params: V2ClusterUpdateParams = {
apiVips: values.apiVips,
ingressVips: values.ingressVips,
apiVips,
ingressVips,
sshPublicKey: values.sshPublicKey,
vipDhcpAllocation: values.vipDhcpAllocation,
networkType: values.networkType,
Expand All @@ -247,7 +251,6 @@ const NetworkConfigurationPage = ({ cluster }: { cluster: Cluster }) => {
serviceNetworks: values.serviceNetworks,
userManagedNetworking: isUserManagedNetworking,
};

if (params.userManagedNetworking) {
params.apiVips = [];
params.ingressVips = [];
Expand Down
Loading