@@ -101,24 +101,23 @@ def is_ip_address(ip_interface: Union[IPv4Interface, IPv6Interface]) -> None:
101
101
def is_unique_ip (
102
102
ip_interface : Union [IPv4Interface , IPv6Interface ],
103
103
server : "Server" ,
104
- exclude_addr_types : list [str ],
105
- attribute_id : Optional [str ] = None ,
104
+ addr_types : list [str ],
105
+ is_net : bool ,
106
+ attribute_id : Optional [int ] = None ,
106
107
) -> None :
107
- """Validate if IPv4/IPv6 address is unique
108
+ """Validate if IPv4/IPv6 interface is unique
108
109
109
110
Raises a ValidationError if intern_ip or any other attribute of type inet
110
111
with this ip_address already exists.
111
112
112
113
:param ip_interface:
113
- :param object_id:
114
+ :param server:
115
+ :param addr_types:
116
+ :param is_net:
114
117
:param attribute_id:
115
118
:return:
116
119
"""
117
120
118
- # We avoid querying the duplicate hosts here and giving the user
119
- # detailed information because checking with exists is cheaper than
120
- # querying the server and this is a validation and should be fast.
121
-
122
121
# Always exclude the current object_id from the query because we allow
123
122
# duplication of data between the legacy (intern_ip, primary_ip6) and
124
123
# the modern (ipv4, ipv6) attributes.
@@ -130,23 +129,41 @@ def is_unique_ip(
130
129
duplicates = []
131
130
object_id = server .server_id
132
131
133
- # TODO: Make "aid" mandatory when intern_ip is gone.
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 )
137
+ 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 )
142
+
143
+ # TODO: Make attribute_id mandatory when intern_ip is gone.
134
144
if attribute_id :
135
- object_attribute_condition = Q (server_id = object_id ) | ~ Q (
136
- attribute_id = attribute_id
137
- )
145
+ object_attribute_q = Q (server_id = object_id ) | ~ Q (attribute_id = attribute_id )
138
146
else :
139
- object_attribute_condition = Q (server_id = object_id )
147
+ object_attribute_q = Q (server_id = object_id )
140
148
141
149
# 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 )
150
+ for d in Server .objects .filter (
151
+ Q (
152
+ servertype_intern_ip_q # Servertype
153
+ & intern_ip_q # IP address
154
+ & Q (servertype__ip_addr_type__in = addr_types ) # IP address type
155
+ & ~ Q (server_id = object_id ) # Self-server
156
+ )
144
157
):
145
158
duplicates .append (f"{ d .hostname } (intern_ip)" )
146
159
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
160
+ for d in ServerInetAttribute .objects .filter (
161
+ Q (
162
+ servertype_inet_attr_q # Servertype
163
+ & inet_attr_q # IP address
164
+ & Q (server__servertype__ip_addr_type__in = addr_types ) # IP address type
165
+ & ~ object_attribute_q # Self-server
166
+ )
150
167
):
151
168
duplicates .append (f"{ d .server .hostname } ({ d .attribute } )" )
152
169
@@ -187,43 +204,6 @@ def inet_to_python(obj: object) -> Union[IPv4Interface, IPv6Interface]:
187
204
raise ValidationError (str (error ))
188
205
189
206
190
- def network_overlaps (
191
- ip_interface : Union [IPv4Interface , IPv6Interface ],
192
- servertype_id : str ,
193
- object_id : int ,
194
- ) -> None :
195
- """Validate if network overlaps with other objects of the servertype_id
196
-
197
- Raises a ValidationError if the ip network overlaps with any other existing
198
- objects network of the given servertype.
199
-
200
- :param ip_interface:
201
- :param servertype_id:
202
- :param object_id:
203
- :return:
204
- """
205
-
206
- overlaps = (
207
- Server .objects .filter (
208
- servertype = servertype_id , intern_ip__net_overlaps = ip_interface
209
- )
210
- .exclude (server_id = object_id )
211
- .exists ()
212
- or
213
- # TODO: We should filter for attribute id here as well to have
214
- # consistent bebaviour with ip_addr_type: host and is_unique.
215
- ServerInetAttribute .objects .filter (
216
- server__servertype = servertype_id , value__net_overlaps = ip_interface
217
- )
218
- .exclude (server_id = object_id )
219
- .exists ()
220
- )
221
- if overlaps :
222
- raise ValidationError (
223
- "{0} overlaps with network of another object" .format (str (ip_interface ))
224
- )
225
-
226
-
227
207
class Servertype (models .Model ):
228
208
servertype_id = models .CharField (
229
209
max_length = 32 ,
@@ -515,14 +495,12 @@ def clean(self):
515
495
516
496
if ip_addr_type == "host" :
517
497
is_ip_address (self .intern_ip )
518
- is_unique_ip (self .intern_ip , self , ["network" , "loadbalancer" ])
519
- elif ip_addr_type == "loadbalancer" :
520
- is_ip_address (self .intern_ip )
498
+ is_unique_ip (self .intern_ip , self , ["host" ], False )
521
499
elif ip_addr_type == "network" :
522
500
is_network (self .intern_ip )
523
- network_overlaps (
524
- self . intern_ip , self . servertype . servertype_id , self . server_id
525
- )
501
+ is_unique_ip ( self . intern_ip , self , [ "network" ], True )
502
+ elif ip_addr_type == "loadbalancer" :
503
+ is_ip_address ( self . intern_ip )
526
504
527
505
def get_attributes (self , attribute ):
528
506
model = ServerAttribute .get_model (attribute .type )
@@ -748,16 +726,12 @@ def clean(self):
748
726
)
749
727
elif ip_addr_type == "host" :
750
728
is_ip_address (self .value )
751
- is_unique_ip (
752
- self .value , self .server , ["network" , "loadbalancer" ], self .attribute_id
753
- )
754
- elif ip_addr_type == "loadbalancer" :
755
- is_ip_address (self .value )
729
+ is_unique_ip (self .value , self .server , ["host" ], False , self .attribute_id )
756
730
elif ip_addr_type == "network" :
757
731
is_network (self .value )
758
- network_overlaps (
759
- self . value , self . server . servertype_id , self . server . server_id
760
- )
732
+ is_unique_ip ( self . value , self . server , [ "network" ], True , self . attribute_id )
733
+ elif ip_addr_type == "loadbalancer" :
734
+ is_ip_address ( self . value )
761
735
762
736
763
737
class ServerMACAddressAttribute (ServerAttribute ):
0 commit comments