Skip to content

Commit c9fb976

Browse files
committed
Fix is_unique_ip() logic
We allow Servers to have various inet attribute types which configure behaviour of checking for IP address conflics. Type "loadbalancer" does not have an uniqueness check, type "network" only checks within the same servertype. Ensure that the uniqueness of IP address is checked symmetrically: "host" inet attribute type must not ensure its uniqueness against "network" and "loadbalancer", because they don't check against "host". For example it was possible to create a domain object for IP address of a VM, but it was impossible to create a VM once a domain existed. Improve the message shown to user in case of conflict.
1 parent 83758ca commit c9fb976

File tree

1 file changed

+25
-18
lines changed

1 file changed

+25
-18
lines changed

serveradmin/serverdb/models.py

Lines changed: 25 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
IPv4Network,
1616
IPv6Network,
1717
)
18-
from typing import Union
18+
from typing import Optional, Union
1919

2020
import netfields
2121
from django.contrib.auth.models import User
@@ -100,8 +100,9 @@ def is_ip_address(ip_interface: Union[IPv4Interface, IPv6Interface]) -> None:
100100

101101
def is_unique_ip(
102102
ip_interface: Union[IPv4Interface, IPv6Interface],
103-
object_id: int,
104-
attribute_id: str = None,
103+
server: "Server",
104+
exclude_addr_types: list[str],
105+
attribute_id: Optional[str] = None,
105106
) -> None:
106107
"""Validate if IPv4/IPv6 address is unique
107108
@@ -126,6 +127,9 @@ def is_unique_ip(
126127
# only withing the same attribute id. That means different hosts can have
127128
# the same IP address as long as it is in different attributes.
128129

130+
duplicates = []
131+
object_id = server.server_id
132+
129133
# TODO: Make "aid" mandatory when intern_ip is gone.
130134
if attribute_id:
131135
object_attribute_condition = Q(server_id=object_id) | ~Q(
@@ -134,20 +138,21 @@ def is_unique_ip(
134138
else:
135139
object_attribute_condition = Q(server_id=object_id)
136140

137-
has_duplicates = (
138-
# TODO: Remove intern_ip.
139-
Server.objects.filter(intern_ip=ip_interface)
140-
.exclude(Q(servertype__ip_addr_type="network") | Q(server_id=object_id))
141-
.exists()
142-
or ServerInetAttribute.objects.filter(value=ip_interface)
143-
.exclude(
144-
Q(server__servertype__ip_addr_type="network") | object_attribute_condition
145-
)
146-
.exists()
147-
)
148-
if has_duplicates:
141+
# TODO: Remove intern_ip.
142+
for d in Server.objects.filter(intern_ip=ip_interface).exclude(
143+
Q(servertype__ip_addr_type__in=exclude_addr_types) | Q(server_id=object_id)
144+
):
145+
duplicates.append(f"{d.hostname} (intern_ip)")
146+
147+
for d in ServerInetAttribute.objects.filter(value=ip_interface).exclude(
148+
Q(server__servertype__ip_addr_type__in=exclude_addr_types)
149+
| object_attribute_condition
150+
):
151+
duplicates.append(f"{d.server.hostname} ({d.attribute})")
152+
153+
if duplicates:
149154
raise ValidationError(
150-
"An object with {0} already exists".format(str(ip_interface))
155+
f"Can't set IP address {ip_interface} on {server.hostname}, conflicts with: {', '.join(duplicates)}"
151156
)
152157

153158

@@ -510,7 +515,7 @@ def clean(self):
510515

511516
if ip_addr_type == "host":
512517
is_ip_address(self.intern_ip)
513-
is_unique_ip(self.intern_ip, self.server_id)
518+
is_unique_ip(self.intern_ip, self, ["network", "loadbalancer"])
514519
elif ip_addr_type == "loadbalancer":
515520
is_ip_address(self.intern_ip)
516521
elif ip_addr_type == "network":
@@ -743,7 +748,9 @@ def clean(self):
743748
)
744749
elif ip_addr_type == "host":
745750
is_ip_address(self.value)
746-
is_unique_ip(self.value, self.server.server_id, self.attribute_id)
751+
is_unique_ip(
752+
self.value, self.server, ["network", "loadbalancer"], self.attribute_id
753+
)
747754
elif ip_addr_type == "loadbalancer":
748755
is_ip_address(self.value)
749756
elif ip_addr_type == "network":

0 commit comments

Comments
 (0)