Skip to content

Commit 9b0503c

Browse files
authored
Fix querying supernet attributes (#394)
* Don't hardcode table names * Fix querying supernet attributes * ip_config_new is AF-unaware * Add tests for querying supernet attributes * Add tests for Inet attribute containment * Change ContainedOnlyBy() to use Inet attributes
1 parent cfd8ff9 commit 9b0503c

File tree

4 files changed

+420
-33
lines changed

4 files changed

+420
-33
lines changed

serveradmin/serverdb/fixtures/ip_addr_type.json

Lines changed: 143 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -125,7 +125,7 @@
125125
},
126126
{
127127
"model": "serverdb.attribute",
128-
"pk": "ip_config_ipv4_new",
128+
"pk": "ip_config_new",
129129
"fields": {
130130
"type": "inet",
131131
"multi": false,
@@ -139,6 +139,54 @@
139139
"regexp": "\\A.*\\Z"
140140
}
141141
},
142+
{
143+
"model": "serverdb.attribute",
144+
"pk": "network_type",
145+
"fields": {
146+
"type": "string",
147+
"multi": false,
148+
"hovertext": "",
149+
"group": "other",
150+
"help_link": null,
151+
"readonly": false,
152+
"target_servertype": null,
153+
"reversed_attribute": null,
154+
"clone": false,
155+
"regexp": "\\A.*\\Z"
156+
}
157+
},
158+
{
159+
"model": "serverdb.attribute",
160+
"pk": "network_type_ipv4",
161+
"fields": {
162+
"type": "string",
163+
"multi": false,
164+
"hovertext": "",
165+
"group": "other",
166+
"help_link": null,
167+
"readonly": false,
168+
"target_servertype": null,
169+
"reversed_attribute": null,
170+
"clone": false,
171+
"regexp": "\\A.*\\Z"
172+
}
173+
},
174+
{
175+
"model": "serverdb.attribute",
176+
"pk": "network_type_ipv6",
177+
"fields": {
178+
"type": "string",
179+
"multi": false,
180+
"hovertext": "",
181+
"group": "other",
182+
"help_link": null,
183+
"readonly": false,
184+
"target_servertype": null,
185+
"reversed_attribute": null,
186+
"clone": false,
187+
"regexp": "\\A.*\\Z"
188+
}
189+
},
142190
{
143191
"model": "serverdb.servertypeattribute",
144192
"pk": 7,
@@ -274,7 +322,7 @@
274322
"pk": 18,
275323
"fields": {
276324
"servertype": "host",
277-
"attribute": "ip_config_ipv4_new",
325+
"attribute": "ip_config_new",
278326
"related_via_attribute": null,
279327
"consistent_via_attribute": null,
280328
"required": false,
@@ -287,7 +335,7 @@
287335
"pk": 19,
288336
"fields": {
289337
"servertype": "loadbalancer",
290-
"attribute": "ip_config_ipv4_new",
338+
"attribute": "ip_config_new",
291339
"related_via_attribute": null,
292340
"consistent_via_attribute": null,
293341
"required": false,
@@ -300,12 +348,103 @@
300348
"pk": 20,
301349
"fields": {
302350
"servertype": "route_network",
303-
"attribute": "ip_config_ipv4_new",
351+
"attribute": "ip_config_new",
352+
"related_via_attribute": null,
353+
"consistent_via_attribute": null,
354+
"required": false,
355+
"default_value": null,
356+
"default_visible": false
357+
}
358+
},
359+
{
360+
"model": "serverdb.servertypeattribute",
361+
"pk": 21,
362+
"fields": {
363+
"servertype": "provider_network",
364+
"attribute": "ip_config_new",
365+
"related_via_attribute": null,
366+
"consistent_via_attribute": null,
367+
"required": false,
368+
"default_value": null,
369+
"default_visible": false
370+
}
371+
},
372+
{
373+
"model": "serverdb.servertypeattribute",
374+
"pk": 22,
375+
"fields": {
376+
"servertype": "route_network",
377+
"attribute": "network_type",
304378
"related_via_attribute": null,
305379
"consistent_via_attribute": null,
306380
"required": false,
307381
"default_value": null,
308382
"default_visible": false
309383
}
384+
},
385+
{
386+
"model": "serverdb.servertypeattribute",
387+
"pk": 23,
388+
"fields": {
389+
"servertype": "provider_network",
390+
"attribute": "network_type_ipv4",
391+
"related_via_attribute": null,
392+
"consistent_via_attribute": null,
393+
"required": false,
394+
"default_value": null,
395+
"default_visible": false
396+
}
397+
},
398+
{
399+
"model": "serverdb.servertypeattribute",
400+
"pk": 24,
401+
"fields": {
402+
"servertype": "provider_network",
403+
"attribute": "network_type_ipv6",
404+
"related_via_attribute": null,
405+
"consistent_via_attribute": null,
406+
"required": false,
407+
"default_value": null,
408+
"default_visible": false
409+
}
410+
},
411+
{
412+
"model": "serverdb.servertypeattribute",
413+
"pk": 25,
414+
"fields": {
415+
"servertype": "host",
416+
"attribute": "network_type",
417+
"related_via_attribute": "supernet_no_af",
418+
"consistent_via_attribute": null,
419+
"required": false,
420+
"default_value": null,
421+
"default_visible": false
422+
}
423+
},
424+
{
425+
"model": "serverdb.servertypeattribute",
426+
"pk": 26,
427+
"fields": {
428+
"servertype": "host",
429+
"attribute": "network_type_ipv4",
430+
"related_via_attribute": "supernet_ipv4",
431+
"consistent_via_attribute": null,
432+
"required": false,
433+
"default_value": null,
434+
"default_visible": false
435+
}
436+
},
437+
{
438+
"model": "serverdb.servertypeattribute",
439+
"pk": 27,
440+
"fields": {
441+
"servertype": "host",
442+
"attribute": "network_type_ipv6",
443+
"related_via_attribute": "supernet_ipv6",
444+
"consistent_via_attribute": null,
445+
"required": false,
446+
"default_value": null,
447+
"default_visible": false
448+
}
310449
}
311450
]

serveradmin/serverdb/query_materializer.py

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
ServertypeAttribute,
1919
Server,
2020
ServerAttribute,
21-
ServerRelationAttribute,
21+
ServerRelationAttribute, ServerInetAttribute,
2222
)
2323

2424
logger = logging.getLogger(__package__)
@@ -204,7 +204,7 @@ def _add_supernet_attribute(self, attribute: Attribute, servers_in):
204204
the host's IP address or prefix fits within the net's IP address or prefix.
205205
"""
206206

207-
q = """
207+
q = f"""
208208
SELECT
209209
net.server_id,
210210
net.hostname,
@@ -215,11 +215,11 @@ def _add_supernet_attribute(self, attribute: Attribute, servers_in):
215215
host.server_id AS host_server_id,
216216
host_attr.attribute_id AS host_attr_name
217217
FROM server AS host
218-
JOIN server_inet_attribute AS host_addr ON (host.server_id = host_addr.server_id)
219-
JOIN server_inet_attribute AS net_addr ON (host_addr.value <<= net_addr.value AND host_addr.attribute_id = net_addr.attribute_id)
220-
JOIN server AS net ON (net_addr.server_id = net.server_id)
221-
JOIN attribute AS net_attr ON (net_attr.attribute_id = net_addr.attribute_id)
222-
JOIN attribute AS host_attr ON (host_attr.attribute_id = host_addr.attribute_id)
218+
JOIN {ServerInetAttribute._meta.db_table} AS host_addr ON (host.server_id = host_addr.server_id)
219+
JOIN {ServerInetAttribute._meta.db_table} AS net_addr ON (host_addr.value <<= net_addr.value AND host_addr.attribute_id = net_addr.attribute_id)
220+
JOIN {Server._meta.db_table} AS net ON (net_addr.server_id = net.server_id)
221+
JOIN {Attribute._meta.db_table} AS net_attr ON (net_attr.attribute_id = net_addr.attribute_id)
222+
JOIN {Attribute._meta.db_table} AS host_attr ON (host_attr.attribute_id = host_addr.attribute_id)
223223
WHERE
224224
net.servertype_id = %(target_servertype)s
225225
AND host.server_id = any(%(hosts)s)

serveradmin/serverdb/sql_generator.py

Lines changed: 53 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@
2828
from serveradmin.serverdb.models import (
2929
Server,
3030
ServerAttribute,
31-
ServerRelationAttribute,
31+
ServerRelationAttribute, ServerInetAttribute, Attribute,
3232
)
3333

3434

@@ -179,13 +179,10 @@ def _containment_filter_template(attribute, filt):
179179
elif isinstance(filt, Contains):
180180
template = "{{0}} >>= {0}"
181181
elif isinstance(filt, ContainedOnlyBy):
182-
template = (
183-
"{{0}} << {0} AND NOT EXISTS ("
184-
' SELECT 1 '
185-
' FROM server AS supernet '
186-
' WHERE {{0}} << supernet.intern_ip AND '
187-
' supernet.intern_ip << {0}'
188-
')'
182+
template = "{{0}} << {0} AND NOT " + _supernet_exists_sql(
183+
attribute, 'supernet',
184+
'<< {0}',
185+
('{{0}} << supernet.intern_ip',),
189186
)
190187
elif isinstance(filt, ContainedBy):
191188
template = "{{0}} <<= {0}"
@@ -215,11 +212,14 @@ def _condition_sql(attribute, template, related_vias):
215212
return template.format('server.' + attribute.special.field)
216213

217214
if attribute.type == 'supernet':
218-
return _exists_sql(Server, 'sub', (
219-
"sub.servertype_id = '{0}'".format(attribute.target_servertype_id),
220-
'sub.intern_ip >>= server.intern_ip',
221-
template.format('sub.server_id'),
222-
))
215+
return _supernet_exists_sql(
216+
attribute, 'supernet',
217+
'>>= server_addr.value',
218+
(
219+
f"supernet.servertype_id = '{attribute.target_servertype_id}'",
220+
template.format('supernet.server_id'),
221+
)
222+
)
223223
if attribute.type == 'domain':
224224
return _exists_sql(Server, 'sub', (
225225
"sub.servertype_id = '{0}'".format(attribute.target_servertype_id),
@@ -260,13 +260,14 @@ def _real_condition_sql(attribute, template, related_vias):
260260
# The condition for directly attached attributes
261261
relation_condition = 'server.server_id = sub.server_id'
262262
elif related_via_attribute.type == 'supernet':
263-
relation_condition = _exists_sql(Server, 'rel1', (
264-
"rel1.servertype_id = '{0}'".format(
265-
related_via_attribute.target_servertype_id
263+
relation_condition = _supernet_exists_sql(
264+
related_via_attribute, 'supernet',
265+
'>>= server_addr.value',
266+
(
267+
f"supernet.servertype_id = '{related_via_attribute.target_servertype_id}'",
268+
'supernet.server_id = sub.server_id'
266269
),
267-
'rel1.intern_ip >>= server.intern_ip',
268-
'rel1.server_id = sub.server_id',
269-
))
270+
)
270271
elif related_via_attribute.type == 'reverse':
271272
relation_condition = _exists_sql(ServerRelationAttribute, 'rel1', (
272273
"rel1.attribute_id = '{0}'".format(
@@ -309,6 +310,39 @@ def _exists_sql(model, alias, conditions):
309310
)
310311

311312

313+
def _supernet_exists_sql(attribute: Attribute, supernet_alias: str, addr_match: str, where: tuple[str, ...]):
314+
if attribute.inet_address_family:
315+
af_join = (
316+
(Attribute._meta.db_table, 'server_attr', ('server_attr.attribute_id = server_addr.attribute_id',)),
317+
(Attribute._meta.db_table, 'net_attr', ('net_attr.attribute_id = net_addr.attribute_id',)),
318+
)
319+
af_where = (
320+
f"net_attr.inet_address_family = '{attribute.inet_address_family}'",
321+
f"server_attr.inet_address_family = '{attribute.inet_address_family}'",
322+
)
323+
else:
324+
af_join = ()
325+
af_where = ()
326+
327+
joins = (
328+
(ServerInetAttribute._meta.db_table, 'server_addr', ('server_addr.server_id = server.server_id',)),
329+
(ServerInetAttribute._meta.db_table, 'net_addr', (
330+
'net_addr.value ' + addr_match,
331+
'net_addr.attribute_id = server_addr.attribute_id',
332+
f'net_addr.server_id = {supernet_alias}.server_id',
333+
)),
334+
) + af_join
335+
336+
wheres = where + af_where
337+
338+
return 'EXISTS (SELECT 1 FROM {0} AS {1} {2} WHERE {3})'.format(
339+
Server._meta.db_table,
340+
supernet_alias,
341+
' '.join(f'JOIN {x[0]} AS {x[1]} ON ({" AND ".join(x[2])})' for x in joins),
342+
' AND '.join(c for c in wheres)
343+
)
344+
345+
312346
def _raw_sql_escape(value):
313347
try:
314348
value = str(value)

0 commit comments

Comments
 (0)