Skip to content

Commit 25f6a23

Browse files
authored
[fix] Fixed deletion of VpnClient deleted all provisioned subnets
Bug: When a device has more than 1 Vpn templates applied, then removing any one of the template deletes provisioned subnets for both VpnClients. Fix: Only delete the related provisioned subnets when VpnClient is deleted.
1 parent b6fda24 commit 25f6a23

2 files changed

Lines changed: 69 additions & 0 deletions

File tree

openwisp_controller/subnet_division/rule_types/vpn.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66

77
Vpn = load_model('config', 'Vpn')
88
VpnClient = load_model('config', 'VpnClient')
9+
Subnet = load_model('openwisp_ipam', 'Subnet')
910

1011

1112
class VpnSubnetDivisionRuleType(BaseSubnetDivisionRuleType):
@@ -51,3 +52,14 @@ def post_provision_handler(instance, provisioned, **kwargs):
5152
instance.ip = provisioned['ip_addresses'][0]
5253
instance.full_clean()
5354
instance.save()
55+
56+
@classmethod
57+
def destroy_provisioned_subnets_ips(cls, instance, **kwargs):
58+
# Deleting related subnets automatically deletes related IpAddress
59+
# and SubnetDivisionIndex objects
60+
config = cls.get_config(instance)
61+
rule_type = f'{cls.__module__}.{cls.__name__}'
62+
subnet_ids = config.subnetdivisionindex_set.filter(
63+
rule__master_subnet_id=instance.vpn.subnet_id, rule__type=rule_type
64+
).values_list('subnet_id')
65+
Subnet.objects.filter(id__in=subnet_ids).delete()

openwisp_controller/subnet_division/tests/test_models.py

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -358,6 +358,63 @@ def test_vpnclient_deleted(self):
358358
)
359359
self.assertEqual(self.ip_query.count(), 0)
360360

361+
def test_multiple_vpnclient_delete(self):
362+
"""
363+
When a VpnClient object is deleted, it should only delete
364+
the related provisioned subnets.
365+
"""
366+
rule = self._get_vpn_subdivision_rule()
367+
self.config.templates.add(self.template)
368+
subnet_query = self.subnet_query.filter(organization_id=self.org.id).exclude(
369+
id=self.master_subnet.id
370+
)
371+
self.config.templates.add(self.template)
372+
self.assertEqual(
373+
subnet_query.count(),
374+
rule.number_of_subnets,
375+
)
376+
self.assertEqual(
377+
self.ip_query.count(), (rule.number_of_subnets * rule.number_of_ips)
378+
)
379+
380+
# Create another VPN and VPN template and add this
381+
# template to the Config object
382+
vpn2_subnet = self._create_subnet(subnet='172.18.0.0/24')
383+
subnet_query = subnet_query.exclude(id=vpn2_subnet.id)
384+
vpn_server2 = self._create_wireguard_vpn(
385+
name='wg1',
386+
subnet=vpn2_subnet,
387+
organization=self.org,
388+
)
389+
ip_query = self.ip_query.exclude(id=vpn_server2.ip_id)
390+
vpn2_template = self._create_template(
391+
name='vpn2-test', type='vpn', vpn=vpn_server2, organization=self.org
392+
)
393+
self.config.templates.add(vpn2_template)
394+
395+
# Number of subnets should remain same
396+
self.assertEqual(
397+
subnet_query.count(),
398+
rule.number_of_subnets,
399+
)
400+
# Number of IPs should increase by one:
401+
# 1 IP for the VpnClient (auto_ip)
402+
self.assertEqual(
403+
ip_query.count(), (rule.number_of_subnets * rule.number_of_ips) + 1
404+
)
405+
406+
# Remove the template for the second VPN.
407+
# Number of subnets would return to the original numbers
408+
# Number of IPs would stay increased by 1
409+
self.config.templates.remove(vpn2_template)
410+
self.assertEqual(
411+
subnet_query.count(),
412+
rule.number_of_subnets,
413+
)
414+
self.assertEqual(
415+
ip_query.count(), (rule.number_of_subnets * rule.number_of_ips)
416+
)
417+
361418
@patch('logging.Logger.error')
362419
def test_subnets_exhausted(self, mocked_logger):
363420
subnet = self._get_master_subnet(

0 commit comments

Comments
 (0)