Skip to content

Commit 2e5180c

Browse files
committed
[fix] Ensure VpnClient.post_delete fires on VPN template removal openwisp#1221
Replaced QuerySet.delete() with per-instance deletion in all three locations where VpnClient objects are bulk-deleted so that post_delete signals fire for every instance, ensuring: - VPN peer cache is invalidated on the affected VPN server - Client certificates are revoked (OpenVPN, auto_cert=True) - IP addresses are released (Wireguard) Also fixed the serializers.py cleanup logic: vpn_list was built from the old templates so .exclude(vpn__in=vpn_list) never matched the client being removed. Now uses new_vpn_ids from config_templates (the incoming new set) so PATCH with an empty templates list also works. Added select_related("vpn", "cert", "ip") to all deletion querysets to avoid N+1 queries triggered by the post_delete signal handler. Wrapped deletion loops in transaction.atomic() to prevent partial deletions on failure. Fixes openwisp#1221
1 parent d4d013f commit 2e5180c

File tree

2 files changed

+23
-7
lines changed

2 files changed

+23
-7
lines changed

openwisp_controller/config/api/serializers.py

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -200,9 +200,15 @@ def _update_config(self, device, config_data):
200200
old_templates = list(config.templates.values_list("id", flat=True))
201201
if config_templates != old_templates:
202202
with transaction.atomic():
203-
vpn_list = config.templates.filter(type="vpn").values_list("vpn")
204-
if vpn_list:
205-
config.vpnclient_set.exclude(vpn__in=vpn_list).delete()
203+
new_vpn_ids = Template.objects.filter(
204+
pk__in=config_templates, type="vpn"
205+
).values_list("vpn", flat=True)
206+
for vpnclient in (
207+
config.vpnclient_set.select_related("vpn", "cert", "ip")
208+
.exclude(vpn__in=new_vpn_ids)
209+
.iterator()
210+
):
211+
vpnclient.delete()
206212
config.templates.set(config_templates, clear=True)
207213
config.save()
208214
except ValidationError as error:

openwisp_controller/config/base/config.py

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -338,7 +338,11 @@ def manage_vpn_clients(cls, action, instance, pk_set, **kwargs):
338338
if instance.is_deactivating_or_deactivated():
339339
# If the device is deactivated or in the process of deactivating, then
340340
# delete all vpn clients and return.
341-
instance.vpnclient_set.all().delete()
341+
with transaction.atomic():
342+
for vpnclient in instance.vpnclient_set.select_related(
343+
"vpn", "cert", "ip"
344+
).iterator():
345+
vpnclient.delete()
342346
return
343347

344348
vpn_client_model = cls.vpn.through
@@ -370,9 +374,15 @@ def manage_vpn_clients(cls, action, instance, pk_set, **kwargs):
370374
# signal is triggered again—after all templates, including the required
371375
# ones, have been fully added. At that point, we can identify and
372376
# delete VpnClient objects not linked to the final template set.
373-
instance.vpnclient_set.exclude(
374-
template_id__in=instance.templates.values_list("id", flat=True)
375-
).delete()
377+
with transaction.atomic():
378+
for vpnclient in (
379+
instance.vpnclient_set.select_related("vpn", "cert", "ip")
380+
.exclude(
381+
template_id__in=instance.templates.values_list("id", flat=True)
382+
)
383+
.iterator()
384+
):
385+
vpnclient.delete()
376386

377387
if action == "post_add":
378388
for template in templates.filter(type="vpn"):

0 commit comments

Comments
 (0)