From 3cc9cbd481e1efbd6b1920b667171ec6d643764f Mon Sep 17 00:00:00 2001 From: Nick De Villiers Date: Thu, 18 Jul 2024 11:14:13 +0100 Subject: [PATCH] feat(subnets): show error message if IP is already reserved/used MAASENG-3516 (#5502) - "Reserve static DHCP lease" form will now check if an IP is: - Already reserved - In use by another node --- Fixes [MAASENG-3516](https://warthogs.atlassian.net/browse/MAASENG-3516) --- .../ReserveDHCPLease/ReserveDHCPLease.tsx | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/src/app/subnets/views/SubnetDetails/StaticDHCPLease/ReserveDHCPLease/ReserveDHCPLease.tsx b/src/app/subnets/views/SubnetDetails/StaticDHCPLease/ReserveDHCPLease/ReserveDHCPLease.tsx index f4dc2affbc..cc049b4960 100644 --- a/src/app/subnets/views/SubnetDetails/StaticDHCPLease/ReserveDHCPLease/ReserveDHCPLease.tsx +++ b/src/app/subnets/views/SubnetDetails/StaticDHCPLease/ReserveDHCPLease/ReserveDHCPLease.tsx @@ -19,6 +19,7 @@ import { reservedIpActions } from "@/app/store/reservedip"; import reservedIpSelectors from "@/app/store/reservedip/selectors"; import type { RootState } from "@/app/store/root/types"; import subnetSelectors from "@/app/store/subnet/selectors"; +import { isSubnetDetails } from "@/app/store/subnet/utils"; import { getImmutableAndEditableOctets, getIpRangeFromCidr, @@ -49,6 +50,19 @@ const ReserveDHCPLease = ({ const reservedIp = useSelector((state: RootState) => reservedIpSelectors.getById(state, reservedIpId) ); + const subnetReservedIps = useSelector((state: RootState) => + reservedIpSelectors.getBySubnet(state, subnetId) + ); + + const subnetReservedIpList = subnetReservedIps + .map((reservedIp) => reservedIp.ip) + .filter((ipAddress) => ipAddress !== reservedIp?.ip); + const subnetUsedIps = isSubnetDetails(subnet) + ? subnet.ip_addresses + .map((address) => address.ip) + .filter((ipAddress) => ipAddress !== reservedIp?.ip) + : []; + const subnetLoading = useSelector(subnetSelectors.loading); const reservedIpLoading = useSelector(reservedIpSelectors.loading); const errors = useSelector(reservedIpSelectors.errors); @@ -123,6 +137,16 @@ const ReserveDHCPLease = ({ } } }, + }) + .test({ + name: "ip-already-reserved", + message: "This IP address is already used or reserved.", + test: (ip_address) => { + const ip = formatIpAddress(ip_address, subnet.cidr); + return ( + !subnetReservedIpList.includes(ip) && !subnetUsedIps.includes(ip) + ); + }, }), mac_address: Yup.string().matches(MAC_ADDRESS_REGEX, "Invalid MAC address"), comment: Yup.string(),