Skip to content

Commit 301de43

Browse files
authored
Merge branch 'master' into chore/add-publiccode-parser-action
2 parents 05ab319 + 18052d6 commit 301de43

File tree

10 files changed

+323
-45
lines changed

10 files changed

+323
-45
lines changed

.github/FUNDING.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
# These are supported funding model platforms
2-
github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
2+
github: [openwisp]
33
patreon: # Replace with a single Patreon username
44
open_collective: # Replace with a single Open Collective username
55
ko_fi: # Replace with a single Ko-fi username

docs/user/templates.rst

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ Default Templates
7979
:alt: Templates enabled by default
8080

8181
When templates are flagged as **"Enabled by default"**, they will be
82-
automatically assigned to new devices.
82+
automatically assigned to all devices, **this includes existing devices**.
8383

8484
This is a very powerful feature: **once default templates are correctly
8585
configured to implement the use case you need, you will only have to
@@ -95,11 +95,10 @@ default templates without the need of manual intervention from the network
9595
operators.
9696

9797
An organization specific template flagged as default will be automatically
98-
assigned to any new device which will be created in the same organization.
98+
assigned to all current and future devices in the same organization.
9999

100100
A shared default template instead will be automatically assigned to all
101-
the new devices which will be created in the system, regardless of
102-
organization.
101+
existing and new devices in the system, regardless of organization.
103102

104103
.. _required_templates:
105104

openwisp_controller/config/base/template.py

Lines changed: 67 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import json
2+
import logging
23
from collections import OrderedDict
34
from copy import copy
45

@@ -7,14 +8,19 @@
78
from django.utils.translation import gettext_lazy as _
89
from jsonfield import JSONField
910
from netjsonconfig.exceptions import ValidationError as NetjsonconfigValidationError
10-
from swapper import get_model_name
11+
from swapper import get_model_name, load_model
1112
from taggit.managers import TaggableManager
1213

1314
from ...base import ShareableOrgMixinUniqueName
1415
from ..settings import DEFAULT_AUTO_CERT
15-
from ..tasks import update_template_related_config_status
16+
from ..tasks import (
17+
auto_add_template_to_existing_configs,
18+
update_template_related_config_status,
19+
)
1620
from .base import BaseConfig
1721

22+
logger = logging.getLogger(__name__)
23+
1824
TYPE_CHOICES = (("generic", _("Generic")), ("vpn", _("VPN-client")))
1925

2026

@@ -61,7 +67,8 @@ class AbstractTemplate(ShareableOrgMixinUniqueName, BaseConfig):
6167
default=False,
6268
db_index=True,
6369
help_text=_(
64-
"whether new configurations will have this template enabled by default"
70+
"whether this template is applied to all current and future devices"
71+
" by default (can be unassigned manually)"
6572
),
6673
)
6774
required = models.BooleanField(
@@ -124,14 +131,31 @@ def pre_save_handler(cls, instance, *args, **kwargs):
124131
# the old configuration may become invalid, raising an exception.
125132
# Setting the checksum to None forces related configurations to update.
126133
current_checksum = None
127-
instance._update_related_config_status = instance.checksum != current_checksum
134+
instance._should_update_related_config_status = (
135+
instance.checksum != current_checksum
136+
)
137+
138+
# Check if template is becoming default or required
139+
if (instance.default and not current.default) or (
140+
instance.required and not current.required
141+
):
142+
instance._should_add_to_existing_configs = True
128143

129144
@classmethod
130145
def post_save_handler(cls, instance, created, *args, **kwargs):
131-
if not created and getattr(instance, "_update_related_config_status", False):
146+
if not created and getattr(
147+
instance, "_should_update_related_config_status", False
148+
):
132149
transaction.on_commit(
133150
lambda: update_template_related_config_status.delay(instance.pk)
134151
)
152+
# Auto-add template to existing configs if it's new or became default/required
153+
if getattr(instance, "_should_add_to_existing_configs", False) or (
154+
created and (instance.default or instance.required)
155+
):
156+
transaction.on_commit(
157+
lambda: auto_add_template_to_existing_configs.delay(str(instance.pk))
158+
)
135159

136160
def _update_related_config_status(self):
137161
# use atomic to ensure any code bound to
@@ -153,6 +177,44 @@ def _update_related_config_status(self):
153177
if config.pk in changing_status:
154178
config._send_config_status_changed_signal()
155179

180+
def _auto_add_to_existing_configs(self):
181+
"""
182+
When a template is ``default`` or ``required``, adds the template
183+
to each relevant ``Config`` object
184+
"""
185+
Config = load_model("config", "Config")
186+
187+
# Only proceed if template is default or required
188+
if not (self.default or self.required):
189+
return
190+
191+
# use atomic to ensure any code bound to
192+
# be executed via transaction.on_commit
193+
# is executed after the whole block
194+
with transaction.atomic():
195+
# Exclude deactivating or deactivated configs
196+
configs = (
197+
Config.objects.select_related("device")
198+
.filter(
199+
backend=self.backend,
200+
)
201+
.exclude(
202+
models.Q(status__in=["deactivating", "deactivated"])
203+
| models.Q(templates__id=self.pk)
204+
)
205+
)
206+
if self.organization_id:
207+
configs = configs.filter(device__organization_id=self.organization_id)
208+
for config in configs.iterator():
209+
try:
210+
config.templates.add(self)
211+
except Exception as e:
212+
# Log error but continue with other configs
213+
logger.exception(
214+
f"Failed to add template {self.pk} to "
215+
f"config {config.pk}: {e}"
216+
)
217+
156218
def clean(self, *args, **kwargs):
157219
"""
158220
* validates org relationship of VPN if present

openwisp_controller/config/base/vpn.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -904,7 +904,7 @@ def _get_common_name(self):
904904
@classmethod
905905
def post_save(cls, instance, **kwargs):
906906
def _post_save():
907-
instance.vpn._invalidate_peer_cache(update=True)
907+
instance.vpn._invalidate_peer_cache()
908908

909909
transaction.on_commit(_post_save)
910910
# ZT network member should be authorized and assigned

openwisp_controller/config/migrations/0001_squashed_0002_config_settings_uuid.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -294,8 +294,8 @@ class Migration(migrations.Migration):
294294
db_index=True,
295295
default=False,
296296
help_text=(
297-
"whether new configurations will have this "
298-
"template enabled by default"
297+
"whether this template is applied to all current and future"
298+
" devices by default (can be unassigned manually)"
299299
),
300300
verbose_name="enabled by default",
301301
),

openwisp_controller/config/tasks.py

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,26 @@ def update_template_related_config_status(template_pk):
3737
)
3838

3939

40+
@shared_task(soft_time_limit=7200)
41+
def auto_add_template_to_existing_configs(template_pk):
42+
Template = load_model("config", "Template")
43+
try:
44+
template = Template.objects.get(pk=template_pk)
45+
except ObjectDoesNotExist as e:
46+
logger.warning(
47+
f'auto_add_template_to_existing_configs("{template_pk}") failed: {e}'
48+
)
49+
return
50+
try:
51+
template._auto_add_to_existing_configs()
52+
except SoftTimeLimitExceeded:
53+
logger.error(
54+
"soft time limit hit while executing "
55+
f"_auto_add_to_existing_configs for {template} "
56+
f"(ID: {template_pk})"
57+
)
58+
59+
4060
@shared_task(soft_time_limit=1200)
4161
def create_vpn_dh(vpn_pk):
4262
"""
@@ -97,7 +117,9 @@ def invalidate_devicegroup_cache_delete(instance_id, model_name, **kwargs):
97117
)
98118

99119

100-
@shared_task(base=OpenwispCeleryTask)
120+
# Generating large configurations can be time-consuming; hence,
121+
# a custom soft time limit is applied here.
122+
@shared_task(soft_time_limit=300)
101123
def trigger_vpn_server_endpoint(endpoint, auth_token, vpn_id):
102124
Vpn = load_model("config", "Vpn")
103125
try:

0 commit comments

Comments
 (0)