@@ -102,19 +102,22 @@ def is_unique_ip(
102102 ip_interface : Union [IPv4Interface , IPv6Interface ],
103103 server : "Server" ,
104104 addr_types : list [str ],
105- is_net : bool ,
106105 attribute_id : Optional [int ] = None ,
107106) -> None :
108- """Validate if IPv4/IPv6 interface is unique
107+ """Validate if given IP address is unique
109108
110109 Raises a ValidationError if intern_ip or any other attribute of type inet
111110 with this ip_address already exists.
112111
113- :param ip_interface:
114- :param server:
115- :param addr_types:
116- :param is_net:
117- :param attribute_id:
112+ :param ip_interface: The IP address to validate
113+ :param server: The current Server object to which the new IP address is
114+ to be assigned
115+ :param addr_types: Verify only against Servers of given IP_ADDR_TYPES.
116+ Allows for conflicting IP addresses, as long as they are on
117+ Servers with different IP_ADDR_TYPES.
118+ :param attribute_id: Verify only against this attribute. Allows for having
119+ conflicting IP addresses on different attributes, as long
120+ as they are on different Server objects.
118121 :return:
119122 """
120123
@@ -127,40 +130,88 @@ def is_unique_ip(
127130 # the same IP address as long as it is in different attributes.
128131
129132 duplicates = []
130- object_id = server .server_id
131133
132- if is_net :
133- servertype_inet_attr_q = Q ( server__servertype = server . servertype_id )
134- servertype_intern_ip_q = Q (servertype = server .servertype_id )
135- inet_attr_q = Q ( value__net_overlaps = ip_interface )
136- intern_ip_q = Q ( intern_ip__net_overlaps = ip_interface )
134+ # TODO: Make attribute_id mandatory when intern_ip is gone.
135+ if attribute_id :
136+ object_attribute_q = Q (server_id = server .server_id ) | ~ Q (
137+ attribute_id = attribute_id
138+ )
137139 else :
138- servertype_inet_attr_q = Q ()
139- servertype_intern_ip_q = Q ()
140- inet_attr_q = Q (value = ip_interface )
141- intern_ip_q = Q (intern_ip = ip_interface )
140+ object_attribute_q = Q (server_id = server .server_id )
141+
142+ # TODO: Remove intern_ip.
143+ for d in Server .objects .filter (
144+ Q (
145+ Q (intern_ip = ip_interface ) # IP address
146+ & Q (servertype__ip_addr_type__in = addr_types ) # IP address type
147+ & ~ Q (server_id = server .server_id ) # Self-server
148+ )
149+ ):
150+ duplicates .append (f"{ d .hostname } (intern_ip)" )
151+
152+ for d in ServerInetAttribute .objects .filter (
153+ Q (
154+ Q (value = ip_interface ) # IP address
155+ & Q (server__servertype__ip_addr_type__in = addr_types ) # IP address type
156+ & ~ object_attribute_q # Self-server
157+ )
158+ ):
159+ duplicates .append (f"{ d .server .hostname } ({ d .attribute } )" )
160+
161+ if duplicates :
162+ raise ValidationError (
163+ f"Can't set IP address { ip_interface } on { server .hostname } , conflicts with: { ', ' .join (duplicates )} "
164+ )
165+
166+
167+ def network_overlaps (
168+ ip_interface : Union [IPv4Interface , IPv6Interface ],
169+ server : "Server" ,
170+ addr_types : list [str ],
171+ attribute_id : Optional [int ] = None ,
172+ ) -> None :
173+ """Validate if given IP prefix is unique
174+
175+ Raises a ValidationError if the IP prefix overlaps with any other existing
176+ objects network of the given servertype.
177+
178+ :param ip_interface: The IP address to validate
179+ :param server: The current Server object to which the new IP prefix is
180+ to be assigned
181+ :param addr_types: Verify only against Servers of given IP_ADDR_TYPES.
182+ Allows for overlapping IP prefixes, as long as they are on
183+ Servers with different IP_ADDR_TYPES.
184+ :param attribute_id: Verify only against this attribute. Allows for having
185+ overlapping IP prefixes on different attributes, as long
186+ as they are on different Server objects.
187+ :return:
188+ """
189+
190+ duplicates = []
142191
143192 # TODO: Make attribute_id mandatory when intern_ip is gone.
144193 if attribute_id :
145- object_attribute_q = Q (server_id = object_id ) | ~ Q (attribute_id = attribute_id )
194+ object_attribute_q = Q (server_id = server .server_id ) | ~ Q (
195+ attribute_id = attribute_id
196+ )
146197 else :
147- object_attribute_q = Q (server_id = object_id )
198+ object_attribute_q = Q (server_id = server . server_id )
148199
149200 # TODO: Remove intern_ip.
150201 for d in Server .objects .filter (
151202 Q (
152- servertype_intern_ip_q # Servertype
153- & intern_ip_q # IP address
203+ Q ( servertype = server . servertype_id ) # Servertype
204+ & Q ( intern_ip__net_overlaps = ip_interface ) # IP address
154205 & Q (servertype__ip_addr_type__in = addr_types ) # IP address type
155- & ~ Q (server_id = object_id ) # Self-server
206+ & ~ Q (server_id = server . server_id ) # Self-server
156207 )
157208 ):
158209 duplicates .append (f"{ d .hostname } (intern_ip)" )
159210
160211 for d in ServerInetAttribute .objects .filter (
161212 Q (
162- servertype_inet_attr_q # Servertype
163- & inet_attr_q # IP address
213+ Q ( server__servertype = server . servertype_id ) # Servertype
214+ & Q ( value__net_overlaps = ip_interface ) # IP address
164215 & Q (server__servertype__ip_addr_type__in = addr_types ) # IP address type
165216 & ~ object_attribute_q # Self-server
166217 )
@@ -495,10 +546,10 @@ def clean(self):
495546
496547 if ip_addr_type == "host" :
497548 is_ip_address (self .intern_ip )
498- is_unique_ip (self .intern_ip , self , ["host" ], False )
549+ is_unique_ip (self .intern_ip , self , ["host" ])
499550 elif ip_addr_type == "network" :
500551 is_network (self .intern_ip )
501- is_unique_ip (self .intern_ip , self , ["network" ], True )
552+ network_overlaps (self .intern_ip , self , ["network" ])
502553 elif ip_addr_type == "loadbalancer" :
503554 is_ip_address (self .intern_ip )
504555
@@ -726,10 +777,10 @@ def clean(self):
726777 )
727778 elif ip_addr_type == "host" :
728779 is_ip_address (self .value )
729- is_unique_ip (self .value , self .server , ["host" ], False , self .attribute_id )
780+ is_unique_ip (self .value , self .server , ["host" ], self .attribute_id )
730781 elif ip_addr_type == "network" :
731782 is_network (self .value )
732- is_unique_ip (self .value , self .server , ["network" ], True , self .attribute_id )
783+ network_overlaps (self .value , self .server , ["network" ], self .attribute_id )
733784 elif ip_addr_type == "loadbalancer" :
734785 is_ip_address (self .value )
735786
0 commit comments