diff --git a/README.rst b/README.rst index cddb102a0..c899f0d8c 100644 --- a/README.rst +++ b/README.rst @@ -53,8 +53,8 @@ Other popular building blocks that are part of the OpenWISP ecosystem are: provides device status monitoring, collection of metrics, charts, alerts, possibility to define custom checks - `openwisp-firmware-upgrader - `_: automated firmware - upgrades (single devices or mass network upgrades) + `_: automated + firmware upgrades (single devices or mass network upgrades) - `openwisp-radius `_: based on FreeRADIUS, allows to implement network access authentication systems like 802.1x WPA2 Enterprise, captive portal authentication, @@ -65,10 +65,11 @@ Other popular building blocks that are part of the OpenWISP ecosystem are: daemons or other network software (e.g.: OpenVPN); it can be used in conjunction with openwisp-monitoring to get a better idea of the state of the network -- `openwisp-ipam `_: allows to manage - the assignment of IP addresses used in the network -- `openwisp-notifications `_: - allows users to be aware of important events happening in the network. +- `openwisp-ipam `_: allows to + manage the assignment of IP addresses used in the network +- `openwisp-notifications + `_: allows users to be + aware of important events happening in the network. **For a more complete overview of the OpenWISP modules and architecture**, see the `OpenWISP Architecture Overview diff --git a/openwisp_controller/__init__.py b/openwisp_controller/__init__.py index 6e2bfeb2a..d35f51f21 100644 --- a/openwisp_controller/__init__.py +++ b/openwisp_controller/__init__.py @@ -1,4 +1,4 @@ -VERSION = (1, 1, 0, 'final') +VERSION = (1, 2, 0, 'alpha') __version__ = VERSION # alias diff --git a/openwisp_controller/config/base/template.py b/openwisp_controller/config/base/template.py index 1ac764882..25c6a7d00 100644 --- a/openwisp_controller/config/base/template.py +++ b/openwisp_controller/config/base/template.py @@ -6,6 +6,7 @@ from django.db import models, transaction from django.utils.translation import gettext_lazy as _ from jsonfield import JSONField +from netjsonconfig.exceptions import ValidationError as NetjsonconfigValidationError from swapper import get_model_name from taggit.managers import TaggableManager @@ -115,7 +116,14 @@ def save(self, *args, **kwargs): if hasattr(self, 'backend_instance'): del self.backend_instance current = self.__class__.objects.get(pk=self.pk) - update_related_config_status = self.checksum != current.checksum + try: + current_checksum = current.checksum + except NetjsonconfigValidationError: + # If the Netjsonconfig library upgrade changes the schema, + # the old configuration may become invalid, raising an exception. + # Setting the checksum to None forces related configurations to update. + current_checksum = None + update_related_config_status = self.checksum != current_checksum # save current changes super().save(*args, **kwargs) # update relations diff --git a/openwisp_controller/config/tests/test_template.py b/openwisp_controller/config/tests/test_template.py index 914499e7f..53540c6ea 100644 --- a/openwisp_controller/config/tests/test_template.py +++ b/openwisp_controller/config/tests/test_template.py @@ -7,6 +7,7 @@ from django.db import transaction from django.test import TestCase, TransactionTestCase from netjsonconfig import OpenWrt +from netjsonconfig.exceptions import ValidationError as NetjsonconfigValidationError from swapper import load_model from openwisp_utils.tests import catch_signal @@ -500,6 +501,20 @@ def test_required_vpn_template_corner_case(self): # {'__all__': ['VPN client with this Config and Vpn already exists.']} self.assertIsNotNone(vpn_client) + def test_regression_preventing_from_fixing_invalid_conf(self): + template = self._create_template() + # create a template with an invalid configuration + Template.objects.update(config={'interfaces': [{'name': 'eth0', 'type': ''}]}) + # ensure the configuration raises ValidationError + with self.assertRaises(NetjsonconfigValidationError): + template.refresh_from_db() + del template.backend_instance + template.checksum + del template.backend_instance + template.config = {'interfaces': [{'name': 'eth0', 'type': 'ethernet'}]} + template.full_clean() + template.save() + class TestTemplateTransaction( TransactionTestMixin,