Skip to content

Commit d84383c

Browse files
authored
[fix] Fixed reverting template doesn't send config_changed signal #836
Fixes #836
1 parent aba8beb commit d84383c

File tree

3 files changed

+65
-22
lines changed

3 files changed

+65
-22
lines changed

openwisp_controller/config/apps.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ def ready(self, *args, **kwargs):
5555

5656
def __setmodels__(self):
5757
self.device_model = load_model('config', 'Device')
58+
self.template_model = load_model('config', 'Template')
5859
self.devicegroup_model = load_model('config', 'DeviceGroup')
5960
self.config_model = load_model('config', 'Config')
6061
self.vpn_model = load_model('config', 'Vpn')
@@ -135,11 +136,21 @@ def connect_signals(self):
135136
sender=self.config_model,
136137
dispatch_uid='devicegroup_templates_change_handler.backend_changed',
137138
)
139+
pre_save.connect(
140+
self.template_model.pre_save_handler,
141+
sender=self.template_model,
142+
dispatch_uid='template_pre_save_handler',
143+
)
138144
pre_save.connect(
139145
handlers.organization_disabled_handler,
140146
sender=self.org_model,
141147
dispatch_uid='organization_disabled_pre_save_clear_device_checksum_cache',
142148
)
149+
post_save.connect(
150+
self.template_model.post_save_handler,
151+
sender=self.template_model,
152+
dispatch_uid='template_post_save_handler',
153+
)
143154
post_save.connect(
144155
self.org_limits.post_save_handler,
145156
sender=self.org_model,

openwisp_controller/config/base/template.py

Lines changed: 22 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -106,30 +106,31 @@ class Meta:
106106
verbose_name_plural = _('templates')
107107
unique_together = (('organization', 'name'),)
108108

109-
def save(self, *args, **kwargs):
109+
@classmethod
110+
def pre_save_handler(cls, instance, *args, **kwargs):
110111
"""
111-
modifies status of related configs
112-
if key attributes have changed (queries the database)
112+
Modifies status of related configs
113113
"""
114-
update_related_config_status = False
115-
if not self._state.adding:
116-
if hasattr(self, 'backend_instance'):
117-
del self.backend_instance
118-
current = self.__class__.objects.get(pk=self.pk)
119-
try:
120-
current_checksum = current.checksum
121-
except NetjsonconfigValidationError:
122-
# If the Netjsonconfig library upgrade changes the schema,
123-
# the old configuration may become invalid, raising an exception.
124-
# Setting the checksum to None forces related configurations to update.
125-
current_checksum = None
126-
update_related_config_status = self.checksum != current_checksum
127-
# save current changes
128-
super().save(*args, **kwargs)
129-
# update relations
130-
if update_related_config_status:
114+
try:
115+
current = cls.objects.get(id=instance.id)
116+
except cls.DoesNotExist:
117+
return
118+
if hasattr(instance, 'backend_instance'):
119+
del instance.backend_instance
120+
try:
121+
current_checksum = current.checksum
122+
except NetjsonconfigValidationError:
123+
# If the Netjsonconfig library upgrade changes the schema,
124+
# the old configuration may become invalid, raising an exception.
125+
# Setting the checksum to None forces related configurations to update.
126+
current_checksum = None
127+
instance._update_related_config_status = instance.checksum != current_checksum
128+
129+
@classmethod
130+
def post_save_handler(cls, instance, created, *args, **kwargs):
131+
if not created and getattr(instance, '_update_related_config_status', False):
131132
transaction.on_commit(
132-
lambda: update_template_related_config_status.delay(self.pk)
133+
lambda: update_template_related_config_status.delay(instance.pk)
133134
)
134135

135136
def _update_related_config_status(self):

openwisp_controller/config/tests/test_admin.py

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,19 @@
88
from django.contrib.auth import get_user_model
99
from django.core.exceptions import ValidationError
1010
from django.core.files.base import ContentFile
11+
from django.core.management import call_command
1112
from django.db import IntegrityError
1213
from django.db.models.signals import post_save
1314
from django.test import TestCase, TransactionTestCase
1415
from django.urls import reverse
16+
from reversion.models import Version
1517
from swapper import load_model
1618

17-
from openwisp_utils.tests import AdminActionPermTestMixin, catch_signal
19+
from openwisp_utils.tests import (
20+
AdminActionPermTestMixin,
21+
capture_any_output,
22+
catch_signal,
23+
)
1824

1925
from ...geo.tests.utils import TestGeoMixin
2026
from ...tests.utils import TestAdminMixin
@@ -2446,6 +2452,31 @@ def test_device_changelist_deactivate_admin_action(self):
24462452
is_initially_deactivated=False,
24472453
)
24482454

2455+
@capture_any_output()
2456+
def test_restoring_template_sends_config_modified(self):
2457+
template = self._create_template(default=True)
2458+
call_command('createinitialrevisions')
2459+
# Make changes to the template and create revision
2460+
template.config['interfaces'][0]['name'] = 'eth1'
2461+
template.full_clean()
2462+
template.save()
2463+
call_command('createinitialrevisions')
2464+
2465+
config = self._create_config(organization=self._get_org())
2466+
config.set_status_applied()
2467+
config_checksum = config.checksum
2468+
2469+
# Revert the oldest version for the template
2470+
version = Version.objects.get_for_model(Template).last()
2471+
version.revert()
2472+
template.refresh_from_db()
2473+
config = Config.objects.get(id=config.id)
2474+
# Verify the template is restored to the previous version
2475+
self.assertEqual(template.config['interfaces'][0]['name'], 'eth0')
2476+
# Verify config status is changed to modified.
2477+
self.assertEqual(config.status, 'modified')
2478+
self.assertNotEqual(config.checksum, config_checksum)
2479+
24492480

24502481
class TestDeviceGroupAdmin(
24512482
CreateDeviceGroupMixin,

0 commit comments

Comments
 (0)